当前位置: 首页 > web >正文

数据组合与合并:Pandas 数据整合全指南 +缺失值处理

数据组合与合并:Pandas 数据整合全指南

在进行数据分析之前,数据清洗与整合是关键步骤。

遵循“整洁数据”(Tidy Data)原则:

  • 每个观测值占一行
  • 每个变量占一列
  • 每种观测单元构成一张独立的表格

整理好数据后,常常需要将多个数据集组合起来,才能回答复杂问题。


一、连接数据(Concatenation)

当多个数据集结构相似(列名一致或相近),可以使用 pd.concat() 将它们按行或列方向拼接。

1. 添加行:DataFrame 连接

import pandas as pd# 加载多个结构相似的 CSV 文件
df1 = pd.read_csv('data/demo1.csv')
df2 = pd.read_csv('data/demo2.csv')
df3 = pd.read_csv('data/demo3.csv')# 沿行方向(垂直)连接
row_concat = pd.concat([df1, df2, df3], ignore_index=True)  # 重置索引
print(row_concat)

参数说明:

  • ignore_index=True:重新生成从 0 开始的整数索引,避免重复行标签。
  • 默认 axis=0 表示按行连接(上下拼接)。

2. 添加行:DataFrame 与 Series 连接

df1 = pd.read_csv('data/demo1.csv')
new_series = pd.Series(['n1', 'n2', 'n3', 'n4'], index=['A', 'B', 'C', 'D'])  # 推荐指定索引# 错误方式(不推荐):
# pd.concat([df1, new_series])  # Series 被当作新列添加,造成 NaN 填充# 正确方式:将 Series 转为 DataFrame 再连接
new_row_df = pd.DataFrame([new_series], index=['new_row'])  # 包装成一行的 DataFrame
result = pd.concat([df1, new_row_df], ignore_index=False)

关键点:

  • 直接 concat([df, series]) 会尝试按列对齐,导致 Series 成为新列,原数据缺失处填 NaN
  • 应先将 Series 转换为单行 DataFrame,并确保其 index 与目标 DataFrame 的列名匹配。

3. 添加行:使用 append() 方法(已弃用,建议用 concat)

注意:DataFrame.append() 方法在 Pandas 1.4+ 中已被 弃用(deprecated),官方推荐使用 pd.concat() 替代。

旧写法(不推荐):

# 已弃用
new_row = {'A': 'n1', 'B': 'n2', 'C': 'n3', 'D': 'n4'}
df1 = df1.append(new_row, ignore_index=True)

新写法(推荐):

new_row_df = pd.DataFrame([{'A': 'n1', 'B': 'n2', 'C': 'n3', 'D': 'n4'}])
df1 = pd.concat([df1, new_row_df], ignore_index=True)

4. 添加行:重置索引

当多次连接后,行索引可能出现重复或无序:

result = pd.concat([df1, df2, df3])
result = result.reset_index(drop=True)  # 丢弃原索引,生成新整数索引

建议:在垂直连接时始终使用 ignore_index=True 或后续调用 reset_index(drop=True)


5. 添加列:使用 concat()

水平连接(axis=1)可实现列拼接:

col_concat = pd.concat([df1, df2], axis=1)  # 按列拼接(左右)

注意:

  • 默认按行索引对齐,若索引不一致会导致 NaN
  • 若两个 DataFrame 行数不同但索引相同,也能对齐。

6. 添加列:直接赋值(最常用)

# 添加标量(广播)
df1['new_col'] = 0# 添加列表或数组(长度需匹配)
df1['new_col'] = [1, 2, 3, 4]# 添加 Series(按索引自动对齐)
s = pd.Series([10, 20, 30, 40], index=df1.index)
df1['new_col'] = s

这是最简洁高效的方式,适用于大多数场景。


7. 添加列:重置索引的影响

如果 Series 的索引与 DataFrame 不一致,直接赋值可能导致 NaN

s = pd.Series([10, 20, 30, 40], index=[0, 1, 2, 3])
df1['new_col'] = s  # 若 df1 索引为 [0,1,2,3],则正常;否则部分为 NaN

解决方案:

  • 使用 .values 强制忽略索引:
    df1['new_col'] = s.values  # 忽略索引,按顺序赋值
    
  • 或先对齐索引:s.reindex(df1.index)

二、合并多个数据集(Merge & Join)

当数据集之间有共同键(key),但结构不同(如主表+属性表),应使用 mergejoin

方法类型用途对齐方式默认连接类型
pd.concat()函数拼接多个对象(行/列)按索引对齐外连接(outer)
DataFrame.join()方法水平合并多个 DataFrame左表列/行索引 vs 右表行索引左连接(left)
pd.merge()函数灵活合并两个 DataFrame基于列或索引内连接(inner)

1. pd.merge():最灵活的合并方式

left = pd.DataFrame({'key': ['K0', 'K1', 'K2'],'A': ['A0', 'A1', 'A2']
})right = pd.DataFrame({'key': ['K0', 'K1', 'K3'],'B': ['B0', 'B1', 'B3']
})# 基于列 'key' 合并
merged = pd.merge(left, right, on='key', how='inner')

参数详解:

  • on:指定连接键(列名)
  • how:连接方式
    • 'inner':交集(默认)
    • 'outer':并集
    • 'left':保留左表所有行
    • 'right':保留右表所有行
  • left_on, right_on:左右表键名不同时使用
  • left_index=True, right_index=True:基于索引合并

示例:不同列名合并

pd.merge(left, right, left_on='key', right_on='key_r', how='outer')

2. DataFrame.join():基于索引的便捷合并

result = left.join(right.set_index('key'), on='key', how='left')
# 或者 right 的索引已经是 'key'
# result = left.join(right, on='key')

特点:

  • 默认以左表为基准how='left'
  • 通常用于:主表 + 多个属性表(如公司信息 + 股价、行业等)
  • 支持多表连接:df.join([df2, df3, df4])

注意:join() 默认是左连接,而 merge() 默认是内连接


3. concat() vs merge() vs join() 对比总结

功能concat()merge()join()
是否支持多对象支持多个仅两个支持多个
连接方向行或列仅列(水平)仅列(水平)
对齐依据索引列或索引左表列/索引 vs 右表索引
默认连接方式outerinnerleft
典型用途日志文件合并、时间序列拼接主键关联表(如订单+用户)属性扩展(如 ID + 特征)

三、常见实践建议

推荐流程:

  1. 清理单个数据集 → 遵循整洁数据原则
  2. 统一列名与数据类型
  3. 根据关系选择合并方式:
    • 结构相同 → pd.concat()
    • 有公共键 → pd.merge()df.join()
  4. 合并后检查:
    • 行数是否合理?
    • 是否出现意外的 NaN
    • 索引是否需要重置?

常见错误避免:

  • 忘记 ignore_index=True 导致索引重复
  • 直接 concat(df, series) 而未包装成 DataFrame
  • 使用已弃用的 .append()
  • merge 时未指定 on,导致笛卡尔积
  • 忽视 how 参数,丢失数据(如默认 inner 丢掉不匹配行)

四、总结

数据整合三剑客

1. pd.concat()
  • 用途:拼接多个对象(行/列)
  • axis=0: 上下拼(常用)
  • axis=1: 左右拼
  • ignore_index=True 重置索引
2. pd.merge()
  • 用途:基于列或索引合并两个表
  • on=‘key’: 指定连接列
  • how: inner, outer, left, right
  • 最灵活,推荐用于主键关联
3. df.join()
  • 用途:基于索引合并多个表
  • 默认左连接
  • 常用于主表 + 多个属性表

Tip: 优先使用 concatmergejoin 适合索引对齐场景。


五、补充

1. 合并时处理重复列名(suffixes 参数)

当两个 DataFrame 有相同列名但不是连接键时,mergejoin 会自动添加后缀避免冲突。

left = pd.DataFrame({'key': ['K0', 'K1'], 'value': [1, 2]})
right = pd.DataFrame({'key': ['K0', 'K1'], 'value': [3, 4]})merged = pd.merge(left, right, on='key', suffixes=('_left', '_right'))
print(merged)
# 输出:
#   key  value_left  value_right
# 0  K0           1            3
# 1  K1           2            4

用途:比较同一指标在不同时间/来源的数据。

建议:始终显式设置 suffixes,避免默认的 _x, _y 造成混淆。


2. 基于多列合并(复合键合并)

有时需要多个字段共同作为“主键”来合并。

pd.merge(df1, df2, on=['date', 'city', 'product_id'], how='inner')

典型场景

  • 按“日期+地区”合并天气与销售数据
  • 按“用户ID+商品ID”合并评分与评论

注意:确保多列组合后能唯一标识一条记录,否则可能产生笛卡尔积(行数暴增)。


3. 使用索引进行合并(left_index / right_index)

当数据以索引为唯一标识时(如时间序列、ID),可以直接用索引合并。

# 基于索引合并
result = pd.merge(df1, df2, left_index=True, right_index=True, how='outer')# 等价于:
result = df1.join(df2, how='outer')

优势:避免创建冗余的 ID 列;适合时间对齐。

示例:对齐不同频率的股票数据(日频 + 周频)


4. concat 的 join 参数:inner vs outer

pd.concat(..., axis=1) 默认是 outer(外连接),但可通过 join='inner' 只保留共有的行。

# 只保留所有表都存在的行
pd.concat([df1, df2, df3], axis=1, join='inner')# 保留所有行(默认)
pd.concat([df1, df2, df3], axis=1, join='outer')

用途

  • join='inner':严格对齐,避免 NaN
  • join='outer':保留全部信息,后续填充缺失值

5. 合并性能优化建议

场景建议
大数据集合并确保连接键是 categoryint 类型(比 object 快)
多次合并先合并小表,再与大表连接
时间序列拼接使用 pd.concat() + ignore_index=False 保持时间索引
内存不足考虑使用 daskpolars 替代

小技巧:合并前检查数据类型:

print(df1['key'].dtype)
print(df2['key'].dtype)
# 确保类型一致,否则可能导致匹配失败!

6. 合并后的质量检查清单(QA)

每次合并后建议检查以下几点:

def check_merge_result(left, right, merged, how):print(f"左表行数: {len(left)}")print(f"右表行数: {len(right)}")print(f"合并后行数: {len(merged)}")if how == 'inner':assert len(merged) <= min(len(left), len(right))elif how == 'left':assert len(merged) >= len(left)elif how == 'right':assert len(merged) >= len(right)elif how == 'outer':assert len(merged) >= max(len(left), len(right))print(f"新增 NaN 数量: {merged.isna().sum().sum()}")

关键问题

  • 行数是否合理?
  • 是否出现大量 NaN?是否预期?
  • 索引是否重复或混乱?
  • 数据类型是否被意外转换?

缺失值处理

为什么会出现缺失值?

在数据整合过程中,以下操作极易引入 NaN

  • pd.concat(..., axis=1) 拼接列时,行索引不完全对齐
  • pd.merge(..., how='left') 左连接时,右表无匹配记录
  • join 扩展属性时,某些 ID 没有对应信息
  • 不同来源数据字段覆盖不全

Pandas 使用 NaN(Not a Number)表示浮点型缺失值,None 表示对象型缺失值,两者在大部分操作中被视为等价。


一、识别缺失值

1. 检查缺失情况

# 查看每个字段缺失数量
df.isnull().sum()# 查看整体缺失比例
df.isnull().mean() * 100# 查看是否有任意缺失
df.isnull().any().any()# 可视化缺失模式(需 seaborn)
import seaborn as sns
sns.heatmap(df.isnull(), cbar=True, yticklabels=False, cmap='viridis')

建议:在每次合并后立即检查缺失情况


二、缺失值的产生场景与应对策略

整合操作缺失原因建议处理方式
concat(axis=0)不同文件字段不一致统一列名 / 补充默认值
concat(axis=1)行索引不对齐对齐索引 / 使用 join='inner'
merge(how='left')右表无匹配键检查数据完整性 / 改用 outer
join()某些 ID 无扩展信息补充默认属性 / 标记为“未知”

三、处理缺失值的常用方法

1. 删除缺失值(dropna

适用于:缺失严重且无法填补,或样本足够多。

# 删除任意含缺失的行
df.dropna(axis=0, how='any')# 删除所有值都缺失的列
df.dropna(axis=1, how='all')# 只在关键列缺失时删除
df.dropna(subset=['user_id', 'order_date'])

风险:可能导致样本偏差或信息丢失。


2. 填充缺失值(fillna

(1)填充固定值
df['category'] = df['category'].fillna('Unknown')
df['price'] = df['price'].fillna(0)
(2)前向/后向填充(适合时间序列)
df['value'] = df['value'].fillna(method='ffill')  # 用前一个值填充
df['value'] = df['value'].fillna(method='bfill')  # 用后一个值填充

method 参数已弃用,推荐使用 ffill / bfill 方法:

df['value'] = df['value'].ffill()
(3)用统计量填充
# 数值型:均值、中位数
df['age'] = df['age'].fillna(df['age'].mean())
df['income'] = df['income'].fillna(df['income'].median())# 分类型:众数
mode_value = df['gender'].mode()[0]  # 取第一个众数
df['gender'] = df['gender'].fillna(mode_value)
(4)用模型预测填充(高级)
from sklearn.impute import KNNImputer
imputer = KNNImputer(n_neighbors=5)
df[['age', 'income']] = imputer.fit_transform(df[['age', 'income']])

3. 插值法填充(interpolate

适用于:时间序列或有序数据。

# 线性插值
df['value'] = df['value'].interpolate(method='linear')# 时间序列插值(考虑时间间隔)
df['value'] = df.set_index('date')['value'].interpolate(method='time').values# 多项式插值(更平滑)
df['value'] = df['value'].interpolate(method='polynomial', order=2)

4. 标记缺失(创建指示变量)

有时“是否缺失”本身就是一个重要特征。

# 创建缺失标志列
df['age_missing'] = df['age'].isnull().astype(int)
df['price_missing'] = df['price'].isnull().astype(int)

用途:在机器学习中作为额外特征,帮助模型理解数据质量。


四、结合数据整合的完整流程示例

# 示例:合并用户信息与订单数据
users = pd.read_csv('users.csv')   # 包含 user_id, age, city
orders = pd.read_csv('orders.csv') # 包含 order_id, user_id, amount# 步骤1:合并
merged = pd.merge(orders, users, on='user_id', how='left')# 步骤2:检查缺失
print("合并后缺失情况:")
print(merged.isnull().sum())# 步骤3:处理缺失
merged['city'] = merged['city'].fillna('Unknown')
merged['age'] = merged['age'].fillna(merged['age'].median())# 步骤4:标记缺失(可选)
merged['age_was_missing'] = (merged['age'] == merged['age'].median()).astype(int)# 步骤5:验证
assert merged.isnull().sum().sum() == 0  # 确保无缺失

五、缺失值处理策略选择指南

场景推荐方法
缺失率 < 5%可考虑删除行
数值型变量均值/中位数填充,或插值
分类型变量众数填充,或新增“Unknown”类别
时间序列前向填充、时间插值
关键字段无匹配检查数据源,避免错误合并
机器学习建模结合 fillna + 缺失标志列

六、最佳实践建议

黄金法则

“宁可标记,也不要随意删除或填充。”

推荐流程

  1. 合并前:统一字段、数据类型、索引
  2. 合并后:立即检查缺失分布
  3. 分析缺失机制
    • MCAR(完全随机缺失)
    • MAR(随机缺失)
    • MNAR(非随机缺失)
  4. 选择合适策略:根据业务逻辑决定如何处理
  5. 记录处理过程:便于复现和审计

七、缺失值处理速查表

检查

  • df.isnull().sum() → 查看各列缺失数
  • df.isnull().mean() → 查看缺失比例

删除

  • df.dropna() → 删除含缺失的行
  • df.dropna(subset=['col']) → 指定列删除

填充

  • df['col'].fillna(0) → 填固定值
  • df['col'].fillna(df['col'].mean()) → 填均值
  • df['col'].ffill() → 前向填充
  • df['col'].interpolate() → 插值

标记

  • df['col_missing'] = df['col'].isnull().astype(int)

http://www.xdnf.cn/news/18371.html

相关文章:

  • Redission是什么
  • 【大模型本地运行与部署框架】Ollama的使用记录
  • TDengine IDMP 运维指南(3. 使用 Ansible 部署)
  • HTML应用指南:利用GET请求获取全国新荣记门店位置信息
  • 代码随想录Day56:图论(冗余连接、冗余连接II)
  • CTFshow系列——命令执行web34-37
  • 深入理解抽象类
  • 08.5【C++ 初阶】实现一个相对完整的日期类--附带源码
  • 《算法导论》第 31 章 - 数论算法
  • AI驱动的SEO关键词优化秘籍
  • DAY 50 预训练模型+CBAM模块
  • RabbitMQ:SpringAMQP 多消费者绑定同一队列
  • .net core web程序如何设置redis预热?
  • 借助AI将infoNES移植到HarmonyOS平台的详细方案介绍
  • 基于SpringBoot+Vue的养老院管理系统的设计与实现 智能养老系统 养老架构管理 养老小程序
  • NestJS @Inject 装饰器入门教程
  • Go语言中的优雅并发控制:通道信号量模式详解
  • MVC、MVP、MVCC 和 MVI 架构的介绍及区别对比
  • 决策树二-泰坦尼克号幸存者
  • Unity常用工具及默认快捷键
  • 视觉测试:确保应用界面一致性
  • 牛客面经 - 2025/8/19
  • 深入理解Redis持久化:让你的数据永不丢失
  • Android Studio常用知识总结
  • 技术攻坚全链铸盾 锁定12月济南第26届食品农产品安全高峰论坛
  • 上网行为管理-内容审计
  • 效果图只是起点:深挖3D可视化在家装建筑中的隐藏金矿
  • Leetcode 3654. Minimum Sum After Divisible Sum Deletions
  • DL00291-联邦学习以去中心化锂离子电池健康预测模型完整实现
  • el-input 重写带图标密码框(点击小眼睛显示、隐藏密码)