concat不能直接拼接列名不同的DataFrame,因为pandas默认按列名对齐,缺失列补NaN,易造成“假空值”;需先统一语义列名、再concat、最后按业务逻辑fillna并检查dtype。

concat 时为什么不能直接拼接列名不同的 DataFrame
因为 pandas.concat 默认按列名对齐,缺失列会自动补 NaN,但如果你没意识到列对齐逻辑,可能误以为数据“丢了”——其实只是被填成了空值。业务上相似不等于列名/列序一致,比如一个表叫 user_id,另一个叫 id;一个有 reg_time,另一个只有 created_at。这时候硬拼,结果里会出现大量未对齐的 NaN,后续 fillna 就容易填错位置。
用 keys + ignore_index=True 确保索引干净,再对齐列
先明确目标:不是强行让两表列名一样,而是让业务字段归到同一语义列下。推荐分三步走:
- 手动重命名列,把业务含义相同的列统一成相同名字,例如
df1.rename(columns={'id': 'user_id', 'created_at': 'reg_time'}) - 用
pd.concat([df1, df2], ignore_index=True, sort=False)拼接,ignore_index=True防止旧索引干扰,sort=False避免 pandas 自动重排列(v1.4+ 默认为 True,会打乱你刚对齐的列序) - 拼完再统一处理缺失:比如
reg_time在 df1 里全有、df2 里全空,那fillna前得确认空值是真缺失,而不是因列名没对齐导致的“假空”
fillna 填什么?按业务逻辑选策略,别无脑用 0 或空字符串
fillna 不是补洞工具,是补业务意义。常见错误是:df['amount'].fillna(0) 把本该为空的退款记录也填成 0,后续 sum 直接失真。实际应:
- 数值型字段(如
score,age):优先考虑fillna(df['score'].median())或按分组填充,比如df.groupby('region')['score'].transform('mean') - 时间字段(如
reg_time):别填字符串'1970-01-01',更安全的是留NaT,或用pd.NaT显式标记 - 分类字段(如
status):如果缺失比例高(>30%),填'unknown'比填众数更诚实;若低,可用mode().iloc[0]
合并后检查列类型是否崩了——object 列混入数字是隐形炸弹
两个 DataFrame 的同一列,一个存的是 int64,另一个因含空值被转成 float64,再拼一起,pandas 会统一升为 float64;更糟的是,如果某列在 df1 是数字,在 df2 是字符串 'N/A',整列会变成 object,后续 .sum() 直接报错。
立即学习“Python免费学习笔记(深入)”;
合并后务必运行:
print(df.dtypes)
重点关注那些本该是数值却变成 object 的列,用 pd.to_numeric(df['col'], errors='coerce') 强制转换,errors='coerce' 会把非法值变 NaN,比崩掉强。
真正麻烦的永远不是 concat 或 fillna 这两个动作本身,而是列语义没对齐就急着填空——业务字段映射一旦出错,后面所有统计都跟着偏。


















