groupby 默认丢弃 NaN 分组是设计行为,从 Pandas 1.1 起对含 NaN、None 或 NaT 的分组键默认跳过,需显式设 dropna=False 才保留;启用后 NaN 作为独立分组键出现,索引中可直接用 .loc[np.nan] 访问。

groupby 默认丢弃 NaN 分组是设计行为,不是 bug
从 Pandas 1.1 开始,groupby 对分组键中含 NaN、None 或 NaT 的行默认跳过,不生成对应分组。这不是计算错误,而是历史沿袭的默认策略——Pandas 认为缺失值无法参与“有意义的分组比较”。所以你看到分组结果里压根没有 NaN 键,不是数据丢了,是它被主动过滤了。
常见现象包括:df.groupby('col').size() 结果 sum 小于 len(df);用 get_group(np.nan) 报 KeyError;聚合后发现某些预期类别消失。
必须显式设 dropna=False 才能保留空键分组
dropna 是 groupby 的关键参数,仅控制分组键本身的缺失值是否参与分组,不影响数据行的筛选逻辑。设为 False 后,NaN 会作为独立分组键出现(注意:多个 NaN 会被归为同一组,因为 NaN != NaN 但 Pandas 在分组时特殊处理为等价)。
-
df.groupby('col', dropna=False).size()—— 输出含NaN行的计数 -
df.groupby(['col1', 'col2'], dropna=False).agg({'x': 'mean'})—— 允许组合键中任一列为NaN仍建组 - 若用
pd.Grouper(key='col', dropna=False)(如在resample或多级 groupby 中),同样生效
dropna=False 的副作用和兼容性注意点
启用后虽保留空键,但带来几个实操细节:
立即学习“Python免费学习笔记(深入)”;
- 分组结果的索引中会出现
NaN,后续用.loc[...]取值需写成.loc[np.nan],不能用字符串'NaN'或None - 当分组键是
datetime64[ns]且含NaT,dropna=False同样生效,但NaT在索引中显示为NaT,不是NaN - Pandas dropna 参数,强行传入会报
TypeError: groupby() got an unexpected keyword argument 'dropna',需先检查版本:pd.__version__ - 性能上无显著差异,但内存中会多存一组键为
NaN的中间结构,大数据量下可忽略
空值分组后聚合结果里的 NaN 值不是 dropna 控制的
很多人混淆:以为 dropna=False 能让 mean()、sum() 等聚合函数返回非 NaN。其实不是。dropna 只管“分组键是否为空”,不管“组内数值是否为空”。例如一个全为 NaN 的组调用 .mean(),结果仍是 NaN —— 这由各聚合函数自身的 skipna 参数决定(如 mean(skipna=True) 是默认行为)。
所以如果你看到某组聚合结果是 NaN,先确认:是这组根本没被创建(dropna=True 导致),还是这组存在但内部全是空值(需查 count() 或 size())。
dropna=False,比如 df.groupby('a').apply(lambda g: g.groupby('b', dropna=False).size()) —— 外层没设不重要,但内层不设就又丢空键了。


















