深入理解 pandas.concat:数据连接的利器
在数据分析和处理过程中,将多个数据集有效地合并或连接是一个非常常见的任务。Pandas 库提供了多种强大的工具来完成这项工作,其中 pandas.concat 是一个非常灵活且常用的函数。与用于数据库风格连接(基于共同列的值)的 merge 和 join 不同,concat 函数主要用于沿着某个轴(行或列)将多个 Pandas 对象(Series 或 DataFrame)堆叠或并排放置。
本文将详细介绍 pandas.concat 的使用方法、核心参数及其应用示例,帮助你掌握如何高效地使用它来整合你的数据。
什么是 pandas.concat?
pandas.concat 函数用于在 Pandas 中进行串联(Concatenation)操作。它的基本思想是沿着一个轴将多个 DataFrame 或 Series 对象连接起来。这个过程就像是将积木一块一块地拼接起来。
- 连接对象: 可以连接一个或多个 Series 或 DataFrame 对象。
- 连接轴: 可以沿着行的方向(
axis=0,默认)或列的方向(axis=1)进行连接。 - 索引/列处理:
concat在连接时会根据非连接轴上的标签(索引或列名)进行对齐。如果标签不完全匹配,默认情况下会保留所有标签,并在不匹配的位置用缺失值(NaN)填充。
pandas.concat 的基本语法
pandas.concat 函数的基本语法如下:
python
pandas.concat(objs, axis=0, join='outer', ignore_index=False, keys=None,
levels=None, names=None, verify_integrity=False, sort=False, copy=True)
其中,最常用的参数是:
objs: 这是一个列表或字典,包含要进行连接的 Pandas 对象(Series 或 DataFrame)。axis: 指定连接的轴。0表示按行连接(默认),1表示按列连接。join: 指定如何处理非连接轴上的标签。'outer'表示取并集(默认),'inner'表示取交集。ignore_index: 布尔值,如果设置为True,连接后的结果将生成一个新的从 0 到 n-1 的整数索引,而忽略原始对象的索引。keys: 用于创建分层索引(MultiIndex)。可以传入一个列表,为每个原始对象指定一个键,这些键将成为结果 DataFrame 或 Series 的外层索引。names: 为使用keys创建的分层索引指定层级名称。verify_integrity: 布尔值,如果设置为True,连接后如果结果中出现重复的索引,将引发错误。
接下来,我们将通过具体的示例来演示这些参数的使用。
按行连接 (axis=0)
这是 concat 的默认行为,它将多个 DataFrame 或 Series 沿着行的方向堆叠起来。这要求要连接的对象具有相同的列,或者我们愿意接受因列不匹配而产生的缺失值。
示例 1:连接具有相同列的 DataFrames
假设我们有两个 DataFrame,代表不同时间段收集的相同类型的数据:
“`python
import pandas as pd
数据框 1
df1 = pd.DataFrame({
‘A’: [‘A0’, ‘A1’, ‘A2’, ‘A3’],
‘B’: [‘B0’, ‘B1’, ‘B2’, ‘B3’],
‘C’: [‘C0’, ‘C1’, ‘C2’, ‘C3’],
‘D’: [‘D0’, ‘D1’, ‘D2’, ‘D3’]},
index=[0, 1, 2, 3])
数据框 2
df2 = pd.DataFrame({
‘A’: [‘A4’, ‘A5’, ‘A6’, ‘A7’],
‘B’: [‘B4’, ‘B5’, ‘B6’, ‘B7’],
‘C’: [‘C4’, ‘C5’, ‘C6’, ‘C7’],
‘D’: [‘D4’, ‘D5’, ‘D6’, ‘D7’]},
index=[4, 5, 6, 7])
print(“df1:”)
print(df1)
print(“\ndf2:”)
print(df2)
按行连接
result_concat_rows = pd.concat([df1, df2])
print(“\n按行连接 (axis=0) 结果:”)
print(result_concat_rows)
“`
输出:
“`
df1:
A B C D
0 A0 B0 C0 D0
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
df2:
A B C D
4 A4 B4 C4 D4
5 A5 B5 C5 D5
6 A6 B6 C6 D6
7 A7 B7 C7 D7
按行连接 (axis=0) 结果:
A B C D
0 A0 B0 C0 D0
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
4 A4 B4 C4 D4
5 A5 B5 C5 D5
6 A6 B6 C6 D6
7 A7 B7 C7 D7
“`
可以看到,df1 和 df2 的行被简单地堆叠在一起,保留了原始的索引。
示例 2:连接具有不同列的 DataFrames (join='outer')
如果 DataFrames 的列不完全相同,concat 在 axis=0 的默认行为 (join='outer') 会取所有列的并集,并在缺失的位置填充 NaN。
“`python
数据框 3 (列比 df1 少)
df3 = pd.DataFrame({
‘A’: [‘A8’, ‘A9’],
‘B’: [‘B8’, ‘B9’]},
index=[8, 9])
数据框 4 (列比 df1 多)
df4 = pd.DataFrame({
‘C’: [‘C10’, ‘C11’],
‘D’: [‘D10’, ‘D11’],
‘E’: [‘E10’, ‘E11’]},
index=[10, 11])
print(“df3:”)
print(df3)
print(“\ndf4:”)
print(df4)
按行连接 df1, df3, df4
result_concat_diff_cols_outer = pd.concat([df1, df3, df4], axis=0, join=’outer’)
print(“\n按行连接 (axis=0, join=’outer’) 具有不同列的数据框结果:”)
print(result_concat_diff_cols_outer)
“`
输出:
“`
df3:
A B
8 A8 B8
9 A9 B9
df4:
C D E
10 C10 D10 E10
11 C11 D11 E11
按行连接 (axis=0, join=’outer’) 具有不同列的数据框结果:
A B C D E
0 A0 B0 C0 D0 NaN
1 A1 B1 C1 D1 NaN
2 A2 B2 C2 D2 NaN
3 A3 B3 C3 D3 NaN
8 A8 B8 NaN NaN NaN
9 A9 B9 NaN NaN NaN
10 NaN NaN C10 D10 E10
11 NaN NaN C11 D11 E11
“`
结果包含了所有原始 DataFrame 的列 (A, B, C, D, E),不匹配的位置用 NaN 填充。
示例 3:连接具有不同列的 DataFrames (join='inner')
如果希望结果只包含所有原始 DataFrame 都有的列,可以使用 join='inner'。
“`python
使用 df1, df3, df4 进行 inner 连接
result_concat_diff_cols_inner = pd.concat([df1, df3, df4], axis=0, join=’inner’)
print(“\n按行连接 (axis=0, join=’inner’) 具有不同列的数据框结果:”)
print(result_concat_diff_cols_inner)
“`
输出:
按行连接 (axis=0, join='inner') 具有不同列的数据框结果:
Empty DataFrame
Columns: []
Index: [0, 1, 2, 3, 8, 9, 10, 11]
在这个例子中,df1 有 [‘A’, ‘B’, ‘C’, ‘D’],df3 有 [‘A’, ‘B’],df4 有 [‘C’, ‘D’, ‘E’]。它们之间没有共同的列,所以 inner 连接的结果是一个空的 DataFrame。
如果我们连接 df1 和 df3 (join='inner'),它们共同的列是 ‘A’ 和 ‘B’:
“`python
result_concat_df1_df3_inner = pd.concat([df1, df3], axis=0, join=’inner’)
print(“\n按行连接 (axis=0, join=’inner’) df1 和 df3:”)
print(result_concat_df1_df3_inner)
“`
输出:
按行连接 (axis=0, join='inner') df1 和 df3:
A B
0 A0 B0
1 A1 B1
2 A2 B2
3 A3 B3
8 A8 B8
9 A9 B9
这次结果只包含列 ‘A’ 和 ‘B’。
按列连接 (axis=1)
当 axis=1 时,concat 将多个 DataFrame 或 Series 沿着列的方向并排放置。这要求要连接的对象具有相同的索引,或者我们愿意接受因索引不匹配而产生的缺失值。
示例 4:连接具有相同索引的 DataFrames
假设我们有两个 DataFrame,它们描述了同一组对象的不同属性:
“`python
数据框 5
df5 = pd.DataFrame({
‘E’: [‘E0’, ‘E1’, ‘E2’, ‘E3’],
‘F’: [‘F0’, ‘F1’, ‘F2’, ‘F3’]},
index=[0, 1, 2, 3])
数据框 6
df6 = pd.DataFrame({
‘G’: [‘G0’, ‘G1’, ‘G2’, ‘G3’],
‘H’: [‘H0’, ‘H1’, ‘H2’, ‘H3’]},
index=[0, 1, 2, 3])
print(“df5:”)
print(df5)
print(“\ndf6:”)
print(df6)
按列连接 df5 和 df6
result_concat_cols = pd.concat([df5, df6], axis=1)
print(“\n按列连接 (axis=1) 结果:”)
print(result_concat_cols)
“`
输出:
“`
df5:
E F
0 E0 F0
1 E1 F1
2 E2 F2
3 E3 F3
df6:
G H
0 G0 H0
1 G1 H1
2 G2 H2
3 G3 H3
按列连接 (axis=1) 结果:
E F G H
0 E0 F0 G0 H0
1 E1 F1 G1 H1
2 E2 F2 G2 H2
3 E3 F3 G3 H3
“`
可以看到,df5 和 df6 的列被并排放置,它们通过共享的索引进行了对齐。
示例 5:连接具有不同索引的 DataFrames (join='outer')
如果 DataFrames 的索引不完全相同,concat 在 axis=1 的默认行为 (join='outer') 会取所有索引的并集,并在缺失的位置填充 NaN。
“`python
数据框 7 (索引与 df5 部分重叠)
df7 = pd.DataFrame({
‘I’: [‘I2’, ‘I3’, ‘I4’, ‘I5’]},
index=[2, 3, 4, 5])
print(“df5:”)
print(df5)
print(“\ndf7:”)
print(df7)
按列连接 df5 和 df7
result_concat_diff_index_outer = pd.concat([df5, df7], axis=1, join=’outer’)
print(“\n按列连接 (axis=1, join=’outer’) 具有不同索引的数据框结果:”)
print(result_concat_diff_index_outer)
“`
输出:
“`
df5:
E F
0 E0 F0
1 E1 F1
2 E2 F2
3 E3 F3
df7:
I
2 I2
3 I3
4 I4
5 I5
按列连接 (axis=1, join=’outer’) 具有不同索引的数据框结果:
E F I
0 E0 F0 NaN
1 E1 F1 NaN
2 E2 F2 I2
3 E3 F3 I3
4 NaN NaN I4
5 NaN NaN I5
“`
结果的索引是 df5 和 df7 索引的并集 (0, 1, 2, 3, 4, 5)。在索引不匹配的位置,用 NaN 填充。
示例 6:连接具有不同索引的 DataFrames (join='inner')
如果希望结果只包含所有原始 DataFrame 都有的索引,可以使用 join='inner'。
“`python
使用 df5 和 df7 进行 inner 连接
result_concat_diff_index_inner = pd.concat([df5, df7], axis=1, join=’inner’)
print(“\n按列连接 (axis=1, join=’inner’) 具有不同索引的数据框结果:”)
print(result_concat_diff_index_inner)
“`
输出:
按列连接 (axis=1, join='inner') 具有不同索引的数据框结果:
E F I
2 E2 F2 I2
3 E3 F3 I3
结果的索引是 df5 和 df7 索引的交集 (2, 3)。
重置索引 (ignore_index=True)
当按行连接多个 DataFrame 时,如果原始 DataFrame 的索引没有特殊含义,或者我们不希望保留原始索引,可以使用 ignore_index=True 参数。这将忽略原始索引,并为结果生成一个从 0 开始的新的整数索引。
示例 7:使用 ignore_index=True
“`python
print(“df1:”)
print(df1)
print(“\ndf2:”)
print(df2)
按行连接并忽略索引
result_concat_ignore_index = pd.concat([df1, df2], ignore_index=True)
print(“\n按行连接并忽略索引 (ignore_index=True) 结果:”)
print(result_concat_ignore_index)
“`
输出:
“`
df1:
A B C D
0 A0 B0 C0 D0
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
df2:
A B C D
4 A4 B4 C4 D4
5 A5 B5 C6 B5
6 A6 B6 C6 D6
7 A7 B7 C7 D7
按行连接并忽略索引 (ignore_index=True) 结果:
A B C D
0 A0 B0 C0 D0
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
4 A4 B4 C4 D4
5 A5 B5 C6 B5
6 A6 B6 C6 D6
7 A7 B7 C7 D7
“`
与示例 1 对比,可以看到这次结果的索引是从 0 到 7 的连续整数。
使用 keys 创建分层索引
有时候,我们希望在连接后能够区分出每一行或每一列来自哪个原始对象。keys 参数可以帮助我们实现这一点,它会在连接结果中创建一个分层索引(MultiIndex)。
示例 8:使用 keys 进行行连接
“`python
print(“df1:”)
print(df1)
print(“\ndf2:”)
print(df2)
使用 keys 进行行连接
result_concat_keys_rows = pd.concat([df1, df2], keys=[‘source_df1’, ‘source_df2’])
print(“\n使用 keys 进行行连接结果:”)
print(result_concat_keys_rows)
“`
输出:
“`
df1:
A B C D
0 A0 B0 C0 D0
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
df2:
A B C D
4 A4 B4 C4 D4
5 A5 B5 C6 B5
6 A6 B6 C6 D6
7 A7 B7 C7 D7
使用 keys 进行行连接结果:
A B C D
source_df1 0 A0 B0 C0 D0
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
source_df2 4 A4 B4 C4 D4
5 A5 B5 C6 B5
6 A6 B6 C6 D6
7 A7 B7 C7 D7
“`
结果 DataFrame 有一个 MultiIndex 作为行索引。外层索引是我们在 keys 参数中指定的键 (‘source_df1’, ‘source_df2’),内层索引是原始 DataFrame 的索引。这使得我们可以方便地按来源选择数据,例如 result_concat_keys_rows.loc['source_df1'] 将返回 df1 的数据。
示例 9:使用 keys 进行列连接
当 axis=1 时使用 keys,会在列上创建 MultiIndex。
“`python
print(“df5:”)
print(df5)
print(“\ndf6:”)
print(df6)
使用 keys 进行列连接
result_concat_keys_cols = pd.concat([df5, df6], axis=1, keys=[‘df5_cols’, ‘df6_cols’])
print(“\n使用 keys 进行列连接结果:”)
print(result_concat_keys_cols)
“`
输出:
“`
df5:
E F
0 E0 F0
1 E1 F1
2 E2 F2
3 E3 F3
df6:
G H
0 G0 H0
1 G1 H1
2 G2 H2
3 G3 H3
使用 keys 进行列连接结果:
df5_cols df6_cols
E F G H
0 E0 F0 G0 H0
1 E1 F1 G1 H1
2 E2 F2 G2 H2
3 E3 F3 G3 H3
“`
结果 DataFrame 的列索引是 MultiIndex。外层是指定的键 (‘df5_cols’, ‘df6_cols’),内层是原始 DataFrame 的列名。我们可以通过外层键访问列,例如 result_concat_keys_cols['df5_cols'] 将返回由 df5 的列组成的子 DataFrame。
使用 names 命名分层索引的层级
使用 keys 创建分层索引后,可以使用 names 参数为索引的各个层级指定名称,提高可读性。
示例 10:使用 keys 和 names
“`python
使用 keys 和 names 进行行连接
result_concat_keys_names = pd.concat(
[df1, df2],
keys=[‘source_df1’, ‘source_df2’],
names=[‘Source’, ‘Original_Index’] # 为 MultiIndex 的两个层级命名
)
print(“\n使用 keys 和 names 进行行连接结果:”)
print(result_concat_keys_names)
“`
输出:
使用 keys 和 names 进行行连接结果:
A B C D
Source Original_Index
source_df1 0 A0 B0 C0 D0
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
source_df2 4 A4 B4 C4 D4
5 A5 B5 C6 B5
6 A6 B6 C6 D6
7 A7 B7 C7 D7
现在行索引的两个层级分别有了名称 ‘Source’ 和 ‘Original_Index’。
验证索引的唯一性 (verify_integrity=True)
默认情况下,concat 允许结果中出现重复的索引(如果原始对象有重复索引且你没有使用 ignore_index)。如果需要确保结果索引的唯一性,可以使用 verify_integrity=True。如果连接会导致重复索引,pandas 会抛出 ValueError 异常。
示例 11:使用 verify_integrity=True
首先创建两个具有相同索引的 DataFrame:
“`python
数据框 8 (与 df1 索引部分重复)
df8 = pd.DataFrame({
‘I’: [‘I0’, ‘I1’, ‘I2’]},
index=[0, 1, 2]) # 注意索引与 df1 有重复 0, 1, 2
print(“df1:”)
print(df1)
print(“\ndf8:”)
print(df8)
尝试按行连接 df1 和 df8 (会产生重复索引 0, 1, 2)
try:
pd.concat([df1, df8], axis=0, verify_integrity=True)
except ValueError as e:
print(f”\n使用 verify_integrity=True 导致的错误:\n{e}”)
如果不使用 verify_integrity=True,会成功连接并保留重复索引
result_concat_duplicate_index = pd.concat([df1, df8], axis=0, verify_integrity=False) # False 是默认值
print(“\n不使用 verify_integrity=True 连接结果 (包含重复索引):”)
print(result_concat_duplicate_index)
“`
输出:
“`
df1:
A B C D
0 A0 B0 C0 D0
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
df8:
I
0 I0
1 I1
2 I2
使用 verify_integrity=True 导致的错误:
Indexes have overlapping values: Int64Index([0, 1, 2], dtype=’int64′)
不使用 verify_integrity=True 连接结果 (包含重复索引):
A B C D I
0 A0 B0 C0 D0 NaN
1 A1 B1 C1 D1 NaN
2 A2 B2 C2 D2 NaN
3 A3 B3 C3 D3 NaN
0 NaN NaN NaN NaN I0
1 NaN NaN NaN NaN I1
2 NaN NaN NaN NaN I2
“`
可以看到,当 verify_integrity=True 且连接结果会产生重复索引时,concat 会抛出错误,这有助于在数据处理早期发现潜在的问题。
连接 Series 对象
concat 也可以用于连接 Series 对象。连接 Series 的行为与连接 DataFrames 类似。
示例 12:连接 Series
“`python
Series 1
s1 = pd.Series([‘s1a’, ‘s1b’, ‘s1c’], index=[1, 2, 3])
Series 2
s2 = pd.Series([‘s2a’, ‘s2b’, ‘s2c’, ‘s2d’], index=[3, 4, 5, 6])
print(“s1:”)
print(s1)
print(“\ns2:”)
print(s2)
按行连接 Series (axis=0, 默认)
result_concat_series_rows = pd.concat([s1, s2])
print(“\n按行连接 Series (axis=0) 结果:”)
print(result_concat_series_rows)
按列连接 Series (axis=1)
结果会是一个 DataFrame,Series 的索引成为 DataFrame 的索引
Series 的名称(如果设置了)或默认整数成为 DataFrame 的列名
result_concat_series_cols = pd.concat([s1, s2], axis=1)
print(“\n按列连接 Series (axis=1) 结果:”)
print(result_concat_series_cols)
给 Series 命名后再按列连接
s1.name = ‘Series_1’
s2.name = ‘Series_2’
result_concat_series_cols_named = pd.concat([s1, s2], axis=1)
print(“\n按列连接命名后的 Series (axis=1) 结果:”)
print(result_concat_series_cols_named)
“`
输出:
“`
s1:
1 s1a
2 s1b
3 s1c
dtype: object
s2:
3 s2a
4 s2b
5 s2c
6 s2d
dtype: object
按行连接 Series (axis=0) 结果:
1 s1a
2 s1b
3 s1c
3 s2a
4 s2b
5 s2c
6 s2d
dtype: object
按列连接 Series (axis=1) 结果:
0 1
1 s1a NaN
2 s1b NaN
3 s1c s2a
4 NaN s2b
5 NaN s2c
6 NaN s2d
按列连接命名后的 Series (axis=1) 结果:
Series_1 Series_2
1 s1a NaN
2 s1b NaN
3 s1c s2a
4 NaN s2b
5 NaN s2c
6 NaN s2d
“`
按行连接 Series 结果是一个 Series,如果原始 Series 有重叠索引,结果 Series 的索引也会重复。按列连接 Series 结果是一个 DataFrame,通过索引对齐,列名是原始 Series 的名称或默认的整数。
连接 DataFrame 和 Series
concat 也可以混合连接 DataFrame 和 Series。
示例 13:按列将 Series 添加为 DataFrame 的新列
这是将 Series 添加为现有 DataFrame 新列的常用方法,通过索引进行对齐。
“`python
DataFrame df1
print(“df1:”)
print(df1)
Series s3 (索引与 df1 部分重叠)
s3 = pd.Series([‘S3_0’, ‘S3_2’, ‘S3_4′], index=[0, 2, 4], name=’New_Column’)
print(“\ns3:”)
print(s3)
按列连接 df1 和 s3
df1 会被视为一个包含单列(Series s3)的 DataFrame
result_concat_df_series_cols = pd.concat([df1, s3], axis=1)
print(“\n按列连接 DataFrame 和 Series (axis=1) 结果:”)
print(result_concat_df_series_cols)
“`
输出:
“`
df1:
A B C D
0 A0 B0 C0 D0
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
s3:
0 S3_0
2 S3_2
4 S3_4
Name: New_Column, dtype: object
按列连接 DataFrame 和 Series (axis=1) 结果:
A B C D New_Column
0 A0 B0 C0 D0 S3_0
1 A1 B1 C1 D1 NaN
2 A2 B2 C2 D2 S3_2
3 A3 B3 C3 D3 NaN
4 NaN NaN NaN NaN S3_4
“`
结果 DataFrame 包含了 df1 的所有列以及 s3 作为一个新列 (‘New_Column’)。通过索引进行对齐,不匹配的索引位置填充 NaN。
concat vs merge/join
简要区分一下 concat 和 merge/join:
pandas.concat: 主要用于沿着一个轴(行或列)将多个对象堆叠或并排放置。它基于 轴标签(索引或列名) 进行对齐。当你想简单地将几个数据表摞起来(即使列不完全一样)或者把几个数据表按行或列拼在一起时,concat是首选。pandas.merge/pandas.DataFrame.join: 主要用于将两个 DataFrame 基于 一个或多个共同列的值 进行合并,类似于数据库的 JOIN 操作。当你想根据某个关联字段将两个独立的数据集合并成一个更宽的表时,应该使用merge或join。
总结
pandas.concat 是一个强大且灵活的函数,用于将多个 Pandas 对象沿指定轴进行连接。
- 通过
axis参数控制连接方向:axis=0(默认) 按行堆叠,axis=1按列并置。 - 通过
join参数控制非连接轴的对齐方式:'outer'(默认) 取并集,'inner'取交集。 ignore_index=True可以方便地重置结果的索引。keys参数可以创建分层索引,用于标识数据的来源。verify_integrity=True可以帮助检查连接结果是否存在重复索引。
熟练掌握 pandas.concat 的各种用法,将极大地提高你在数据处理中的效率。希望本文的详细介绍和示例能帮助你更好地理解和应用这个重要的 Pandas 函数。