【基本工具】S02E14 Pandas数据合并中的关系代数和集合操作

0.本集概览

1.DataFrame数据连接中的一对一连接、多对一连接和多对多连接
2.指定DataFrame合并列的方法
3.DataFrame对象合并列中存在索引列的处理方法
4.DataFrame对象连接过程中的内连接、外连接、左连接和右连接
5.重复列名问题的处理

DataFrame是一种表格型的数据,他的每一个数据行表征着具备各列属性值的一个实体对象,类似关系型数据,而关系代数就是处理这类关系数据的方法基础。

这一集我们首先就来探讨数据连接的关系代数:一对一连接、多对一连接和多对多连接。

1.DataFrame数据连接的三种关系代数

1.1.一对一连接:

一对一连接是最简单的数据合并类型,他指的是两个待连接的DataFrame数据对象有一个完全相同的属性列,并自动以这一列作为键进行连接,生成一个新的DataFrame数据对象,这样就能合并之前数据里的其他不同属性,通过merge方法即可实现:
代码片段:

import pandas as pd

df1 = pd.DataFrame({'employee': ['Bob', 'Jake', 'Lisa', 'Sue'],
                    'group': ['MGR', 'R&D', 'HR', 'R&D']})
df2 = pd.DataFrame({'employee': ['Bob', 'Jake', 'Sue', 'Lisa'],
                    'hire_date': [2004, 2009, 2013, 2010]})
print(df1)
print(df2)
df3 = pd.merge(df1, df2)
print(df3)

运行结果:

  employee group
0      Bob   MGR
1     Jake   R&D
2     Lisa    HR
3      Sue   R&D

  employee  hire_date
0      Bob       2004
1     Jake       2009
2      Sue       2013
3     Lisa       2010

  employee group  hire_date
0      Bob   MGR       2004
1     Jake   R&D       2009
2     Lisa    HR       2010
3      Sue   R&D       2013

从这个例子中我们看到,df1和df2分别表示了雇员的部门信息和入职时间,正是因为他们在employee属性列中拥有完全相同的人名信息,因此就可以实现我们上述的一对一连接。

1.2.多对一连接

和一对一的属性列强调完全一致不同,多对一连接中,需要连接的两个列里,有一个列的值有重复,我们接着利用上面的df3数据进行演示:
代码片段:

df4 = pd.DataFrame({'group': ['MGR', 'R&D', 'HR'],
                    'supervisor': ['Bill', 'Tom', 'Bob']})
print(df3)
print(df4)
df5 = pd.merge(df3, df4)
print(df5)

运行结果:

  employee group  hire_date
0      Bob   MGR       2004
1     Jake   R&D       2009
2     Lisa    HR       2010
3      Sue   R&D       2013

  group supervisor
0   MGR       Bill
1   R&D        Tom
2    HR        Bob

  employee group  hire_date supervisor
0      Bob   MGR       2004       Bill
1     Jake   R&D       2009        Tom
2      Sue   R&D       2013        Tom
3     Lisa    HR       2010        Bob

我们可以看出,df3和df4是针对group列进行合并,因为df3中的group有重复项R&D,因此是多对一连接。

1.3.多对多连接

按照上面的逻辑进行思考,如果两个DataFrame的共同列都包含重复值,那么这种连接方式就是多对多连接。我们接着引用上面的df1进行操作举例:
代码片段:

df6 = pd.DataFrame({'group':['MGR', 'R&D', 'R&D','HR','HR'],
                    'skill':['management','CS','math','office','english']})
df7 = pd.merge(df1, df6)
print(df1)
print(df6)
print(df7)

运行结果:

  employee group
0      Bob   MGR
1     Jake   R&D
2     Lisa    HR
3      Sue   R&D

  group       skill
0   MGR  management
1   R&D          CS
2   R&D        math
3    HR      office
4    HR     english

  employee group       skill
0      Bob   MGR  management
1     Jake   R&D          CS
2     Jake   R&D        math
3      Sue   R&D          CS
4      Sue   R&D        math
5     Lisa    HR      office
6     Lisa    HR     english

2.指定DataFrame合并列

以上的举例中,数据都十分规整,可以说是数据连接操作中的理想情况,但是实际情况下,可能会出现一些额外需要处理的工作。

第一种情况是,如果两个待连接的DataFrame对象的合并列名称不一样如何处理?

因为合并列的名称不一样,所以默认条件下merge方法就不知道该合并谁,因此就需要显式的人为指定两边的合并列,我们还是举上面那个雇员的例子,只不过把df2的列名employee改为name。
代码片段:

import pandas as pd

df1 = pd.DataFrame({'employee': ['Bob', 'Jake', 'Lisa', 'Sue'],
                    'group': ['MGR', 'R&D', 'HR', 'R&D']})
df2 = pd.DataFrame({'name': ['Bob', 'Jake', 'Sue', 'Lisa'],
                    'hire_date': [2004, 2009, 2013, 2010]})
print(df1)
print(df2)
df3 = pd.merge(df1, df2, left_on='employee', right_on='name')
print(df3)

运行结果:

  employee group
0      Bob   MGR
1     Jake   R&D
2     Lisa    HR
3      Sue   R&D

   hire_date  name
0       2004   Bob
1       2009  Jake
2       2013   Sue
3       2010  Lisa

  employee group  hire_date  name
0      Bob   MGR       2004   Bob
1     Jake   R&D       2009  Jake
2     Lisa    HR       2010  Lisa
3      Sue   R&D       2013   Sue

结果是正确的,不过有一点点瑕疵,就是合并的两个一模一样的列都同时被保留了下来,我们需要去掉其中一个:
代码片段:

df3 = pd.merge(df1, df2, left_on='employee', right_on='name').drop('name',axis=1)
print(df3)

运行结果:

  employee group  hire_date
0      Bob   MGR       2004
1     Jake   R&D       2009
2     Lisa    HR       2010
3      Sue   R&D       2013

3.合并索引列的方法

如果我们不是合并数据列,而是合并索引列,需要什么特别注意的吗?

大体上没什么区别,只不过需要额外设置两个参数:left_index和right_index.
代码片段:

import pandas as pd

df1 = pd.DataFrame({'employee': ['Bob', 'Jake', 'Lisa', 'Sue'],
                    'group': ['MGR', 'R&D', 'HR', 'R&D']})
df2 = pd.DataFrame({'name': ['Bob', 'Jake', 'Sue', 'Lisa'],
                    'hire_date': [2004, 2009, 2013, 2010]})

df1_a = df1.set_index('employee')
df2_a = df2.set_index('name')
df3 = pd.merge(df1_a, df2_a, left_index=True, right_index=True)
print(df1_a)
print(df2_a)
print(df3)

运行结果:

         group
employee      
Bob        MGR
Jake       R&D
Lisa        HR
Sue        R&D

      hire_date
name           
Bob        2004
Jake       2009
Sue        2013
Lisa       2010

     group  hire_date
Bob    MGR       2004
Jake   R&D       2009
Sue    R&D       2013
Lisa    HR       2010

这种情形下有一种更简便的替代方法,就是使用join方法来专门进行索引列的合并。
代码片段:

top Created with Sketch.