Data Whale第20期组队学习 Pandas学习—连接

一、关系型连接

1.1 连接的基本概念

把两张相关的表按照某一个或某一组键连接起来是一种常见操作,例如学生期末考试各个科目的成绩表按照 姓名 和 班级 连接成总的成绩表,又例如对企业员工的各类信息表按照 员工ID号 进行连接汇总。由此可以看出,在关系型连接中, 键 是十分重要的,往往用 on 参数表示。
连接的形式: 在 pandas 中的关系型连接函数 merge 和 join 中提供了 how 参数来代表连接形式,分为左连接 left 、右连接 right 、内连接 inner 、外连接 outer .

pd.merge()实现的功能基于关系代数的一部分。关系代数是处理关系型数据的通用理论,巨大部分数据库的可用操作都以此为理论基础。Pandas的pd.merge()方法与Series和DataFrame的join()方法就是基于这些关系代数对应的基本操作规则。

1、merge()函数的具体参数
用法:

DataFrame1.merge(DataFrame2, how=‘inner’, on=None, left_on=None, right_on=None, left_index=False, right_index=False, sort=False, suffixes=(’_x’, ‘_y’))

2、参数说明

参数说明
how默认为inner,可设为inner/outer/left/right
on根据某个字段进行连接,必须存在于两个DateFrame中(若未同时存在,则需要分别使用left_on和right_on来设置)
left_on左连接,以DataFrame1中用作连接键的列
right_on右连接,以DataFrame2中用作连接键的列
left_index将DataFrame1行索引用作连接键
right_index将DataFrame2行索引用作连接键
sort根据连接键对合并后的数据进行排列,默认为True
suffixes对两个数据集中出现的重复列,新数据集中加上后缀_x,_y进行区别

3、编程实践

import pandas as pd
import numpy as np
DF1=pd.DataFrame({'lkey':['abc','def','baz','abc'],'value':[2,3,4,5]})
DF2=pd.DataFrame({'rkey':['abc','def','qwe','abc'],'value':[6,7,8,9]})
print("DF1=",DF1)
# DF1=   lkey  value
# 0  abc      2
# 1  def      3
# 2  baz      4
# 3  abc      5
print("DF2=",DF2)
# DF2=   rkey  value
# 0  abc      6
# 1  def      7
# 2  qwe      8
# 3  abc      9
# 内连接(Inner)
DF_I=DF1.merge(DF2,left_on='lkey',right_on='rkey')
print("DF_I=",DF_I)
# DF_I=   lkey  value_x rkey  value_y
# 0  abc        2  abc        6
# 1  abc        2  abc        9
# 2  abc        5  abc        6
# 3  abc        5  abc        9
# 4  def        3  def        7
# 右链接(Right)
DF_R=DF1.merge(DF2, left_on='lkey', right_on='rkey',how='right')
print("DF_R=",DF_R)
# DF_R=   lkey  value_x rkey  value_y
# 0  abc      2.0  abc        6
# 1  abc      5.0  abc        6
# 2  abc      2.0  abc        9
# 3  abc      5.0  abc        9
# 4  def      3.0  def        7
# 5  NaN      NaN  qwe        8
# 全链接(Outer)
DF_O=DF1.merge(DF2, left_on='lkey', right_on='rkey', how='outer')
print("DF_O=",DF_O)
# DF_O=   lkey  value_x rkey  value_y
# 0  abc      2.0  abc      6.0
# 1  abc      2.0  abc      9.0
# 2  abc      5.0  abc      6.0
# 3  abc      5.0  abc      9.0
# 4  def      3.0  def      7.0
# 5  baz      4.0  NaN      NaN
# 6  NaN      NaN  qwe      8.0
# 两个表中的列出现了重复的列名,那么可以通过 suffixes 参数指定
df1 = pd.DataFrame({'Name':['San Liu'],'Grade':[79]})
df2 = pd.DataFrame({'Name':['San Liu'],'Grade':[85]})
print("df1.merge(df2, on='Name', how='left', suffixes=['_Chinese','_Math'])=",
      df1.merge(df2, on='Name', how='left', suffixes=['_Chinese','_Math']))
# df1.merge(df2, on='Name', how='left', suffixes=['_Chinese','_Math'])=       Name  Grade_Chinese  Grade_Math
# 0  San Liu             79          85
"""
在某些时候出现重复元素是麻烦的,
例如两位同学来自不同的班级,但是姓名相同,
这种时候就要指定 on 参数为多个列使得正确连接
"""
df3 = pd.DataFrame({'Name':['San Zhang', 'San Zhang'],'Age':[20, 21],'Class':['one', 'two']})
df4 = pd.DataFrame({'Name':['San Zhang', 'San Zhang'],'Gender':['F', 'M'],'Class':['two', 'one']})
print("df3=",df3)
# df3=         Name  Age Class
# 0  San Zhang   20   one
# 1  San Zhang   21   two
print("df4=",df4)
# df4=         Name Gender Class
# 0  San Zhang      F   two
# 1  San Zhang      M   one
print("df3.merge(df4, on=['Name', 'Class'], how='left')=",
      df3.merge(df4, on=['Name', 'Class'], how='left'))
# df3.merge(df4, on=['Name', 'Class'], how='left')=         Name  Age Class Gender
# 0  San Zhang   20   one      M
# 1  San Zhang   21   two      F

1.2 索引连接

索引连接就是把索引当作键,即和值连接本质上没有区别, pandas 中利用 join 函数来处理索引连接,它的参数选择要少于 merge ,除了必须的 on 和 how 之外,可以对重复的列指定左右后缀 lsuffix 和 rsuffix 。其中, on 参数指索引名,单层索引时省略参数表示按照当前索引连接。

join(self, other, on=None, how='left', lsuffix='', rsuffix='',sort=False):
DF3=pd.DataFrame({'G': ['G0', 'G1', 'G2', 'G3'],'H': ['H0', 'H1', 'H2', 'H3'],'key': ['K0', 'K1', 'K0', 'K1']})
DF4=pd.DataFrame({'C': ['C0', 'C1'],'D': ['D0', 'D1']},index=['K0', 'K1'])
res=DF3.join(DF4,on='key')
print("res=",res)
# res=     G   H key   C   D
# 0  G0  H0  K0  C0  D0
# 1  G1  H1  K1  C1  D1
# 2  G2  H2  K0  C0  D0
# 3  G3  H3  K1  C1  D1
"""
一次组合多个dataframe的时候可以传入元素为dataframe的列表或者tuple,一次join多个.
"""
DF5=pd.DataFrame({'v': [5, 19, 29]}, index=['K0', 'K1', 'K2'])
DF6=pd.DataFrame({'v': [17, 18, 91]}, index=['K0', 'K0', 'K3'])
DF7=pd.DataFrame({'v': [7, 8, 9]}, index=['K1', 'K1', 'K2'])
res1=DF5.join([DF6,DF7])
print("res1=",res1)
# res1=     v_x   v_y    v
# K0    5  17.0  NaN
# K0    5  18.0  NaN
# K1   19   NaN  7.0
# K1   19   NaN  8.0
# K2   29   NaN  9.0

二、方向连接

2.1 concat

关系型连接中最重要的参数是 on 和 how , pandas 中 concat 函数可以把两个表或者多个表按照纵向或者横向拼接。
concat方法相当于数据库中的全连接(union all),它不仅可以指定连接的方式(outer join或inner join)还可以指定按照某个轴进行连接。与数据库不同的是,它不会去重,但是可以使用drop_duplicates方法达到去重的效果。
在 concat 中,最常用的参数是 axis, join, keys ,分别表示拼接方向,连接形式,以及在新表中指示来自于哪一张旧表的名字。这里需要特别注意, join 和 keys 与之前提到的 join 函数和键的概念没有任何关系。

在默认状态下的 axis=0 ,表示纵向拼接多个表,常常用于多个样本的拼接;而 axis=1 表示横向拼接多个表,常用于多个字段或特征的拼接。

# 纵向合并各表中人的信息
df5=pd.DataFrame({'Name':['San Zhang','Si Li','Wu Wang'],'Age':[20,30,25]})
df6=pd.DataFrame({'Name':['Ming Xiao','Hong Xiao','Zhang Xiao'], 'Age':[19,18,23]})
print("pd.concat([df5, df6])=",pd.concat([df5, df6]))
# pd.concat([df5, df6])=          Name  Age
# 0   San Zhang   20
# 1       Si Li   30
# 2     Wu Wang   25
# 0   Ming Xiao   19
# 1   Hong Xiao   18
# 2  Zhang Xiao   23
# 横向合并各表中的字段
df7=pd.DataFrame({'Grade':[80,85,96, 90]})
df8=pd.DataFrame({'Gender':['M','K','R' 'F']})
print("pd.concat([df5, df7, df8], 1)=",pd.concat([df5, df7, df8], 1))
# pd.concat([df5, df7, df8], 1)=         Name   Age  Grade Gender
# 0  San Zhang  20.0     80      M
# 1      Si Li  30.0     85      K
# 2    Wu Wang  25.0     96     RF
# 3        NaN   NaN     90    NaN
df9= pd.DataFrame({'Name':['Wu Wang','Fang Xiao'], 'Gender':['M',"L"]})
print("pd.concat([df5, df9])=",pd.concat([df5, df9]))
# pd.concat([df5, df9])=         Name   Age Gender
# 0  San Zhang  20.0    NaN
# 1      Si Li  30.0    NaN
# 2    Wu Wang  25.0    NaN
# 0    Wu Wang   NaN      M
# 1  Fang Xiao   NaN      L
df10=pd.DataFrame({'Grade':[80,85, 90,86]}, index=[1, 2,3,4])
print(" pd.concat([df5, df10], 1)=", pd.concat([df5, df10], 1))
#  pd.concat([df5, df10], 1)=         Name   Age  Grade
# 0  San Zhang  20.0    NaN
# 1      Si Li  30.0   80.0
# 2    Wu Wang  25.0   85.0
# 3        NaN   NaN   90.0
# 4        NaN   NaN   86.0
print("pd.concat([df5, df10], axis=1, join='inner')=",pd.concat([df5, df10], axis=1, join='inner'))
# pd.concat([df5, df10], axis=1, join='inner')=       Name  Age  Grade
# 1    Si Li   30     80
# 2  Wu Wang   25     85

keys 参数的使用场景在于多个表合并后,用户仍然想要知道新表中的数据来自于哪个原表,这时可以通过 keys 参数产生多级索引进行标记

"""
第一个表中都是一班的同学,而第二个表中都是二班的同学,可以使用如下方式合并:
"""
print("pd.concat([df5, df6], keys=['one', 'two'])=", pd.concat([df5, df6], keys=['one', 'two']))
# pd.concat([df5, df6], keys=['one', 'two'])=              Name  Age
# one 0   San Zhang   20
#     1       Si Li   30
#     2     Wu Wang   25
# two 0   Ming Xiao   19
#     1   Hong Xiao   18
#     2  Zhang Xiao   23

2.2 序列与表的合并

利用 concat 可以实现多个表之间的方向拼接,如果想要把一个序列追加到表的行末或者列末,还可以分别使用 append 和 assign 方法。

在 append 中,如果原表是默认整数序列的索引,那么可以使用 ignore_index=True 对新序列对应索引的自动标号,否则必须对 Series 指定 name 属性。

df11=pd.Series(['Wu Qian', 24], index = df5.columns)
print("df5.append(df11, ignore_index=True)=",df5.append(df11, ignore_index=True))
# df5.append(df11, ignore_index=True)=         Name  Age
# 0  San Zhang   20
# 1      Si Li   30
# 2    Wu Wang   25
# 3    Wu Qian   24
"""
assign虽然可以利用其添加新的列,但一般通过 df['new_col'] = ... 的形式就可以等价地添加新列。
同时使用 [] 修改的缺点是它会直接在原表上进行改动,而 assign 返回的是一个临时副本.
"""
df12=pd.Series([80, 90,95])
print("df5.assign(Grade=df12)=",df5.assign(Grade=df12))
# df5.assign(Grade=df12)=         Name  Age  Grade
# 0  San Zhang   20     80
# 1      Si Li   30     90
# 2    Wu Wang   25     95
df5['Grade'] = df12
print("df5=",df5)
# df5=         Name  Age  Grade
# 0  San Zhang   20     80
# 1      Si Li   30     90
# 2    Wu Wang   25     95

三、类连接操作

3.1 比较

compare 是在 1.1.0 后引入的新函数,它能够比较两个表或者序列的不同处并将其汇总展示

df13= pd.DataFrame({'Name':['San Zhang', 'Si Li', 'Wu Wang'],'Age':[20, 21 ,21],'Class':['one', 'two', 'three']})
df14= pd.DataFrame({'Name':['San Zhang', 'Li Si', 'Wu Qian'],'Age':[20, 21 ,25],'Class':['one', 'three', 'Three']})
print("df13.compare(df14)=",df13.compare(df14))
# df13.compare(df14)=       Name            Age        Class
#       self    other  self other   self  other
# 1    Si Li    Li Si   NaN   NaN    two  three
# 2  Wu Wang  Wu Qian  21.0  25.0  three  Three
"""
结果中返回了不同值所在的行列,如果相同则会被填充为缺失值 NaN ,其中 other 和 self 分别指代传入的参数表和被调用的表自身。
"""
# 如果想要完整显示表中所有元素的比较情况,可以设置 keep_shape=True
print("df13.compare(df14, keep_shape=True)=",df13.compare(df14, keep_shape=True))
# df13.compare(df14, keep_shape=True)=       Name            Age        Class       
#       self    other  self other   self  other
# 0      NaN      NaN   NaN   NaN    NaN    NaN
# 1    Si Li    Li Si   NaN   NaN    two  three
# 2  Wu Wang  Wu Qian  21.0  25.0  three  Three

3.2 组合

combine 函数能够让两张表按照一定的规则进行组合,在进行规则比较时会自动进行列索引的对齐。对于传入的函数而言,每一次操作中输入的参数是来自两个表的同名 Series ,依次传入的列是两个表列名的并集,例如下面这个例子会依次传入 A,B,C,D 四组序列,每组为左右表的两个序列。同时,进行 A 列比较的时候, s1 指代的就是一个全空的序列,因为它在被调用的表中并不存在,并且来自第一个表的序列索引会被 reindex 成两个索引的并集。具体的过程可以通过在传入的函数中插入适当的 print 方法查看。

def choose_min(s1, s2):
    s2 = s2.reindex_like(s1)
    res = s1.where(s1 < s2, s2)
    res = res.mask(s1.isna())  # isna表示是否为缺失值,返回布尔序列
    return res
df15 = pd.DataFrame({'A':[1,2,9], 'B':[3,4,6], 'C':[5,6,8]})
df16 = pd.DataFrame({'B':[5,6,4], 'C':[7,8,2], 'D':[9,10,3]}, index=[1,2,4])
print("df15.combine(df16, choose_min)=",df15.combine(df16, choose_min))
# df15.combine(df16, choose_min)=     A    B    C   D
# 0 NaN  NaN  NaN NaN
# 1 NaN  4.0  6.0 NaN
# 2 NaN  6.0  8.0 NaN
# 4 NaN  NaN  NaN NaN

#  设置 overtwrite 参数为 False 可以保留 被调用表 中未出现在传入的参数表中的列,而不会设置未缺失值.
print("df15.combine(df16, choose_min, overwrite=False)=",
      df15.combine(df16, choose_min, overwrite=False))
# df15.combine(df16, choose_min, overwrite=False)=      A    B    C   D
# 0  1.0  NaN  NaN NaN
# 1  2.0  4.0  6.0 NaN
# 2  9.0  6.0  8.0 NaN
# 4  NaN  NaN  NaN NaN

参考文献

1、https://datawhalechina.github.io/joyful-pandas/build/html/%E7%9B%AE%E5%BD%95/ch6.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值