合并与连接
合并与连接
import pandas as pd
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 完全一致。
pd.concat([left, right])
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 |
left.join(right)
A | B | C | D | |
---|---|---|---|---|
K0 | A0 | B0 | C0 | D0 |
K1 | A1 | B1 | NaN | NaN |
K2 | A2 | B2 | C2 | D2 |
right.join(left)
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 |