concat: 合并多个 DataFrame 和 Series
垂直合并
left = pd.DataFrame({'Name': ['Sam', 'Emma'], 'Age': [14, 15]})
left
|
Name |
Age |
0 |
Sam |
14 |
1 |
Emma |
15 |
right = pd.DataFrame({'Name': ['Karen', 'Rahul'], 'Age': [10, 13]})
right
|
Name |
Age |
0 |
Karen |
10 |
1 |
Rahul |
13 |
垂直合并即按列合并,一般需要 DataFrame 的 column 完全一致。
|
Name |
Age |
0 |
Sam |
14 |
1 |
Emma |
15 |
0 |
Karen |
10 |
1 |
Rahul |
13 |
不过,上面的结果仍然保留了这些 DataFrame 原来的索引。我们可以重置索引:
pd.concat([left, right]).reset_index(drop=True)
|
Name |
Age |
0 |
Sam |
14 |
1 |
Emma |
15 |
2 |
Karen |
10 |
3 |
Rahul |
13 |
水平合并
left = pd.DataFrame({'Name': ['Sam', 'Emma'], 'Age': [14, 15]})
left
|
Name |
Age |
0 |
Sam |
14 |
1 |
Emma |
15 |
right = pd.DataFrame({'Math': ['B', 'A+'], 'Science': ['A', 'B+']})
right
|
Math |
Science |
0 |
B |
A |
1 |
A+ |
B+ |
设置 axis=1
即可实现水平合并,即按行合并,一般要求 DataFrame 的 index 完全一致。
pd.concat([left, right], axis=1)
|
Name |
Age |
Math |
Science |
0 |
Sam |
14 |
B |
A |
1 |
Emma |
15 |
A+ |
B+ |
设置合并模式
如果 index 或者 column 不一致,则可以设置连接 join
的方式。
outer
:类似于关系代数的外连接运算,没有的内容补 NaN 值。该选项为默认值。
inner
:类似于关系代数的自然连接运算,先求笛卡尔积中对应相等的元组,再去掉其中重复的属性值。
注:与关系代数不同的是,pandas 对 DataFrame 的连接可以在 index 和 column 上都可以进行。
left = 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],
)
left
|
A |
B |
C |
D |
0 |
A0 |
B0 |
C0 |
D0 |
1 |
A1 |
B1 |
C1 |
D1 |
2 |
A2 |
B2 |
C2 |
D2 |
3 |
A3 |
B3 |
C3 |
D3 |
right = pd.DataFrame(
{
"B": ["B2", "B3", "B6", "B7"],
"D": ["D2", "D3", "D6", "D7"],
"F": ["F2", "F3", "F6", "F7"],
},
index=[2, 3, 6, 7],
)
right
|
B |
D |
F |
2 |
B2 |
D2 |
F2 |
3 |
B3 |
D3 |
F3 |
6 |
B6 |
D6 |
F6 |
7 |
B7 |
D7 |
F7 |
pd.concat([left, right], axis=1, join='outer')
|
A |
B |
C |
D |
B |
D |
F |
0 |
A0 |
B0 |
C0 |
D0 |
NaN |
NaN |
NaN |
1 |
A1 |
B1 |
C1 |
D1 |
NaN |
NaN |
NaN |
2 |
A2 |
B2 |
C2 |
D2 |
B2 |
D2 |
F2 |
3 |
A3 |
B3 |
C3 |
D3 |
B3 |
D3 |
F3 |
6 |
NaN |
NaN |
NaN |
NaN |
B6 |
D6 |
F6 |
7 |
NaN |
NaN |
NaN |
NaN |
B7 |
D7 |
F7 |
pd.concat([left, right], axis=1, join='inner')
|
A |
B |
C |
D |
B |
D |
F |
2 |
A2 |
B2 |
C2 |
D2 |
B2 |
D2 |
F2 |
3 |
A3 |
B3 |
C3 |
D3 |
B3 |
D3 |
F3 |
pd.concat([left, right], axis=0, join='outer')
|
A |
B |
C |
D |
F |
0 |
A0 |
B0 |
C0 |
D0 |
NaN |
1 |
A1 |
B1 |
C1 |
D1 |
NaN |
2 |
A2 |
B2 |
C2 |
D2 |
NaN |
3 |
A3 |
B3 |
C3 |
D3 |
NaN |
2 |
NaN |
B2 |
NaN |
D2 |
F2 |
3 |
NaN |
B3 |
NaN |
D3 |
F3 |
6 |
NaN |
B6 |
NaN |
D6 |
F6 |
7 |
NaN |
B7 |
NaN |
D7 |
F7 |
pd.concat([left, right], axis=0, join='inner')
|
B |
D |
0 |
B0 |
D0 |
1 |
B1 |
D1 |
2 |
B2 |
D2 |
3 |
B3 |
D3 |
2 |
B2 |
D2 |
3 |
B3 |
D3 |
6 |
B6 |
D6 |
7 |
B7 |
D7 |
可以设置忽略 index,index 将按照顺序递增。这样就和关系代数中的连接运算完全一致了。
pd.concat([left, right], ignore_index=True)
|
A |
B |
C |
D |
F |
0 |
A0 |
B0 |
C0 |
D0 |
NaN |
1 |
A1 |
B1 |
C1 |
D1 |
NaN |
2 |
A2 |
B2 |
C2 |
D2 |
NaN |
3 |
A3 |
B3 |
C3 |
D3 |
NaN |
4 |
NaN |
B2 |
NaN |
D2 |
F2 |
5 |
NaN |
B3 |
NaN |
D3 |
F3 |
6 |
NaN |
B6 |
NaN |
D6 |
F6 |
7 |
NaN |
B7 |
NaN |
D7 |
F7 |
DataFrame 和 Series 可以直接合并
s = pd.Series(["X0", "X1", "X2", "X3"], name="X")
s
0 X0
1 X1
2 X2
3 X3
Name: X, dtype: object
df = pd.DataFrame({'Name': ['Sam', 'Emma'], 'Age': [14, 15]})
df
|
Name |
Age |
0 |
Sam |
14 |
1 |
Emma |
15 |
pd.concat([df, s], axis=1)
|
Name |
Age |
X |
0 |
Sam |
14.0 |
X0 |
1 |
Emma |
15.0 |
X1 |
2 |
NaN |
NaN |
X2 |
3 |
NaN |
NaN |
X3 |
merge: 功能更强大的数据库连接操作
left = pd.DataFrame(
{
"key": ["K0", "K1", "K2", "K3"],
"A": ["A0", "A1", "A2", "A3"],
"B": ["B0", "B1", "B2", "B3"],
}
)
left
|
key |
A |
B |
0 |
K0 |
A0 |
B0 |
1 |
K1 |
A1 |
B1 |
2 |
K2 |
A2 |
B2 |
3 |
K3 |
A3 |
B3 |
right = pd.DataFrame(
{
"key": ["K0", "K1", "K2", "K3"],
"C": ["C0", "C1", "C2", "C3"],
"D": ["D0", "D1", "D2", "D3"],
}
)
right
|
key |
C |
D |
0 |
K0 |
C0 |
D0 |
1 |
K1 |
C1 |
D1 |
2 |
K2 |
C2 |
D2 |
3 |
K3 |
C3 |
D3 |
pd.merge(left, right, on='key')
|
key |
A |
B |
C |
D |
0 |
K0 |
A0 |
B0 |
C0 |
D0 |
1 |
K1 |
A1 |
B1 |
C1 |
D1 |
2 |
K2 |
A2 |
B2 |
C2 |
D2 |
3 |
K3 |
A3 |
B3 |
C3 |
D3 |
连接方式
merge
可以实现关系代数当中的除自然连接和外连接更强大的连接操作。因此,它的 how
参数支持如下五种运算:
outer
:外连接
inner
:自然连接
left
:左连接
right
:右连接
cross
:笛卡尔积
这种运算要求左表和右表有交叉点 intersection
存在,因此连接方式默认值为 inner
。
left = pd.DataFrame(
{
"key1": ["K0", "K0", "K1", "K2"],
"key2": ["K0", "K1", "K0", "K1"],
"A": ["A0", "A1", "A2", "A3"],
"B": ["B0", "B1", "B2", "B3"],
}
)
left
|
key1 |
key2 |
A |
B |
0 |
K0 |
K0 |
A0 |
B0 |
1 |
K0 |
K1 |
A1 |
B1 |
2 |
K1 |
K0 |
A2 |
B2 |
3 |
K2 |
K1 |
A3 |
B3 |
right = pd.DataFrame(
{
"key1": ["K0", "K1", "K1", "K2"],
"key2": ["K0", "K0", "K0", "K0"],
"C": ["C0", "C1", "C2", "C3"],
"D": ["D0", "D1", "D2", "D3"],
}
)
right
|
key1 |
key2 |
C |
D |
0 |
K0 |
K0 |
C0 |
D0 |
1 |
K1 |
K0 |
C1 |
D1 |
2 |
K1 |
K0 |
C2 |
D2 |
3 |
K2 |
K0 |
C3 |
D3 |
这个例子是由两个字段组成主码的表的连接操作。
pd.merge(left, right, on=["key1", "key2"])
|
key1 |
key2 |
A |
B |
C |
D |
0 |
K0 |
K0 |
A0 |
B0 |
C0 |
D0 |
1 |
K1 |
K0 |
A2 |
B2 |
C1 |
D1 |
2 |
K1 |
K0 |
A2 |
B2 |
C2 |
D2 |
自然连接
pd.merge(left, right, how="inner", on=["key1", "key2"])
|
key1 |
key2 |
A |
B |
C |
D |
0 |
K0 |
K0 |
A0 |
B0 |
C0 |
D0 |
1 |
K1 |
K0 |
A2 |
B2 |
C1 |
D1 |
2 |
K1 |
K0 |
A2 |
B2 |
C2 |
D2 |
外连接
pd.merge(left, right, how="outer", on=["key1", "key2"])
|
key1 |
key2 |
A |
B |
C |
D |
0 |
K0 |
K0 |
A0 |
B0 |
C0 |
D0 |
1 |
K0 |
K1 |
A1 |
B1 |
NaN |
NaN |
2 |
K1 |
K0 |
A2 |
B2 |
C1 |
D1 |
3 |
K1 |
K0 |
A2 |
B2 |
C2 |
D2 |
4 |
K2 |
K0 |
NaN |
NaN |
C3 |
D3 |
5 |
K2 |
K1 |
A3 |
B3 |
NaN |
NaN |
左连接
pd.merge(left, right, how="left", on=["key1", "key2"])
|
key1 |
key2 |
A |
B |
C |
D |
0 |
K0 |
K0 |
A0 |
B0 |
C0 |
D0 |
1 |
K0 |
K1 |
A1 |
B1 |
NaN |
NaN |
2 |
K1 |
K0 |
A2 |
B2 |
C1 |
D1 |
3 |
K1 |
K0 |
A2 |
B2 |
C2 |
D2 |
4 |
K2 |
K1 |
A3 |
B3 |
NaN |
NaN |
右连接
pd.merge(left, right, how="right", on=["key1", "key2"])
|
key1 |
key2 |
A |
B |
C |
D |
0 |
K0 |
K0 |
A0 |
B0 |
C0 |
D0 |
1 |
K1 |
K0 |
A2 |
B2 |
C1 |
D1 |
2 |
K1 |
K0 |
A2 |
B2 |
C2 |
D2 |
3 |
K2 |
K0 |
NaN |
NaN |
C3 |
D3 |
笛卡尔积
pd.merge(left, right, how="cross")
|
key1_x |
key2_x |
A |
B |
key1_y |
key2_y |
C |
D |
0 |
K0 |
K0 |
A0 |
B0 |
K0 |
K0 |
C0 |
D0 |
1 |
K0 |
K0 |
A0 |
B0 |
K1 |
K0 |
C1 |
D1 |
2 |
K0 |
K0 |
A0 |
B0 |
K1 |
K0 |
C2 |
D2 |
3 |
K0 |
K0 |
A0 |
B0 |
K2 |
K0 |
C3 |
D3 |
4 |
K0 |
K1 |
A1 |
B1 |
K0 |
K0 |
C0 |
D0 |
5 |
K0 |
K1 |
A1 |
B1 |
K1 |
K0 |
C1 |
D1 |
6 |
K0 |
K1 |
A1 |
B1 |
K1 |
K0 |
C2 |
D2 |
7 |
K0 |
K1 |
A1 |
B1 |
K2 |
K0 |
C3 |
D3 |
8 |
K1 |
K0 |
A2 |
B2 |
K0 |
K0 |
C0 |
D0 |
9 |
K1 |
K0 |
A2 |
B2 |
K1 |
K0 |
C1 |
D1 |
10 |
K1 |
K0 |
A2 |
B2 |
K1 |
K0 |
C2 |
D2 |
11 |
K1 |
K0 |
A2 |
B2 |
K2 |
K0 |
C3 |
D3 |
12 |
K2 |
K1 |
A3 |
B3 |
K0 |
K0 |
C0 |
D0 |
13 |
K2 |
K1 |
A3 |
B3 |
K1 |
K0 |
C1 |
D1 |
14 |
K2 |
K1 |
A3 |
B3 |
K1 |
K0 |
C2 |
D2 |
15 |
K2 |
K1 |
A3 |
B3 |
K2 |
K0 |
C3 |
D3 |
join: DataFrame 对象的强大连接操作
left = pd.DataFrame(
{"A": ["A0", "A1", "A2"], "B": ["B0", "B1", "B2"]}, index=["K0", "K1", "K2"]
)
left
|
A |
B |
K0 |
A0 |
B0 |
K1 |
A1 |
B1 |
K2 |
A2 |
B2 |
right = pd.DataFrame(
{"C": ["C0", "C2", "C3"], "D": ["D0", "D2", "D3"]}, index=["K0", "K2", "K3"]
)
right
|
C |
D |
K0 |
C0 |
D0 |
K2 |
C2 |
D2 |
K3 |
C3 |
D3 |
|
A |
B |
C |
D |
K0 |
A0 |
B0 |
C0 |
D0 |
K1 |
A1 |
B1 |
NaN |
NaN |
K2 |
A2 |
B2 |
C2 |
D2 |
|
C |
D |
A |
B |
K0 |
C0 |
D0 |
A0 |
B0 |
K2 |
C2 |
D2 |
A2 |
B2 |
K3 |
C3 |
D3 |
NaN |
NaN |
DataFrame 的 join
默认值为 left
,可以设置为除笛卡尔积外的连接方式。
left.join(right, how='inner')
|
A |
B |
C |
D |
K0 |
A0 |
B0 |
C0 |
D0 |
K2 |
A2 |
B2 |
C2 |
D2 |