pandas数据合并

pandas数据合并

💡 核心要点:Pandas提供了强大的数据合并功能,包括堆叠合并(concat)和主键合并(merge),以及数据重塑功能(stack/unstack),是数据预处理的核心技能。


📚 目录

  1. [堆叠合并 - concat](#堆叠合并 - concat)
  2. [主键合并 - merge](#主键合并 - merge)
  3. [数据重塑 - stack/unstack](#数据重塑 - stack/unstack)
  4. 实战应用场景
  5. 知识点速查表

1. 堆叠合并 - concat

1.1 核心概念

📌 什么是堆叠合并

堆叠合并(Concatenation)是将多个DataFrame像积木一样拼接在一起的操作,类似于把多张表格物理地拼在一起。

💡 两种堆叠方向

  • 横向堆叠(axis=1):沿X轴方向,增加列(特征),类似于在表格右侧添加新列
  • 纵向堆叠(axis=0):沿Y轴方向,增加行(记录),类似于在表格下方添加新行

💡 生活化类比:横向堆叠就像给学生档案添加成绩单,纵向堆叠就像把两个班级的学生名单合并成一个年级名单。


1.2 concat函数详解

🔧 函数签名

python 复制代码
pd.concat(objs, axis=0, join='outer', ignore_index=False, verify_integrity=False)

📊 参数说明

参数 类型 说明 默认值
objs list 要合并的DataFrame列表 必填
axis int 合并方向:0=纵向,1=横向 0
join str 连接方式:'outer'=并集,'inner'=交集 'outer'
ignore_index bool 是否忽略原索引,重新生成0开始的索引 False
verify_integrity bool 是否检查索引重复(True时有重复会报错) False

1.3 横向堆叠(增加列)

💡 知识点:横向合并增加特征

横向堆叠用于将不同来源的特征数据合并到同一张表中,根据行索引进行匹配。

📝 代码示例:学生信息与成绩合并

python 复制代码
import pandas as pd

# 学生基本信息表
dic1 = {
    'name': ['lisa', 'cc', 'john'],
    'age': [18, 19, 20],
    'gender': ['女', '男', '男']
}
stu1 = pd.DataFrame(dic1)

# 学生成绩表
dic2 = {
    'Chinese': [90, 85, 96],
    'English': [85, 95, 65]
}
score1 = pd.DataFrame(dic2)

# 横向堆叠(axis=1)
result = pd.concat(objs=[stu1, score1], axis=1)
print(result)

输出结果:

复制代码
   name  age gender  Chinese  English
0  lisa   18      女       90       85
1    cc   19      男       85       95
2  john   20      男       96       65

⚠️ 注意事项:索引匹配规则

  • 横向堆叠时,pandas会根据行索引自动匹配数据
  • 如果行索引不一致,会产生NaN(缺失值)

📝 代码示例:索引不匹配的情况

python 复制代码
# 修改score1的行索引
score1.index = [1, 2, 3]

# 横向堆叠(默认join='outer',取并集)
result = pd.concat(objs=[stu1, score1], axis=1, join='outer')
print(result)

输出结果:

复制代码
   name   age gender  Chinese  English
0  lisa  18.0      女      NaN      NaN
1    cc  19.0      男     90.0     85.0
2  john  20.0      男     85.0     95.0
3   NaN   NaN    NaN     96.0     65.0

🔍 对比说明:join参数的影响

join参数 说明 结果
'outer' 并集(默认),保留所有索引 索引不匹配处填充NaN
'inner' 交集,只保留共同索引 只保留两表都有的索引

📝 代码示例:使用inner连接

python 复制代码
# 只保留共同的行索引
result = pd.concat(objs=[stu1, score1], axis=1, join='inner')
print(result)
# 输出:只有索引1和2的数据

💎 最佳实践

python 复制代码
✅ 推荐:合并前确保索引一致
score1.index = stu1.index  # 先对齐索引
result = pd.concat([stu1, score1], axis=1)

❌ 避免:直接合并索引不一致的表
result = pd.concat([stu1, score1], axis=1)  # 可能产生大量NaN

1.4 纵向堆叠(增加行)

💡 知识点:纵向合并增加记录

纵向堆叠用于将多批数据合并成一个完整的数据集,根据列名进行匹配。

📝 代码示例:合并多个班级的学生信息

python 复制代码
# 第一批学生信息
dic1 = {
    'name': ['lisa', 'cc', 'john'],
    'age': [18, 19, 20],
    'gender': ['女', '男', '男']
}
stu1 = pd.DataFrame(dic1)

# 第二批学生信息
dic3 = {
    'name': ['lili', 'xiaoming'],
    'age': [18, 19]
}
stu2 = pd.DataFrame(dic3)

# 纵向堆叠(axis=0)
result = pd.concat(
    objs=[stu1, stu2],
    axis=0,
    join='outer',
    ignore_index=True
)
print(result)

输出结果:

复制代码
      name  age gender
0     lisa   18      女
1       cc   19      男
2     john   20      男
3     lili   18    NaN
4  xiaoming   19    NaN

⚠️ 注意事项:列名匹配与索引处理

  1. 列名不一致:缺失的列会填充NaN(如上例中stu2没有gender列)
  2. 索引重复问题:两个表的索引可能重复(如都是0,1,2)
  3. 索引处理方案 :使用ignore_index=True重新生成索引

📊 参数详解:verify_integrity

python 复制代码
# verify_integrity=False(默认):允许索引重复
result = pd.concat([stu1, stu2], axis=0, verify_integrity=False)
# 成功合并,但索引会重复(0,1,2,0,1)

# verify_integrity=True:检查索引重复
result = pd.concat([stu1, stu2], axis=0, verify_integrity=True)
# 报错:ValueError: Indexes have overlapping values

💎 最佳实践:纵向合并时重置索引

python 复制代码
✅ 推荐:使用ignore_index=True
result = pd.concat([stu1, stu2], axis=0, ignore_index=True)
# 自动生成新索引:0,1,2,3,4

❌ 避免:保留重复索引
result = pd.concat([stu1, stu2], axis=0)
# 索引重复:0,1,2,0,1(容易引起混淆)

1.5 实战案例:销售数据合并

🎯 应用场景1:合并上下半年销售数据

📝 代码示例

python 复制代码
import pandas as pd

# 上半年销售数据
data_first_half = {
    'Month': ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
    'Sales': [150, 200, 250, 300, 350, 400]
}
df_first_half = pd.DataFrame(data_first_half)

# 下半年销售数据
data_second_half = {
    'Month': ['Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    'Sales': [450, 500, 550, 600, 650, 700]
}
df_second_half = pd.DataFrame(data_second_half)

# 纵向堆叠得到全年数据
yearly_sales = pd.concat(
    objs=[df_first_half, df_second_half],
    axis=0,
    ignore_index=True
)
print(yearly_sales)

输出结果:

复制代码
   Month  Sales
0    Jan    150
1    Feb    200
...
11   Dec    700

🎯 应用场景2:合并不同部门的数据

📝 代码示例

python 复制代码
# 销售部数据
sales_data = {
    'Date': ['2023-01-01', '2023-01-02', '2023-01-03'],
    'Sales': [1000, 1500, 2000]
}
df_sales = pd.DataFrame(sales_data)
df_sales.set_index('Date', inplace=True)  # 将日期设为索引

# 市场部数据
marketing_data = {
    'Date': ['2023-01-01', '2023-01-02', '2023-01-03'],
    'Marketing_Expense': [300, 400, 500]
}
df_marketing = pd.DataFrame(marketing_data)
df_marketing.set_index('Date', inplace=True)

# 横向堆叠合并
result = pd.concat(
    objs=[df_sales, df_marketing],
    axis=1
).reset_index()  # 将索引重置为列

print(result)

输出结果:

复制代码
         Date  Sales  Marketing_Expense
0  2023-01-01   1000                300
1  2023-01-02   1500                400
2  2023-01-03   2000                500

🔧 关键方法:set_index() 和 reset_index()

方法 作用 参数
set_index(col, inplace=True) 将某列设为行索引 inplace=True直接修改原数据
reset_index() 将行索引重置为列 返回新DataFrame

💡 知识点:为什么要使用索引

  • 横向合并时,pandas通过索引匹配数据
  • 将日期设为索引,可以确保按日期正确对齐数据

2. 主键合并 - merge

2.1 核心概念

📌 什么是主键合并

主键合并(Merge)类似于SQL的JOIN操作,通过指定的**关键列(主键)**将两个表关联起来,而不是简单的物理拼接。

💡 与concat的区别

特性 concat merge
合并方式 物理堆叠(按位置/索引) 逻辑关联(按主键值)
适用场景 结构相同的数据拼接 关系型数据关联
类比 把两张纸贴在一起 根据学号匹配学生和成绩

💡 生活化类比:concat像是把两摞卡片叠在一起,merge像是根据身份证号把个人信息和银行账户关联起来。


2.2 merge函数详解

🔧 函数签名

python 复制代码
pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None, 
         left_index=False, right_index=False)

📊 参数说明

参数 类型 说明 默认值
left DataFrame 左表(主表) 必填
right DataFrame 右表(从表) 必填
how str 连接方式:'inner'/'outer'/'left'/'right' 'inner'
on str/list 主键列名(两表列名相同时使用) None
left_on str/list 左表的主键列名 None
right_on str/list 右表的主键列名 None
left_index bool 使用左表的行索引作为主键 False
right_index bool 使用右表的行索引作为主键 False

2.3 四种连接方式

📌 核心概念:SQL风格的连接

merge提供了四种连接方式,对应SQL中的JOIN操作。

🔍 对比说明:四种连接方式

连接方式 说明 保留数据 SQL等价
inner 内连接 只保留两表都有的记录 INNER JOIN
outer 外连接(全连接) 保留两表所有记录 FULL OUTER JOIN
left 左连接 保留左表所有记录 LEFT JOIN
right 右连接 保留右表所有记录 RIGHT JOIN

📝 代码示例:准备测试数据

python 复制代码
import pandas as pd

# 左表:学生基本信息
left_tb = pd.DataFrame(
    [[1, '张三', 20, 1],
     [2, '李四', 21, 0],
     [4, '王五', 22, 1]],
    columns=['id', 'name', 'age', 'gender']
)

# 右表:学生成绩
right_tb = pd.DataFrame(
    [[1, 81, 91],
     [2, 51, 41],
     [3, 99, 98],
     [4, 61, 71]],
    columns=['id', 'math', 'chinese']
)

数据说明:

  • 左表有id: 1, 2, 4(缺少3)
  • 右表有id: 1, 2, 3, 4(完整)

2.3.1 内连接(inner)

💡 知识点:只保留匹配的记录

内连接只保留两个表中主键都存在的记录,类似于求交集。

📝 代码示例

python 复制代码
# 内连接:只保留id在两表都存在的记录
result = pd.merge(
    left=left_tb,
    right=right_tb,
    how='inner',
    on='id'
)
print(result)

输出结果:

复制代码
   id name  age  gender  math  chinese
0   1   张三   20       1    81       91
1   2   李四   21       0    51       41
2   4   王五   22       1    61       71

⚠️ 注意事项

  • id=3的记录被排除(左表中不存在)
  • 结果只包含id为1,2,4的记录

🎯 应用场景

  • 数据清洗:只保留完整的记录
  • 数据验证:检查两表的交集

2.3.2 外连接(outer)

💡 知识点:保留所有记录

外连接保留两个表中所有的记录,缺失的地方填充NaN。

📝 代码示例

python 复制代码
# 外连接:保留所有记录
result = pd.merge(
    left=left_tb,
    right=right_tb,
    how='outer',
    on='id'
)
print(result)

输出结果:

复制代码
   id name   age  gender  math  chinese
0   1   张三  20.0     1.0    81       91
1   2   李四  21.0     0.0    51       41
2   4   王五  22.0     1.0    61       71
3   3  NaN   NaN     NaN    99       98

⚠️ 注意事项

  • id=3的记录被保留,但左表的列填充NaN
  • 适合需要完整数据视图的场景

2.3.3 左连接(left)

💡 知识点:以左表为主

左连接保留左表所有记录,右表没有匹配的填充NaN。

📝 代码示例

python 复制代码
# 左连接:保留左表所有记录
result = pd.merge(
    left=left_tb,
    right=right_tb,
    how='left',
    on='id'
)
print(result)

输出结果:

复制代码
   id name  age  gender  math  chinese
0   1   张三   20       1    81       91
1   2   李四   21       0    51       41
2   4   王五   22       1    61       71

🎯 应用场景

  • 主表数据完整性:确保主表所有记录都保留
  • 补充信息:为主表添加额外信息(有则添加,无则为空)

2.3.4 右连接(right)

💡 知识点:以右表为主

右连接保留右表所有记录,左表没有匹配的填充NaN。

📝 代码示例

python 复制代码
# 右连接:保留右表所有记录
result = pd.merge(
    left=left_tb,
    right=right_tb,
    how='right',
    on='id'
)
print(result)

输出结果:

复制代码
   id name   age  gender  math  chinese
0   1   张三  20.0     1.0    81       91
1   2   李四  21.0     0.0    51       41
2   3  NaN   NaN     NaN    99       98
3   4   王五  22.0     1.0    61       71

💎 最佳实践:选择合适的连接方式

python 复制代码
✅ 内连接:数据分析时,只关注完整记录
result = pd.merge(left, right, how='inner', on='id')

✅ 左连接:以主表为准,补充额外信息
result = pd.merge(left, right, how='left', on='id')

✅ 外连接:需要完整数据视图,发现数据缺失
result = pd.merge(left, right, how='outer', on='id')

2.4 不同主键名称的合并

💡 知识点:使用left_on和right_on

当两个表的主键列名不同时,需要分别指定左表和右表的主键列名。

📝 代码示例:列名不同的情况

python 复制代码
# 修改左表的列名:id -> sid
left_tb = left_tb.rename(columns={'id': 'sid'})

# 使用left_on和right_on指定不同的主键列
result = pd.merge(
    left=left_tb,
    right=right_tb,
    left_on='sid',      # 左表的主键列
    right_on='id',      # 右表的主键列
    how='outer'
)
print(result)

输出结果:

复制代码
   sid name   age  gender  id  math  chinese
0  1.0   张三  20.0     1.0   1    81       91
1  2.0   李四  21.0     0.0   2    51       41
2  4.0   王五  22.0     1.0   4    61       71
3  NaN  NaN   NaN     NaN   3    99       98

⚠️ 注意事项

  • 结果中会同时保留sid和id两列
  • 如果不需要重复列,可以在合并后删除

💎 最佳实践:处理重复列

python 复制代码
✅ 推荐:合并后删除重复列
result = pd.merge(left, right, left_on='sid', right_on='id', how='outer')
result = result.drop('id', axis=1)  # 删除重复的id列

✅ 推荐:合并前统一列名
left_tb = left_tb.rename(columns={'sid': 'id'})
result = pd.merge(left, right, on='id', how='outer')

2.5 使用索引作为主键

💡 知识点:left_index和right_index参数

有时主键不在列中,而是在行索引中,此时需要使用left_indexright_index参数。

📝 代码示例:索引作为主键

python 复制代码
# 创建一个以索引为主键的表
gender_tb = pd.DataFrame([['女'], ['男']], columns=['性别'])
gender_tb.index = [1, 2]  # 设置行索引

# 使用左表的索引和右表的id列进行合并
result = pd.merge(
    left=gender_tb,
    right=right_tb,
    left_index=True,        # 使用左表的行索引
    right_on='id',          # 使用右表的id列
    how='inner'
)
print(result)

2.6 实战案例:数据完整性检查

🎯 应用场景:检查用户订单数据

📝 代码示例:准备数据

python 复制代码
import numpy as np
import pandas as pd

# 用户表
user = pd.DataFrame({
    "ID": [1, 2, 3, 4],
    "Name": list("abcd"),
    "age": np.random.randint(18, 60, 4),
    "gender": np.random.randint(0, 2, 4)
})

# 订单表(注意:userid=5不在用户表中)
order = pd.DataFrame({
    "orderid": [1, 2, 3, 4],
    "userid": [2, 3, 4, 5],
    "sales": np.random.randint(10, 100, 4)
})

案例1:查找未下单的用户

💡 知识点:使用isin()方法

python 复制代码
# 方法1:使用isin()
no_order_users = user[~user.ID.isin(order.userid)]
print("未下单的用户:")
print(no_order_users)

💡 知识点:使用左连接+isnull()

python 复制代码
# 方法2:使用左连接
df1 = pd.merge(
    left=user,
    right=order,
    left_on='ID',
    right_on='userid',
    how='left'
)

# 找出orderid为空的记录(即未下单的用户)
no_order_users = df1[df1['orderid'].isnull()]['Name']
print("未下单的用户:")
print(no_order_users)

输出结果:

复制代码
未下单的用户:
0    a
Name: Name, dtype: object

🔍 对比说明:两种方法的优劣

方法 优点 缺点
isin() 代码简洁,性能好 只能判断存在性
merge + isnull() 可以获取更多信息 代码较长

案例2:检查数据约束违规

💡 知识点:发现不符合约束的记录

业务规则:下单的用户必须存在于用户表中。

python 复制代码
# 方法1:使用isin()
invalid_orders = order[~order.userid.isin(user.ID)]
print("不符合约束的订单:")
print(invalid_orders)

💡 知识点:使用右连接+isnull()

python 复制代码
# 方法2:使用右连接
df2 = pd.merge(
    left=user,
    right=order,
    left_on='ID',
    right_on='userid',
    how='right'
)

# 找出ID为空的记录(即用户不存在的订单)
invalid_orders = df2[df2['ID'].isnull()]
print("不符合约束的订单:")
print(invalid_orders)

输出结果:

复制代码
不符合约束的订单:
   ID Name  age  gender  orderid  userid  sales
3 NaN  NaN  NaN     NaN        4       5     XX

💎 最佳实践:数据质量检查流程

python 复制代码
✅ 推荐:建立完整的数据检查流程

# 1. 检查主表完整性
missing_users = user[~user.ID.isin(order.userid)]

# 2. 检查外键约束
invalid_orders = order[~order.userid.isin(user.ID)]

# 3. 检查数据一致性
merged = pd.merge(user, order, left_on='ID', right_on='userid', how='outer')
inconsistent = merged[merged['ID'].isnull() | merged['userid'].isnull()]

# 4. 生成数据质量报告
print(f"未下单用户数:{len(missing_users)}")
print(f"无效订单数:{len(invalid_orders)}")
print(f"数据不一致记录数:{len(inconsistent)}")

3. 数据重塑 - stack/unstack

3.1 核心概念

📌 什么是数据重塑

数据重塑(Reshaping)是改变数据的组织形式,在行索引列索引之间转换数据,而不改变数据的实际内容。

💡 两种重塑操作

  • unstack():将行索引转换为列索引(行变列,数据"展开")
  • stack():将列索引转换为行索引(列变行,数据"堆叠")

💡 生活化类比:unstack像是把竖着排列的书横着摆放,stack像是把横着摆放的书竖着排列。


3.2 多级索引基础

💡 知识点:什么是多级索引

多级索引(MultiIndex)允许在一个轴上有多个索引层级,类似于Excel中的分组。

📝 代码示例:创建多级索引

python 复制代码
import pandas as pd

# 创建学生成绩数据
data = {
    '学生': ['小明', '小明', '小红', '小红', '小刚', '小刚'],
    '科目': ['数学', '语文', '数学', '语文', '数学', '语文'],
    '成绩': [90, 85, 95, 88, 78, 92]
}
df = pd.DataFrame(data)

# 设置多级索引(学生和科目)
multi_index_df = df.set_index(['学生', '科目'])
print(multi_index_df)

输出结果:

复制代码
          成绩
学生 科目    
小明 数学    90
    语文    85
小红 数学    95
    语文    88
小刚 数学    78
    语文    92

⚠️ 注意事项

  • 多级索引有两个层级:第一级是学生,第二级是科目
  • 数据以层级结构组织,便于分组查询

3.3 unstack操作

💡 知识点:将行索引转为列索引

unstack()将最内层的行索引转换为列索引,使数据从"长格式"变为"宽格式"。

📝 代码示例:使用unstack()

python 复制代码
# 将科目从行索引移动到列索引
unstacked_df = multi_index_df.unstack()
print(unstacked_df)

输出结果:

复制代码
      成绩    
科目   数学  语文
学生          
小明    90  85
小红    95  88
小刚    78  92

🔍 对比说明:unstack前后的变化

特性 unstack前 unstack后
行索引 学生+科目(两级) 学生(一级)
列索引 成绩(一级) 成绩+科目(两级)
数据形状 6行1列(长格式) 3行2列(宽格式)
可读性 适合存储 适合展示

🎯 应用场景

  • 数据透视:将分类变量转为列
  • 报表生成:生成交叉表格式
  • 数据可视化:准备宽格式数据

💎 最佳实践

python 复制代码
✅ 推荐:用于生成报表
# 将长格式数据转为宽格式,便于阅读
report = multi_index_df.unstack()

❌ 避免:对大量类别使用unstack
# 如果科目有100个,会生成100列,不便查看

3.4 stack操作

💡 知识点:将列索引转为行索引

stack()将列索引转换为行索引,使数据从"宽格式"变为"长格式"。

📝 代码示例:使用stack()

python 复制代码
# 将科目从列索引移动回行索引
stacked_df = unstacked_df.stack()
print(stacked_df)

输出结果:

复制代码
学生  科目
小明  数学    90
     语文    85
小红  数学    95
     语文    88
小刚  数学    78
     语文    92
Name: 成绩, dtype: int64

🔍 对比说明:stack与unstack的关系

操作 方向 结果
unstack() 行→列 长格式→宽格式
stack() 列→行 宽格式→长格式

💡 知识点:stack和unstack互为逆操作

python 复制代码
# 原始数据
original = multi_index_df

# unstack后再stack,恢复原状
result = multi_index_df.unstack().stack()

# 验证是否相同
print(original.equals(result))  # 输出:True

3.5 reset_index()配合使用

💡 知识点:将索引转为普通列

在数据重塑后,常常需要将索引重置为普通列,便于后续处理。

📝 代码示例:完整的数据重塑流程

python 复制代码
# 1. 原始数据
data = {
    '学生': ['小明', '小明', '小红', '小红', '小刚', '小刚'],
    '科目': ['数学', '语文', '数学', '语文', '数学', '语文'],
    '成绩': [90, 85, 95, 88, 78, 92]
}
df = pd.DataFrame(data)

# 2. 设置多级索引
multi_index_df = df.set_index(['学生', '科目'])

# 3. unstack展开
unstacked_df = multi_index_df.unstack()

# 4. stack堆叠
stacked_df = unstacked_df.stack()

# 5. 重置索引,将多级索引转为普通列
final_df = stacked_df.reset_index()
print(final_df)

输出结果:

复制代码
   学生 科目  成绩
0  小明 数学  90
1  小明 语文  85
2  小红 数学  95
3  小红 语文  88
4  小刚 数学  78
5  小刚 语文  92

🔧 关键方法总结

方法 作用 使用场景
set_index() 将列设为索引 准备多级索引
unstack() 行索引→列索引 长格式→宽格式
stack() 列索引→行索引 宽格式→长格式
reset_index() 索引→普通列 恢复普通表格

💎 最佳实践:数据重塑工作流

python 复制代码
✅ 推荐:完整的数据重塑流程

# 1. 设置索引
df_indexed = df.set_index(['分组列1', '分组列2'])

# 2. 重塑数据
df_reshaped = df_indexed.unstack()  # 或 stack()

# 3. 重置索引
df_final = df_reshaped.reset_index()

# 4. 清理列名(如果需要)
df_final.columns = ['新列名1', '新列名2', ...]

4. 实战应用场景

4.1 场景总结

🎯 concat适用场景

场景 方向 示例
合并多个时间段数据 纵向 上半年+下半年销售数据
合并多个来源数据 纵向 多个分店的销售记录
添加新特征 横向 学生信息+成绩
合并不同维度数据 横向 销售数据+市场费用

🎯 merge适用场景

场景 连接方式 示例
关联主从表 inner 学生表+成绩表
补充信息 left 订单表+用户信息
数据完整性检查 outer 发现缺失数据
数据质量验证 right 检查外键约束

🎯 stack/unstack适用场景

场景 操作 示例
生成透视表 unstack 学生-科目成绩表
数据可视化准备 unstack 时间序列宽格式
数据存储 stack 宽格式→长格式
数据分析 stack 便于分组统计

4.2 综合案例:销售数据分析

🎯 业务场景

某公司需要分析2023年各地区、各产品的销售情况,数据分散在多个表中。

📝 代码示例:完整流程

python 复制代码
import pandas as pd
import numpy as np

# 1. 准备数据
# 第一季度销售数据
q1_sales = pd.DataFrame({
    'region': ['北京', '上海', '广州'],
    'product': ['A', 'B', 'C'],
    'sales': [100, 150, 200]
})

# 第二季度销售数据
q2_sales = pd.DataFrame({
    'region': ['北京', '上海', '广州'],
    'product': ['A', 'B', 'C'],
    'sales': [120, 160, 210]
})

# 产品信息表
product_info = pd.DataFrame({
    'product': ['A', 'B', 'C'],
    'category': ['电子', '服装', '食品'],
    'cost': [50, 80, 100]
})

# 2. 纵向合并季度数据
all_sales = pd.concat(
    [q1_sales, q2_sales],
    axis=0,
    keys=['Q1', 'Q2'],  # 添加季度标识
    names=['quarter', 'id']
).reset_index(level=0)

print("合并后的销售数据:")
print(all_sales)

# 3. 关联产品信息
sales_with_info = pd.merge(
    left=all_sales,
    right=product_info,
    on='product',
    how='left'
)

print("\n关联产品信息后:")
print(sales_with_info)

# 4. 计算利润
sales_with_info['profit'] = sales_with_info['sales'] - sales_with_info['cost']

# 5. 数据透视(使用unstack)
pivot_data = sales_with_info.set_index(['quarter', 'region', 'product'])
pivot_table = pivot_data['profit'].unstack(level='product')

print("\n利润透视表:")
print(pivot_table)

💡 知识点:keys参数的使用

在concat中使用keys参数可以为合并的数据添加标识,便于后续区分数据来源。


4.3 性能优化建议

🚀 concat性能优化

python 复制代码
✅ 推荐:一次性合并多个DataFrame
result = pd.concat([df1, df2, df3, df4], axis=0)

❌ 避免:循环中多次concat
result = df1
for df in [df2, df3, df4]:
    result = pd.concat([result, df], axis=0)  # 每次都创建新对象,效率低

🚀 merge性能优化

python 复制代码
✅ 推荐:使用索引进行merge
df1.set_index('key', inplace=True)
df2.set_index('key', inplace=True)
result = pd.merge(df1, df2, left_index=True, right_index=True)

✅ 推荐:对大数据集使用inner连接
result = pd.merge(df1, df2, how='inner')  # 减少结果集大小

🚀 内存优化

python 复制代码
✅ 推荐:及时删除不需要的中间结果
result = pd.concat([df1, df2], axis=0)
del df1, df2  # 释放内存

✅ 推荐:使用合适的数据类型
df['id'] = df['id'].astype('int32')  # 而不是默认的int64

5. 知识点速查表

5.1 函数对比速查

📊 concat vs merge

特性 concat merge
合并方式 物理堆叠 逻辑关联
主要参数 objs, axis, join left, right, how, on
匹配依据 索引位置 主键值
适用场景 同结构数据拼接 关系型数据关联
性能 较快 较慢(需要匹配)

5.2 参数速查表

📊 concat参数

参数 可选值 说明 优先级
axis 0, 1 0=纵向,1=横向 ⭐⭐⭐
join 'outer', 'inner' 并集/交集 ⭐⭐
ignore_index True, False 重置索引 ⭐⭐
verify_integrity True, False 检查索引重复
keys list 添加层级标识 ⭐⭐

📊 merge参数

参数 可选值 说明 优先级
how 'inner', 'outer', 'left', 'right' 连接方式 ⭐⭐⭐
on str/list 主键列名(相同时) ⭐⭐⭐
left_on str/list 左表主键 ⭐⭐⭐
right_on str/list 右表主键 ⭐⭐⭐
left_index True, False 使用左表索引 ⭐⭐
right_index True, False 使用右表索引 ⭐⭐

5.3 常用方法速查

🔧 索引操作方法

方法 作用 返回值 优先级
set_index(col) 将列设为索引 DataFrame ⭐⭐⭐
reset_index() 将索引重置为列 DataFrame ⭐⭐⭐
unstack() 行索引→列索引 DataFrame ⭐⭐
stack() 列索引→行索引 Series ⭐⭐

🔧 数据检查方法

方法 作用 返回值 优先级
isin(values) 检查值是否在列表中 bool Series ⭐⭐⭐
isnull() 检查是否为空 bool Series ⭐⭐⭐
duplicated() 检查是否重复 bool Series ⭐⭐
equals(other) 比较两个对象是否相同 bool

5.4 学习路径建议

基础必学(第1周)

  1. concat纵向堆叠(axis=0)
  2. concat横向堆叠(axis=1)
  3. merge内连接(how='inner')
  4. merge左连接(how='left')
  5. set_index()和reset_index()

⭐⭐ 进阶提升(第2周)

  1. concat的join参数(inner/outer)
  2. concat的ignore_index参数
  3. merge的四种连接方式对比
  4. 使用left_on和right_on
  5. 使用left_index和right_index
  6. isin()方法进行数据筛选

⭐⭐⭐ 高级技巧(第3周)

  1. unstack()和stack()数据重塑
  2. 多级索引的创建和操作
  3. concat的keys参数
  4. merge的数据完整性检查
  5. 综合案例:多表关联分析
  6. 性能优化技巧

5.5 常见错误与解决方案

错误1:索引不匹配导致NaN

python 复制代码
# 问题代码
result = pd.concat([df1, df2], axis=1)  # 索引不一致

# 解决方案
df2.index = df1.index  # 先对齐索引
result = pd.concat([df1, df2], axis=1)

错误2:主键列名不一致

python 复制代码
# 问题代码
result = pd.merge(df1, df2, on='id')  # df2的主键是user_id

# 解决方案
result = pd.merge(df1, df2, left_on='id', right_on='user_id')

错误3:索引重复导致错误

python 复制代码
# 问题代码
result = pd.concat([df1, df2], axis=0, verify_integrity=True)  # 报错

# 解决方案
result = pd.concat([df1, df2], axis=0, ignore_index=True)

错误4:merge后列名冲突

python 复制代码
# 问题代码
result = pd.merge(df1, df2, on='id')  # 两表都有name列

# 解决方案
result = pd.merge(df1, df2, on='id', suffixes=('_left', '_right'))

5.6 思维导图

复制代码
Pandas数据处理
│
├─ 堆叠合并(concat)
│  ├─ 横向堆叠(axis=1)
│  │  ├─ 增加列/特征
│  │  ├─ 根据索引匹配
│  │  └─ join参数(outer/inner)
│  │
│  └─ 纵向堆叠(axis=0)
│     ├─ 增加行/记录
│     ├─ 根据列名匹配
│     └─ ignore_index参数
│
├─ 主键合并(merge)
│  ├─ 内连接(inner)- 交集
│  ├─ 外连接(outer)- 并集
│  ├─ 左连接(left)- 保留左表
│  ├─ 右连接(right)- 保留右表
│  │
│  └─ 主键指定方式
│     ├─ on(列名相同)
│     ├─ left_on + right_on(列名不同)
│     └─ left_index + right_index(使用索引)
│
└─ 数据重塑(stack/unstack)
   ├─ unstack(行→列)
   │  ├─ 长格式→宽格式
   │  └─ 适合报表展示
   │
   └─ stack(列→行)
      ├─ 宽格式→长格式
      └─ 适合数据存储

5.7 快速决策树

复制代码
需要合并数据?
│
├─ 是否需要根据主键关联?
│  │
│  ├─ 是 → 使用merge
│  │  └─ 选择连接方式
│  │     ├─ 只要完整记录 → inner
│  │     ├─ 保留主表 → left
│  │     ├─ 保留从表 → right
│  │     └─ 保留所有 → outer
│  │
│  └─ 否 → 使用concat
│     └─ 选择方向
│        ├─ 增加列 → axis=1
│        └─ 增加行 → axis=0
│
└─ 需要改变数据形状?
   │
   ├─ 长→宽 → unstack()
   └─ 宽→长 → stack()

📚 总结

核心要点回顾

  1. concat:物理堆叠,适合同结构数据拼接

    • 横向(axis=1):增加列,按索引匹配
    • 纵向(axis=0):增加行,按列名匹配
  2. merge:逻辑关联,适合关系型数据连接

    • 四种连接方式:inner/outer/left/right
    • 灵活的主键指定:on/left_on+right_on/索引
  3. stack/unstack:数据重塑,改变数据组织形式

    • unstack:行→列,长格式→宽格式
    • stack:列→行,宽格式→长格式

📊 参数组合说明

场景 参数组合 说明
两表列名相同 on='id' 最简单的情况
两表列名不同 left_on='sid', right_on='id' 分别指定主键
左表用索引 left_index=True, right_on='id' 左表索引匹配右表列
右表用索引 left_on='id', right_index=True 左表列匹配右表索引
两表都用索引 left_index=True, right_index=True 索引对索引
相关推荐
小白狮ww3 小时前
lammps 教程:npt 控温估计 FCC Cu 熔点
人工智能·深度学习·机器学习·分子动力学·lammps·npt·材料建模
爱打代码的小林4 小时前
numpy库数组笔记
笔记·python·numpy
雪不下4 小时前
计算机中的数学:概率(6)
人工智能·机器学习·概率论
珑墨4 小时前
【AI产品】当下AI产品的变现模式深度分析
人工智能·ai·数据分析·产品运营·aigc·ai编程·ai写作
祝余Eleanor4 小时前
Day 30 函数专题2 装饰器
人工智能·python·机器学习·数据分析
β添砖java5 小时前
机器学习----深度学习部分
人工智能·深度学习·机器学习
FL16238631295 小时前
智慧工地建筑工地常见装备手推车切割机安全帽检测数据集VOC+YOLO格式13364张15类别
深度学习·yolo·机器学习
天一生水water6 小时前
储层认知→技术落地→产量优化
人工智能·算法·机器学习
CClaris6 小时前
手撕 LSTM:用 NumPy 从零实现 LSTM 前向传播
人工智能·numpy·lstm