缺失值
余额缺失,可能是没有记录,不能据此判断账户里没有钱。是否填充,要看数据为什么缺失。Pandas 为 pd.NA、NaN 和 NaT 等缺失表示提供统一的处理 API。
检测
import pandas as pd
df = pd.DataFrame({
"required_key": [1, 2, 3], "country": ["CA", None, "FR"],
"account_id": ["A", "A", "B"], "time": [1, 2, 1],
"balance": pd.Series([10, pd.NA, pd.NA], dtype="Int64"),
}).sort_values(["account_id", "time"])
missing_by_column = df.isna().sum()
complete_rows = df.notna().all(axis="columns")
填充前,missing_by_column 显示两个余额缺失、一个国家缺失,其余列没有缺失;complete_rows 只有第一行为真。
始终使用 isna 和 notna 进行判断,直接比较是否等于某个缺失哨兵值(sentinel)是不可靠的。
除了查看缺失总数,还要关注缺失比例。如果整体缺失率掩盖了局部规律,需按业务维度(如用户群、时间段)拆分检查,以发现系统性模式。
选择处理策略
df = df.dropna(subset=["required_key"])
df["country"] = df["country"].fillna("unknown")
df["balance"] = df.groupby("account_id")["balance"].ffill()
处理后的余额如下:
A 的第二行沿用本账户上一条观测的余额 10。B 在自己的分组里没有更早的余额,所以仍然缺失;A 的 10 不能跨账户传给 B。所有必填键都有值,因此 dropna 没有删行;缺失的国家被填成 "unknown"。dropna、fillna 默认返回结果,上面的赋值才会更新 df。
执行 ffill 等方向性填充前,先按实体键和时间键排序,再按实体分组,避免把其他账户的值填进来。
数据类型与聚合
Int64、boolean 和 string 等可空扩展类型(Nullable Extension Dtypes)能在表示缺失的同时保留语义类型。在关键数据边界处应显式指定这些类型,而非依赖 Pandas 的自动推断。
大多数聚合操作(reductions)默认跳过缺失值。当默认行为会改变结果语义时,需显式设置 skipna 或 min_count(最少有效值数量)。
未知不等于零或假
缺失数据规则也影响逻辑判断:pd.NA == pd.NA 的结果仍未知,bool(pd.NA) 会抛出 TypeError。空字符串和无穷大默认不算缺失。全缺失序列求和默认得到零,除非要求最少有效值数量。可空比较后调用 .all() 可能跳过未知项,因此必填列还需单独检查是否缺失。
empty = pd.Series([pd.NA, pd.NA], dtype="Int64")
assert empty.sum() == 0
assert pd.isna(empty.sum(min_count=1))
assert empty.ge(0).all()
assert not empty.notna().all()
记录决策
数据清洗流程应明确记录:哪些列允许缺失、缺失原因,以及各处理策略对下游分析的影响。 如果插补行为本身可能携带预测或诊断信息,建议保留一个指示列(indicator column)标记原始数据是否缺失。