哈喽,我是小白~
今儿和大家再聊聊pandas中的一个非常重要的内容,数据合并与连接操作!~
在机器学习实验中,Pandas数据合并与连接操作 能够有效地整合多个数据源,确保特征和目标变量一致。通过灵活的连接方式(如内连接、外连接等),这些操作能处理数据缺失和冗余信息,提升数据完整性。高效的数据合并能够简化数据预处理流程,为后续模型训练提供高质量的数据基础。
pd.concat()
– 沿轴连接多个 DataFrame 或 Seriespd.merge()
– 类似 SQL 的合并操作join()
– 基于索引的合并append()
– 添加新行到 DataFramecombine_first()
– 数据补全update()
– 按索引更新 DataFramereindex()
– 重新索引pd.melt()
– 将宽格式数据转为长格式pivot()
– 构建透视表pd.pivot_table()
– 聚合透视表
我们从基础功能和一些特定的场景和大家详细的聊聊~
要更深入地细化 Pandas 的数据合并与连接操作,我们不仅要介绍它们的基本功能,还要详细说明它们在特定场景中的高级用法和一些可能的陷阱。下面我将逐个操作进行更细致的讲解,逐步深入,提供更高级的用法和场景解析。
1. pd.concat()
– 沿轴连接多个 DataFrame 或 Series
pd.concat()
是 Pandas 中最常用的函数之一,它用于沿着指定轴(axis=0 代表行,axis=1 代表列)连接多个数据集(DataFrame 或 Series)。常见的应用场景包括合并多个文件、将不同时间点的数据合并等。
关键参数
axis
: 控制合并的方向,axis=0
沿行,axis=1
沿列。ignore_index
: 如果为True
,新 DataFrame 的索引将被重置为连续的整数索引。join
: 类似于 SQL 中的连接方式,有 'outer'(默认值,取所有列)和 'inner'(取公共列)两种。keys
: 如果给定,会在结果中添加一个多级索引,方便区分数据来源。
Python案例
合并多组数据并保持其来源
使用 keys
参数可以为数据集加上区分标记,形成层次化索引,便于区分来自不同来源的数据。
import pandas as pd
# 创建三个 DataFrame
df1 = pd.DataFrame({'A': ['A0', 'A1'], 'B': ['B0', 'B1']})
df2 = pd.DataFrame({'A': ['A2', 'A3'], 'B': ['B2', 'B3']})
df3 = pd.DataFrame({'A': ['A4', 'A5'], 'B': ['B4', 'B5']})
# 使用 keys 参数合并并加上标识
result = pd.concat([df1, df2, df3], keys=['Group1', 'Group2', 'Group3'])
print(result)
输出:
A B
Group1 0 A0 B0
1 A1 B1
Group2 0 A2 B2
1 A3 B3
Group3 0 A4 B4
1 A5 B5
列合并并处理不同列名
当数据集的列名不完全相同,可以通过 join='outer'
或 join='inner'
来控制是否保留全部列或仅保留公共列。
df1 = pd.DataFrame({'A': ['A0', 'A1'], 'B': ['B0', 'B1']})
df2 = pd.DataFrame({'A': ['A2', 'A3'], 'C': ['C2', 'C3']})
# outer: 保留所有列,缺失值填 NaN
result_outer = pd.concat([df1, df2], axis=1, join='outer')
print(result_outer)
# inner: 只保留公共列
result_inner = pd.concat([df1, df2], axis=1, join='inner')
print(result_inner)
输出 (outer):
A B A C
0 A0 B0 A2 C2
1 A1 B1 A3 C3
输出 (inner):
# 内连接没有公共列,结果为空
2. pd.merge()
– 类似 SQL 的合并操作
merge()
是 Pandas 中功能最丰富的合并操作之一,它可以基于一个或多个列进行连接,类似于 SQL 的 JOIN 操作。merge()
提供了多种连接方式,如内连接、左连接、右连接和外连接,能灵活处理复杂的表结构。
关键参数
on
: 指定要连接的列名。如果不指定,Pandas 会尝试寻找同名列进行连接。left_on
和right_on
: 当左右两个 DataFrame 的列名不同,可以分别用这两个参数指定。how
: 指定连接方式,有 'left'(左连接)、'right'(右连接)、'inner'(内连接,默认)、'outer'(全连接)。suffixes
: 当两个 DataFrame 中有同名列时,用于区分列名的后缀。
Python案例
多键合并
可以通过指定多个列来进行多键合并,尤其适合需要精确匹配多个条件的情况。
# 创建 DataFrame
df1 = pd.DataFrame({'key1': ['K0', 'K0', 'K1'], 'key2': ['X0', 'X1', 'X0'], 'A': ['A0', 'A1', 'A2']})
df2 = pd.DataFrame({'key1': ['K0', 'K1', 'K1'], 'key2': ['X0', 'X0', 'X1'], 'B': ['B0', 'B1', 'B2']})
# 多列合并
result = pd.merge(df1, df2, on=['key1', 'key2'], how='inner')
print(result)
输出:
key1 key2 A B
0 K0 X0 A0 B0
1 K1 X0 A2 B1
不同列名的合并
当两个 DataFrame 的连接列名不同,可以通过 left_on
和 right_on
参数来指定它们。
df1 = pd.DataFrame({'lkey': ['K0', 'K1', 'K2'], 'A': ['A0', 'A1', 'A2']})
df2 = pd.DataFrame({'rkey': ['K0', 'K2', 'K3'], 'B': ['B0', 'B2', 'B3']})
# 使用不同列名进行连接
result = pd.merge(df1, df2, left_on='lkey', right_on='rkey', how='outer')
print(result)
输出:
lkey A rkey B
0 K0 A0 K0 B0
1 K1 A1 NaN NaN
2 K2 A2 K2 B2
3 NaN NaN K3 B3
多对多合并
merge()
支持多对多的合并,即当键值在左右表中都重复时,会产生笛卡尔积的组合。
df1 = pd.DataFrame({'key': ['K0', 'K1'], 'A': ['A0', 'A1']})
df2 = pd.DataFrame({'key': ['K0', 'K0', 'K1'], 'B': ['B0', 'B1', 'B2']})
# 多对多连接
result = pd.merge(df1, df2, on='key', how='inner')
print(result)
输出:
key A B
0 K0 A0 B0
1 K0 A0 B1
2 K1 A1 B2
自连接
merge()
还可以用于自连接,即将一个 DataFrame 自己与自己连接。这在需要匹配某些特定的条件时很有用。
df = pd.DataFrame({'key': ['K0', 'K1', 'K2'], 'value': ['V0', 'V1', 'V2']})
# 自连接
result = pd.merge(df, df, on='key', how='inner')
print(result)
输出:
key value_x value_y
0 K0 V0 V0
1 K1 V1 V1
2 K2 V2 V2
3. join()
– 基于索引的合并
join()
是一个基于索引的横向连接操作。与 merge()
类似,但 join()
默认基于索引进行连接,可以方便地连接两个 DataFrame 而无需指定键。
关键参数
on
: 用于指定连接的列(如果不基于索引)。how
: 指定连接方式,默认为 'left',即左连接。
Python案例
左连接与右连接
df1 = pd.DataFrame({'A': ['A0', 'A1', 'A2']}, index=['K0', 'K1', 'K2'])
df2 = pd.DataFrame({'B': ['B0', 'B1']}, index=['K1', 'K2'])
# 左连接,df1 为主表
result_left = df1.join(df2, how='left')
print(result_left)
#右连接,df2 为主表
result_right = df1.join(df2, how='right')
print(result_right)
输出 (left):
A B
K0 A0 NaN
K1 A1 B0
K2 A2 B1
输出 (right):
A B
K1 A1 B0
K2 A2 B1
多个 DataFrame 同时连接
join()
支持同时连接多个 DataFrame。
df1 = pd.DataFrame({'A': ['A0', 'A1', 'A2']}, index=['K0', 'K1', 'K2'])
df2 = pd.DataFrame({'B': ['B0', 'B1', 'B2']}, index=['K0', 'K1', 'K2'])
df3 = pd.DataFrame({'C': ['C0', 'C1', 'C2']}, index=['K0', 'K1', 'K2'])
# 同时连接多个 DataFrame
result = df1.join([df2, df3])
print(result)
输出:
A B C
K0 A0 B0 C0
K1 A1 B1 C1
K2 A2 B2 C2
4. append()
– 添加新行到 DataFrame
append()
是一种简单的行连接操作,它用于将一个 DataFrame 或 Series 作为新行追加到已有的 DataFrame 中。它的行为类似于 concat()
,但仅适用于行方向(axis=0
)。
关键参数
ignore_index
: 设置为True
时,会重新分配新 DataFrame 的索引;默认为False
,会保留原索引。verify_integrity
: 设置为True
时,如果产生重复索引则会抛出错误。
Python案例
import pandas as pd
# 创建两个 DataFrame
df1 = pd.DataFrame({'A': ['A0', 'A1'], 'B': ['B0', 'B1']})
df2 = pd.DataFrame({'A': ['A2', 'A3'], 'B': ['B2', 'B3']})
# 追加新行
result = df1.append(df2, ignore_index=True)
print(result)
输出:
A B
0 A0 B0
1 A1 B1
2 A2 B2
3 A3 B3
注意事项
效率问题: append()
在处理大数据集时效率不高,因为每次调用都会生成一个新的 DataFrame。如果要多次追加数据,建议使用列表然后一次性concat()
。
处理 Series 的场景
# 创建一个 Series 并追加到 DataFrame
s = pd.Series(['A4', 'B4'], index=['A', 'B'])
# 追加单个 Series 作为新行
result = df1.append(s, ignore_index=True)
print(result)
输出:
A B
0 A0 B0
1 A1 B1
2 A4 B4
5. combine_first()
– 数据补全
combine_first()
用于将两个 DataFrame 按照相同的索引进行合并。如果第一个 DataFrame 中某个位置为空值(NaN),则从第二个 DataFrame 中取该位置的值。它是数据补全或修复缺失值的理想选择。
Python案例
# 创建两个 DataFrame
df1 = pd.DataFrame({'A': [1, None], 'B': [None, 2]})
df2 = pd.DataFrame({'A': [3, 4], 'B': [5, 6]})
# 使用 df2 的数据补全 df1
result = df1.combine_first(df2)
print(result)
输出:
A B
0 1.0 5.0
1 4.0 2.0
常见应用场景
补全数据缺失值:
combine_first()
常用于数据修复场景,比如有一个主要的数据集和一个备份数据集,可以用备份集的非空值来补全主要集的空值。时间序列数据的拼接:可以用于补充不同来源的时间序列数据。
6. update()
– 按索引更新 DataFrame
update()
用于根据另一个 DataFrame 中的值更新当前 DataFrame 的数据。它不会返回新对象,而是就地更新。通常用于将一个表中的某些值覆盖到另一个表中,而保持表的结构不变。
Python案例
# 创建两个 DataFrame
df1 = pd.DataFrame({'A': ['A0', 'A1', 'A2'], 'B': ['B0', 'B1', 'B2']})
df2 = pd.DataFrame({'A': ['A3', 'A4'], 'B': ['B3', 'B4']}, index=[1, 2])
# 更新 df1 中的数据
df1.update(df2)
print(df1)
输出:
A B
0 A0 B0
1 A3 B3
2 A4 B4
注意事项
update()
是就地操作,它不会返回一个新的 DataFrame,而是直接修改原 DataFrame。如果需要保留原始数据,可以在调用之前进行 copy()
操作。
7. reindex()
– 重新索引
reindex()
用于根据新的索引重新排列 DataFrame 或 Series 的行或列。可以用它来引入缺失值、根据新的顺序重新排列数据,或者根据新的列名调整 DataFrame 的列顺序。
关键参数
axis
: 控制重新索引的轴,axis=0
表示行,axis=1
表示列。fill_value
: 指定在新的索引中填充缺失值的默认值。
Python案例
# 创建一个 DataFrame
df = pd.DataFrame({'A': ['A0', 'A1', 'A2'], 'B': ['B0', 'B1', 'B2']})
# 重新按新的索引排列
result = df.reindex([0, 2, 1, 3])
print(result)
输出:
A B
0 A0 B0
2 A2 B2
1 A1 B1
3 NaN NaN
常见应用场景
时间序列数据的对齐:可以使用
reindex()
对齐两个时间序列。根据新列名重排列:用
reindex()
来调整 DataFrame 列的顺序,特别是在对某些列进行操作时。
# 根据新列名重排列
result = df.reindex(columns=['B', 'A'])
print(result)
输出:
B A
0 B0 A0
1 B1 A1
2 B2 A2
8. pd.melt()
– 将宽格式数据转为长格式
melt()
用于将宽格式的数据转换为长格式,适合数据透视表、长格式的分析和可视化。它将多个列名作为变量名,然后将对应值放入新的列中。
Python案例
# 创建一个 DataFrame
df = pd.DataFrame({
'ID': ['ID1', 'ID2'],
'Math': [85, 90],
'Physics': [88, 93],
'Chemistry': [92, 85]
})
# 将宽表转换为长表
result = pd.melt(df, id_vars=['ID'], value_vars=['Math', 'Physics', 'Chemistry'],
var_name='Subject', value_name='Score')
print(result)
输出:
ID Subject Score
0 ID1 Math 85
1 ID2 Math 90
2 ID1 Physics 88
3 ID2 Physics 93
4 ID1 Chemistry 92
5 ID2 Chemistry 85
常见应用场景
数据可视化准备:将宽格式的特征列展开为长格式,便于在绘图工具(如 Seaborn)中进行分组和绘制。
数据透视:当多个列代表相同的变量但分布在不同列时,通过
melt()
将其转换为标准的长格式。
9. pivot()
– 构建透视表
pivot()
用于将长格式数据转化为宽格式,类似于 Excel 中的透视表。通过指定行索引、列索引和要展示的值,可以将数据进行汇总和展开。
Python案例
# 创建一个长格式的 DataFrame
df = pd.DataFrame({
'ID': ['ID1', 'ID2', 'ID1', 'ID2'],
'Subject': ['Math', 'Math', 'Physics', 'Physics'],
'Score': [85, 90, 88, 93]
})
# 构建透视表
result = df.pivot(index='ID', columns='Subject', values='Score')
print(result)
输出:
Subject Math Physics
ID
ID1 85 88
ID2 90 93
注意事项
pivot()
要求每个组合(index
和columns
)必须是唯一的。如果不唯一,可以使用pivot_table()
来处理重复值。
10. pd.pivot_table()
– 聚合透视表
pivot_table()
是 pivot()
的扩展版,它可以处理重复值,并允许对数据进行聚合操作(如求和、均值等)。它通常用于对数据进行分组聚合后生成表格形式的结果。
关键参数
index
: 设置透视表的行标签。columns
: 设置透视表的列标签。aggfunc
: 用于聚合的函数(如sum
、mean
等),默认为mean
。
Python案例
# 创建一个长格式的 DataFrame
df = pd.DataFrame({
'ID': ['ID1', 'ID2', 'ID1', 'ID2', 'ID1'],
'Subject': ['Math', 'Math', 'Physics', 'Physics', 'Math'],
'Score': [85, 90, 88, 93, 87]
})
# 构建透视表并计算均值
result = pd.pivot_table(df, index='ID', columns='Subject', values='Score', aggfunc='mean')
print(result)
输出:
Subject Math Physics
ID
ID1 86.0 88.0
ID2 90.0 93.0
常见应用场景
数据聚合: pivot_table()
可以对数据进行多维度分组聚合,并灵活选择聚合函数,适合生成汇总报告或透视表。
今天举例的这些是 Pandas 中常用的十个数据合并与连接操作,每一个都可以根据具体需求灵活应用,结合高级用法可以处理复杂的数据操作场景。希望有这个资料,大家可以灵活使用~
下面是一个涉及到数据合并与连接操作的 Pandas 时间序列处理案例,展示了时间序列数据的生成、处理、以及数据的可视化。该案例使用虚拟数据集,并生成两个图像:一个是时间序列的折线图,另一个是滚动均值和标准差的图表,用于分析时间序列数据的波动和趋势。
案例背景
假设我们有一家电商平台的销售数据,记录了过去 100 天的每日销售额。我们希望:
分析每日销售额的变化趋势。 计算滚动平均和滚动标准差,并与原始数据一起进行可视化分析,查看销售额是否有显著波动。
完整代码
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 设置随机数种子,确保每次生成相同的随机数据
np.random.seed(42)
# 生成日期范围
date_rng = pd.date_range(start='2023-01-01', end='2024-10-01', freq='D')
# 生成随机销售额数据
sales = np.random.randint(1000, 5000, size=len(date_rng))
# 创建时间序列DataFrame
df = pd.DataFrame(date_rng, columns=['date'])
df['sales'] = sales
# 设置日期为索引
df.set_index('date', inplace=True)
# 计算滚动平均和滚动标准差 (窗口大小为7天)
df['rolling_mean'] = df['sales'].rolling(window=7).mean()
df['rolling_std'] = df['sales'].rolling(window=7).std()
# 创建一个图,包含2个子图
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8), sharex=True)
# 图1: 销售额的时间序列图
ax1.plot(df.index, df['sales'], color='cyan', label='Daily Sales')
ax1.plot(df.index, df['rolling_mean'], color='magenta', linestyle='--', label='7-day Rolling Mean')
ax1.set_title('Daily Sales and 7-Day Rolling Mean', fontsize=14)
ax1.set_ylabel('Sales ($)')
ax1.legend(loc='upper left')
ax1.grid(True)
# 图2: 滚动标准差与原始数据的对比
ax2.plot(df.index, df['sales'], color='cyan', label='Daily Sales')
ax2.plot(df.index, df['rolling_std'], color='orange', linestyle='--', label='7-day Rolling Std Dev')
ax2.set_title('Sales and 7-Day Rolling Standard Deviation', fontsize=14)
ax2.set_xlabel('Date')
ax2.set_ylabel('Sales / Std Dev')
ax2.legend(loc='upper left')
ax2.grid(True)
# 设置整个图的布局和显示
plt.tight_layout()
plt.show()
数据生成: 使用
numpy
生成一个长度为 100 的随机销售额数据(1000-5000之间),并将其作为时间序列。时间序列处理: 计算了 7 天的滚动平均和滚动标准差,滚动平均用于平滑销售额的变化趋势,而滚动标准差用于衡量波动性。
第一个图显示了每日销售额和 7 天滚动平均线。我们可以观察到销售额的波动以及滚动平均如何平滑了这些波动,帮助我们看清趋势。可以看到,原始的销售额数据波动很大,滚动平均线平滑了这些波动,使得整体趋势更加明显。 第二个图显示了每日销售额和 7 天滚动标准差。滚动标准差显示了销售额在不同时间段的波动性,帮助我们识别某些时段的异常波动或稳定期。可以判断哪些时间段波动较大,哪些时间段较为平稳。如果标准差曲线较高,则说明在这段时间内销售额波动较大;标准差较低则说明销售额较为稳定。
最后