ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Pandas进阶指南之数据合并与缺失值处理详解

Pandas进阶指南之数据合并与缺失值处理详解 前言合并merge和缺失值NaN看起来是两个话题实际在实战里几乎总是连在一起一次外连接或左连接就是缺失值的最大来源。左表有、右表没有的键 连接后右表那几列就整片变成NaN很多人把它当成数据本身脏 其实是连接方式的必然结果。这篇进阶篇讲三件事merge/join/concat的连接类型与基数约束、 连接之后如何系统地处理由此产生的缺失值、以及可空类型pd.NA带来的新行为。基础用法请另找入门文章这里只讲容易翻车的地方。前提照旧pandas 是第三方库本机没有 Python 解释器也没有 pandas 示例无法运行验证只能逐行人工推演。参数细节以 Pandas 官方文档为准。一、三种合并的分工方法对齐依据默认方向典型用途concat索引或列名joinouter纵向堆叠多段数据merge键列或索引howinner关系型 JOINjoin索引howleft按索引补列记住默认值差异很关键merge默认内连接、join默认左连接concat默认外连接并集。三者默认方向全都不同混着用必踩坑。# pandas 1.0import pandas as pdmerged pd.merge(orders, users, howleft, onuser_id,suffixes(_o, _u), indicatorTrue)merge的常用参数howinner/left/right/outer/crosson同名连接键键名不同用left_on/right_onsuffixes两边同名列非键的后缀默认(_x, _y)validate检查基数关系可选one_to_one、one_to_many、many_to_one、many_to_manyindicator加一列_merge标记每行来自哪边。二、基数是行数暴涨的元凶关系型连接的语义里多对多连接会产生笛卡尔扩张 如果键在两边都不唯一匹配出的行数可能远大于两表行数之和。# pandas 1.0checked pd.merge(a, b, onid, howinner,validateone_to_one)validateone_to_one会在基数不符时直接抛错把问题挡在合并之前 比合并后才发现行数不对要好得多。日常经验一方是维度表如用户表一方是事实表如订单表→ 用many_to_one两边都唯一 →one_to_one只有确认了基数才用默认的不校验。配合indicatorTrue还能快速看清连接的命中率# pandas 1.0probe pd.merge(left, right, onid, howouter, indicatorTrue)print(probe[_merge].value_counts())输出的both/left_only/right_only分别表示两边都有、 只有左边、只有右边一眼就能判断该用哪种how。三、连接为什么会制造缺失值把上面的结论说透外连接和左连接必然产生缺失值 它们是某一侧没有匹配行在表格里的表现形式。# pandas 1.0# 左表有 3 个 id右表只覆盖 2 个result orders.merge(users, howleft, onuser_id)print(result[city].isna().sum()) # 未被覆盖的行数处理这类缺失要先分清该不该缺业务上本来就该有值却缺了 → 可能是右表漏数据要回查业务上确实没有对应记录 → 这种缺失是真实语义不该无脑填 0。把两种情况混为一谈是报表口径出错的高发点。四、系统地处理缺失值常用的两层手段# pandas 1.0df.isna().sum() # 每列缺多少df.isna().mean() # 每列缺失比例df.dropna(subset[city]) # 只看 city 列有没有缺df.fillna({amount: 0, city: 未知}) # 按列分别填数值列的连续缺失可以用插值# pandas 1.0df[amount] df[amount].interpolate()df[amount] df[amount].ffill() # 前向填充版本差异ffill/bfill早就作为独立方法存在 但在 pandas 3.0 里fillna的method关键字已被移除 官方明确要求改用ffill()/bfill()。旧代码里的df.fillna(methodffill)在新版会直接报错迁移时务必替换。五、pd.NA与可空类型传统上整数列一旦有缺失就被抬成float64于是 3 变成 3.0。 pandas 提供的可空类型解决了这个外观问题# pandas 1.0s pd.Series([1, None, 3], dtypeInt64)print(s) # 缺失显示为 NA整列仍是整数语义要点Int64大写 I是可空整数int64小写是 NumPy 的不能有缺失可空类型里的缺失标记是pd.NA不是NaNpd.NA参与比较时得到的是pd.NA而不是True/False用判定会得到未知所以判定仍然一律用isna()boolean、string也是对应的可空类型命名都是小写全称。六、一个进阶示例# pandas 1.0import pandas as pddef enrich(orders: pd.DataFrame, users: pd.DataFrame) - pd.DataFrame:# 左连接保留全部订单用户信息可能缺out orders.merge(users[[user_id, city]],howleft,onuser_id,validatemany_to_one,indicatorTrue,)# 统计未匹配上的订单unmatched (out[_merge] left_only).sum()print(未匹配订单数:, unmatched)# 缺失的城市填成未知而不是 0out[city] out[city].fillna(未知)return out.drop(columns_merge)if __name__ __main__:o pd.DataFrame({order_id: [1, 2], user_id: [10, 99]})u pd.DataFrame({user_id: [10], city: [北京]})print(enrich(o, u))常见坑点把merge默认当成左连接❌ 没写how发现订单少了一批因为merge默认是inner✅ 需要保留主表就显式howleftjoin的默认才是left连接键不唯一导致行数暴涨❌ 一边键有重复合并后行数翻几倍还以为是数据翻倍了 ✅ 传validatemany_to_one等让 pandas 主动检查基数把连接产生的缺失当成数据脏❌ 看到city列大量NaN就去清洗右表其实只是左连接没匹配上 ✅ 先用indicatorTrue看left_only数量判断是连接问题还是数据问题无脑fillna(0)❌ 把没有对应城市填成 0语义完全变了 ✅ 按列区分数值填 0、类别填未知或保持缺失在 pandas 3.0 里用fillna(method...)❌df.fillna(methodffill)新版本已移除直接报错 ✅ 用df.ffill()/df.bfill()用判定pd.NA❌s pd.NA想筛出缺失得到的是pd.NA而非布尔结果不对 ✅ 一律用s.isna()横向concat以为按键对齐❌pd.concat([df1, df2], axis1)以为按列名合并实际按索引对齐 ✅ 要按键合并用mergeconcat(axis1)靠索引outer连接后不评估覆盖率❌ 直接出报表两侧独有的行混在一起没被察觉 ✅ 用indicatorTrue配合value_counts()评估两边覆盖情况总结主题关键点默认方向concat外、merge内、join左基数检查validate四选一防行数暴涨来源标记indicatorTrue加_merge列缺失来源左/外连接必然制造缺失填充fillna按列填3.0 用ffill()/bfill()可空类型Int64/boolean/string缺失是pd.NA判定一律isna()永远别用进阶的关键心法是把合并和缺失当成同一件事的两面。 先想清楚我要哪一侧的行都保留再用对应的how 连接完立刻用indicator和isna().sum()体检 就能在数据进入分析之前把口径问题拦下来。
返回列表