资讯详情

【Python数据科学】第三章 Pandas 数据处理(下半章)

7.合并数据集:合并与连接

Pandas 高性能内存数据连接的基本特征之一是(join)和合并(merge)操作。如果您有使用数据库的经验,您必须熟悉此类操作。Pandas 的主接口是pd.merge函数,让我们通过一些例子来介绍它的用法。

7.1、关系代数

pd.merge()基于实现的功能关系代数(relational algebra)部分。关系代数是处理关系数据的一般理论,绝大多数数据库的可用操作都以此为基础。关系代数方法论的强大之处在于,。在数据库或程序中高效实现的基本操作规范的帮助下,您可以完成许多非常复杂的操作。

Pandas 在pd.merge()函数与 Series 和 DataFrame 的join()这些基本现了这些基本操作规则。以下是如何用这些简单的规则连接不同数据源的数据。

import pandas as pd import numpy as np  class display(object):     template = """<div style="float: left; padding: 10px;"> <p style='font-family:"Courier New", Courier, monospace'>{0}</p>{1} </div>"""     def __init__(self, *args):         self.args = args              def _repr_html_(self):         return '\n'.join(self.template.format(a, eval(a)._repr_html_())                          for a in self.args)          def __repr__(self):         return '\n\n'.join(a   '\n'   repr(eval(a))                            for a in self.args) 

7.2.数据连接类型

pd.merge()函数实现了三种数据连接类型:一对一多对一多对多

7.2.一对一连接

与之前介绍的按列合并非常相似,如下面的例子所示,有两个包含同一公司员工的不同信息 DataFrame:

df1 = pd.DataFrame({ 
        '员工': ['Bob', 'Jake', 'Lisa', 'Sue']
       
        , 
        '部门'
        : 
        [
        '会计'
        , 
        '工程'
        , 
        '工程'
        , 
        '招聘'
        ]
        }
        ) df2 
        = pd
        .DataFrame
        (
        { 
         
        '员工'
        : 
        [
        'Lisa'
        , 
        'Bob'
        , 
        'Jake'
        , 
        'Sue'
        ]
        , 
        '入职日期'
        : 
        [
        2004
        , 
        2008
        , 
        2012
        , 
        2014
        ]
        }
        ) display
        (
        'df1'
        , 
        'df2'
        ) 
       

df1

员工 部门
0 Bob 会计
1 Jake 工程
2 Lisa 工程
3 Sue 招聘

df2

员工 入职日期
0 Lisa 2004
1 Bob 2008
2 Jake 2012
3 Sue 2014

若想将这两个 DataFrame 合并成一个 DataFrame,可以用pd.merge()函数实现:

df3 = pd.merge(df1, df2)
df3
员工 部门 入职日期
0 Bob 会计 2008
1 Jake 工程 2012
2 Lisa 工程 2004
3 Sue 招聘 2014

pd.merge()方法会默认丢弃原来的行索引,不过也可以自定义

7.2.2、多对一连接

多对一连接是指,在需要连接的两个列中,有一列的值有重复。通过多对一连接获得的结果 DataFrame 将会保留重复值。请看下面的例子:

df4 = pd.DataFrame({ 
        '部门': ['会计', '工程', '招聘'],
                    '领导': ['Carly', 'Guido', 'Steve']})
display('df3', 'df4', 'pd.merge(df3, df4)')

df3

员工 部门 入职日期
0 Bob 会计 2008
1 Jake 工程 2012
2 Lisa 工程 2004
3 Sue 招聘 2014

df4

部门 领导
0 会计 Carly
1 工程 Guido
2 招聘 Steve

pd.merge(df3, df4)

员工 部门 入职日期 领导
0 Bob 会计 2008 Carly
1 Jake 工程 2012 Guido
2 Lisa 工程 2004 Guido
3 Sue 招聘 2014 Steve

在结果 DataFrame 中多了一个“领导”列,里面会因为输入数据的对应关系而有所重复。

7.2.3、多对多连接

多对多连接是个有点复杂的概念,不过也可以理解。如果左右两个输入的共同列都包含重复值,那么合并的结果就是一种多对多连接。用一个例子来演示更容易理解。来看下面的例子,里面有一个 DataFrame 显示不同岗位员工的一种或多种能力。

通过多对多连接,就可以得知每位员工所具备的能力:

df5 = pd.DataFrame({ 
        '部门': ['会计', '会计', '工程', '工程', '招聘', '招聘'],
                    '技能': ['数学', '制表', '编码', 'Linux', '制表', '管理']})
display('df1', 'df5', "pd.merge(df1, df5)")

df1

员工 部门
0 Bob 会计
1 Jake 工程
2 Lisa 工程
3 Sue 招聘

df5

部门 技能
0 会计 数学
1 会计 制表
2 工程 编码
3 工程 Linux
4 招聘 制表
5 招聘 管理

pd.merge(df1, df5)

员工 部门 技能
0 Bob 会计 数学
1 Bob 会计 制表
2 Jake 工程 编码
3 Jake 工程 Linux
4 Lisa 工程 编码
5 Lisa 工程 Linux
6 Sue 招聘 制表
7 Sue 招聘 管理

这三种数据连接可以直接与其他 Pandas 工具组合使用,从而实现各种各样的功能。但是工作中的真实数据集往往不像示例中演示的那么干净、整洁。下面就来介绍pd.merge()的一些功能,它们可以让你更好的应对数据连接中的问题。

7.3、设置数据合并的键

我们已经知道pd.merge()的默认行为:它会将两个输入的一个或多个共同列作为键进行合并。但是由于两个输入要合并的列通常都不是同名的,因此pd.merge()提供了一些参数处理这个问题。

7.3.1、参数on的用法

最简单的方法就是直接将参数on设置为一个列名称或者一个包含多列名称的列表:

  • 这个参数只有两个 DataFrame 有共同列名的时候才可以使用。
display('df1', 'df2', "pd.merge(df1, df2, on='员工')")

df1

员工 部门
0 Bob 会计
1 Jake 工程
2 Lisa 工程
3 Sue 招聘

df2

员工 入职日期
0 Lisa 2004
1 Bob 2008
2 Jake 2012
3 Sue 2014

pd.merge(df1, df2, on='员工')

员工 部门 入职日期
0 Bob 会计 2008
1 Jake 工程 2012
2 Lisa 工程 2004
3 Sue 招聘 2014

7.3.2、left_onright_on参数

有时候你需要合并连个列名不同的数据集,例如前面的员工信息表中有一个字段不是“员工”而是“姓名”。在这种情况下,就可以用left_onright_on参数来指定列名:

df3 = pd.DataFrame({ 
        '姓名': ['Bob', 'Jake', 'Lisa', 'Sue'],
                    '工资': [70000, 80000, 120000, 90000]})
display('df1', 'df3', 'pd.merge(df1, df3, left_on="员工", right_on="姓名")')

df1

员工 部门
0 Bob 会计
1 Jake 工程
2 Lisa 工程
3 Sue 招聘

df3

姓名 工资
0 Bob 70000
1 Jake 80000
2 Lisa 120000
3 Sue 90000

pd.merge(df1, df3, left_on="员工", right_on="姓名")

员工 部门 姓名 工资
0 Bob 会计 Bob 70000
1 Jake 工程 Jake 80000
2 Lisa 工程 Lisa 120000
3 Sue 招聘 Sue 90000

但是获取的结果中有一个多余的列,可以通过 DataFrame 的drop()方法将这一列去掉:

pd.merge(df1, df3, left_on="员工", right_on="姓名").drop("姓名", axis=1)
员工 部门 工资
0 Bob 会计 70000
1 Jake 工程 80000
2 Lisa 工程 120000
3 Sue 招聘 90000

7.3.3、left_indexright_index参数

除了合并列之外,你可能还需要合并索引:

df1a = df1.set_index('员工')
df2a = df2.set_index('员工')
display('df1a', 'df2a')

df1a

部门
员工
Bob 会计
Jake 工程
Lisa 工程
Sue 招聘

df2a

入职日期
员工
Lisa 2004
Bob 2008
Jake 2012
Sue 2014
# pd.merge(df1a, df2a) 这样合并会报错,报错如下(MergeError:没有要执行合并的通用列)
# MergeError: No common columns to perform merge on. Merge options: left_on=None, right_on=None, left_index=False, right_index=False
display('df1a', 'df2a', "pd.merge(df1a, df2a, left_index=True, right_index=True)")

df1a

部门
员工
Bob 会计
Jake 工程
Lisa 工程
Sue 招聘

df2a

入职日期
员工
Lisa 2004
Bob 2008
Jake 2012
Sue 2014

pd.merge(df1a, df2a, left_index=True, right_index=True)

部门 入职日期
员工
Bob 会计 2008
Jake 工程 2012
Lisa 工程 2004
Sue 招聘 2014

为了方便考虑,DataFrame 实现了join()方法,它可以按照索引进行数据合并:

display('df1a', 'df2a', 'df1a.join(df2a)')

df1a

部门
员工
Bob 会计
Jake 工程
Lisa 工程
Sue 招聘

df2a

入职日期
员工
Lisa 2004
Bob 2008
Jake 2012
Sue 2014

df1a.join(df2a)

部门 入职日期
员工
Bob 会计 2008
Jake 工程 2012
Lisa 工程 2004
Sue 招聘 2014

如果想将索引与列混合使用,那么可以通过结合left_indexright_on,或者结合left_onright_index来实现:

# df1a 的 索引,与 df3 的 “姓名” 列,数据相同
display('df1a', 'df3', "pd.merge(df1a, df3, left_index=True, right_on='姓名')")

df1a

部门
员工
Bob 会计
Jake 工程
Lisa 工程
Sue 招聘

df3

姓名 工资
0 Bob 70000
1 Jake 80000
2 Lisa 120000
3 Sue 90000

pd.merge(df1a, df3, left_index=True, right_on='姓名')

部门 姓名 工资
0 会计 Bob 70000
1 工程 Jake 80000
2 工程 Lisa 120000
3 招聘 Sue 90000

7.4、设置数据连接的集合操作规则

通过前面的演示,我们总结出数据连接的一个重要条件:集合操作规则。

  • 当一个值出现在一列,却没有出现在另一列时,就需要考虑集合操作规则了。
df6 = pd.DataFrame({ 
        '姓名': ['Peter', 'Paul', 'Mary'],
                    '食物': ['鱼', '大豆', '面包']},
                   columns=['姓名', '食物'])
df7 = pd.DataFrame({ 
        '姓名': ['Mary', 'Joseph'],
                    '饮品': ['葡萄酒', '啤酒']},
                   columns=['姓名', '饮品'])
display('df6', 'df7', 'pd.merge(df6, df7)')

df6

姓名 食物
0 Peter
1 Paul 大豆
2 Mary 面包

df7

姓名 饮品
0 Mary 葡萄酒
1 Joseph 啤酒

pd.merge(df6, df7)

姓名 食物 饮品
0 Mary 面包 葡萄酒

我们合并两个数据集,在“姓名”列中只有一个共同值:Mary。默认情况下,结果中只会包含两个输入集合的交集,这种连接方式叫做内连接(inner join)。我们可以用how参数设置连接方式,默认值是'inner'

pd.merge(df6, df7, how='inner')
姓名 食物 饮品
0 Mary 面包 葡萄酒

how参数支持的数据连接方式还有'outer''left''right'外连接(outer join)返回两个输入列的并集,所有缺失值都用NaN填充:

display('df6', 'df7', "pd.merge(df6, df7, how='outer')")

df6

姓名 食物
0 Peter
1 Paul 大豆
2 Mary 面包

df7

姓名 饮品
0 Mary 葡萄酒
1 Joseph 啤酒

pd.merge(df6, df7, how='outer')

姓名 食物 饮品
0 Peter NaN
1 Paul 大豆 NaN
2 Mary 面包 葡萄酒
3 Joseph NaN 啤酒

左连接(left join)和右连接(right join)返回的结果分别只包含左列和右列:

display('df6', 'df7', "pd.merge(df6, df7, how='left')")

df6

姓名 食物
0 Peter
1 Paul 大豆
2 Mary 面包

df7

姓名 饮品
0 Mary 葡萄酒
1 Joseph 啤酒

pd.merge(df6, df7, how='left')

姓名 食物 饮品
0 Peter NaN
1 Paul 大豆 NaN
2 Mary 面包 葡萄酒
display('df6', 'df7', "pd.merge(df6, df7, how='right')")

df6

姓名 食物
0 Peter
1 Paul 大豆
2 Mary 面包

df7

姓名 饮品
0 Mary 葡萄酒
1 Joseph 啤酒

pd.merge(df6, df7, how='right')

姓名 食物 饮品
0 Mary 面包 葡萄酒
1 Joseph NaN 啤酒

7.5、重复列名:suffixes参数

df8 = pd.DataFrame({ 
        '姓名': ['Bob', 'Jake', 'Lisa', 'Sue'],
                    '等级': [1, 2, 3, 4]})
df9 = pd.DataFrame({ 
        '姓名': ['Bob', 'Jake', 'Lisa', 'Sue'],
                    '等级': [3, 1, 4, 2]})
display('df8', 'df9', 'pd.merge(df8, df9, on="姓名")')

df8

姓名 等级
0 Bob 1
1 Jake 2
2 Lisa 3
3 Sue 4

df9

姓名 等级
0 Bob 3
1 Jake 1
2 Lisa 4
3 Sue 2

pd.merge(df8, df9, on="姓名")

姓名 等级_x 等级_y
0 Bob 1 3
1 Jake 2 1
2 Lisa 3 4
3 Sue 4 2

由于输出结果中有两个重复的列名,因此pd.merge()函数会自动为它们增加后缀_x_y,当然也可以用通过suffixes参数自定义后缀:

display('df8', 'df9', 'pd.merge(df8, df9, on="姓名", suffixes=["_L", "_R"])')

df8

姓名 等级
0 Bob 1
1 Jake 2
2 Lisa 3
3 Sue 4

df9

姓名 等级
0 Bob 3
1 Jake 1
2 Lisa 4
3 Sue 2

pd.merge(df8, df9, on="姓名", suffixes=["_L", "_R"])

姓名 等级_L 等级_R
0 Bob 1 3
1 Jake 2 1
2 Lisa 3 4
3 Sue 4 2

suffixes参数同样适用于任何连接方式,即使有三个及三个以上的重复列名时也同样适用。

7.6、案例:美国各州的统计数据

pop = pd.read_csv('data/state-population.csv')   # 人口
areas = pd.read_csv('data/state-areas.csv')      # 地区
abbrevs = pd.read_csv('data/state-abbrevs.csv')  # 地区名称缩写

display('pop.head()', 'areas.head()', 'abbrevs.head()')

pop.head()

state/region ages year population
0 AL under18 2012 1117489.0
1 AL total 2012 4817528.0
2 AL under18 2010 1130966.0
3 AL total 2010 4785570.0
4 AL under18 2011 1125763.0

areas.head()

state area (sq. mi)
0 Alabama 52423
1 Alaska 656425
2 Arizona 114006
3 Arkansas 53182
4 California 163707

abbrevs.head()

state abbreviation
0 Alabama AL
1 Alaska AK
2 Arizona AZ
3 Arkansas AR
4 California CA

有了数据,我们先完成一个比较简单的小指标:美国各州的人口密度排名:

7.6.1、先合并人口和州缩写数据

merged = pd.merge(pop, abbrevs, how='outer', left_on='state/region', right_on="abbreviation"
        标签: amic单点式传感器ross高继电器e12

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台