最强总结!十大pandas合并与连接操作 !!

文摘   2024-09-29 11:26   北京  

哈喽,我是小白~

今儿和大家再聊聊pandas中的一个非常重要的内容,数据合并与连接操作!~

在机器学习实验中,Pandas数据合并与连接操作 能够有效地整合多个数据源,确保特征和目标变量一致。通过灵活的连接方式(如内连接、外连接等),这些操作能处理数据缺失和冗余信息,提升数据完整性。高效的数据合并能够简化数据预处理流程,为后续模型训练提供高质量的数据基础。

  • pd.concat() – 沿轴连接多个 DataFrame 或 Series
  • pd.merge() – 类似 SQL 的合并操作
  • join() – 基于索引的合并
  • append() – 添加新行到 DataFrame
  • combine_first() – 数据补全
  • update() – 按索引更新 DataFrame
  • reindex() – 重新索引
  • 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': [1None], 'B': [None2]})
df2 = pd.DataFrame({'A': [34], 'B': [56]})

# 使用 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=[12])

# 更新 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([0213])
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': [8590],
    'Physics': [8893],
    'Chemistry': [9285]
})

# 将宽表转换为长表
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': [85908893]
})

# 构建透视表
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: 用于聚合的函数(如 summean 等),默认为 mean

Python案例

# 创建一个长格式的 DataFrame
df = pd.DataFrame({
    'ID': ['ID1''ID2''ID1''ID2''ID1'],
    'Subject': ['Math''Math''Physics''Physics''Math'],
    'Score': [8590889387]
})

# 构建透视表并计算均值
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 天的每日销售额。我们希望:

  1. 分析每日销售额的变化趋势。
  2. 计算滚动平均和滚动标准差,并与原始数据一起进行可视化分析,查看销售额是否有显著波动。

完整代码

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(10005000, 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(21, figsize=(128), 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 天滚动标准差。滚动标准差显示了销售额在不同时间段的波动性,帮助我们识别某些时段的异常波动或稳定期。可以判断哪些时间段波动较大,哪些时间段较为平稳。如果标准差曲线较高,则说明在这段时间内销售额波动较大;标准差较低则说明销售额较为稳定。

最后

以上就是今天所有的内容了。
如果对你来说比较有用,记得点赞、收藏,慢慢学习~
下期会有更多干货等着你!~

Python和机器学习初学者
Python和机器学习分享,只写干货,一起学习~
 最新文章