pandas 合并与连接

import pandas as pd

concat: 合并多个 DataFrame 和 Series

垂直合并

df1 = pd.DataFrame({'Name': ['Sam', 'Emma'], 'Age': [14, 15]})
df2 = pd.DataFrame({'Name': ['Karen', 'Rahul'], 'Age': [10, 13]})
df1

Name Age
0 Sam 14
1 Emma 15
df2

Name Age
0 Karen 10
1 Rahul 13

垂直合并即按列合并,一般需要 DataFrame 的 column 完全一致。

pd.concat([df1, df2])

Name Age
0 Sam 14
1 Emma 15
0 Karen 10
1 Rahul 13

不过,上面的结果仍然保留了这些 DataFrame 原来的索引。我们可以重置索引:

pd.concat([df1, df2]).reset_index(drop=True)

Name Age
0 Sam 14
1 Emma 15
2 Karen 10
3 Rahul 13

水平合并

df1 = pd.DataFrame({'Name': ['Sam', 'Emma'], 'Age': [14, 15]})
df2 = pd.DataFrame({'Math': ['B', 'A+'], 'Science': ['A', 'B+']})
df1

Name Age
0 Sam 14
1 Emma 15
df2

Math Science
0 B A
1 A+ B+

设置 axis=1 即可实现水平合并,即按行合并,一般要求 DataFrame 的 index 完全一致。

pd.concat([df1, df2], 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 上都可以进行。

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],
)
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 = pd.DataFrame(
    {
        "B": ["B2", "B3", "B6", "B7"],
        "D": ["D2", "D3", "D6", "D7"],
        "F": ["F2", "F3", "F6", "F7"],
    },
    index=[2, 3, 6, 7],
)
df2

B D F
2 B2 D2 F2
3 B3 D3 F3
6 B6 D6 F6
7 B7 D7 F7
pd.concat([df1, df2], 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([df1, df2], 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([df1, df2], 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([df1, df2], 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([df1, df2], 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
pd.concat([df1, s], axis=1)

A B C D X
0 A0 B0 C0 D0 X0
1 A1 B1 C1 D1 X1
2 A2 B2 C2 D2 X2
3 A3 B3 C3 D3 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 K1 A3 B3 NaN NaN
5 K2 K0 NaN NaN C3 D3

左连接

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
Previous
Next