Task06|连接|joyfulpandas|组队学习 2022.8月组队学习

一、关系型连接

1. 连接的基本概念

2. 值连接 merge函数

df1 = pd.DataFrame({'Name':['San Zhang','Si Li'], 'Age':[20,30]})
df2 = pd.DataFrame({'Name':['Si Li','Wu Wang'], 'Gender':['F','M']})
df1.merge(df2, on='Name', how='left')
#如果两个表中想要连接的列不具备相同的列名,可以通过`left_on`和`right_on`指定:
df1 = pd.DataFrame({'df1_name':['San Zhang','Si Li'], 'Age':[20,30]})
df2 = pd.DataFrame({'df2_name':['Si Li','Wu Wang'], 'Gender':['F','M']})
df1.merge(df2, left_on='df1_name', right_on='df2_name', how='left')
#出现了重复的列名,可以通过suffixes参数指定
df1 = pd.DataFrame({'Name':['San Zhang','lili'],'Grade':[70,90]})
df2 = pd.DataFrame({'Name':['San Zhang','lili'],'Grade':[80,85]})
#出现重复元素的时候指定on参数为多个列使得正确连接
df1.merge(df2, on='Name', how='left', suffixes=['_Chinese','_Math'])
#错误的结果
df1.merge(df2, on=['Name','Class'],how='left')
#正确的结果 必须确保唯一性 通过姓名和班级可以确定一个人

检查重复:duplicated函数
merge提供了validate参数来检查连接的唯一性

  1. 一对一 1:1
  2. 多对一 m:1
  3. 一对多 1:m
    第一个是指左右表的键都是唯一的,后面两个分别指右表键唯一和左表键唯一。
【练一练】

上面以多列为键的例子中,错误写法显然是一种多对多连接,而正确写法是一对一连接,请修改原表,使得以多列为键的正确写法能够通过validate='1:m'的检验,但不能通过validate='m:1'的检验。

#可以通过1:m的检验 但不能通过 m:1的检验
df1=pd.DataFrame({'Name':['San Zhang','Si Li'],'Age':[20,21],'Class':['one','two']})
#出现在左表中的内容不出现在右表
df2=pd.DataFrame({'Name':['San Zhang','San Zhang'],'Gender':['F','M'],'Class':['one','one']})
#除了Gender之外都是相等的,连接的根据是 Name和Class,validate的时候可以检测时1:m和M:1
Err_Msg='不存在错误'
try:
    df1.merge(df2,on=['Name','Class'],how='left',validate='1:m')
except Exception as e:
    Err_Msg=e
Err_Msg

出现重复列的时候会报错

3. 索引连接

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

df1=pd.DataFrame({'Age':[20,30]},index=pd.Series(['San Zhang','Si Li']),name='Name')
df2=pd.DataFrame({'Gender':['F','M']},index=pd.Series(['Si Li','Wu Wang']),name='Name')
df1.join(df2,how='left')

df1=pd.DataFrame({'Grade':[70]},index=pd.Series(['San']))
df1.join(df2, how='left', lsuffix='_Chinese', rsuffix='_Math')


#使用多级索引进行连接
df1=pd.DataFrame({'Age':[20,21]},index=pd.MultiIndex.from_arrays([['San Zhang','San Zhang'],['one','two']],names=('Names','Class')))

df2=pd.DataFrame({'Gender':['F','M']},index=pd.MultiIndex.from_arrays([['San Zhang','San Zhang'],['two','one']],names=('Names','Class')))

df1.join(df2)

二、方向连接

1. concat

只是希望把两个表或者多个表按照纵向或者横向拼接,为这种需求,pandas中提供了concat函数来实现
concat是关于索引进行连接的

  1. axis 拼接方向 (0:纵向 1:横向)

  2. join 连接形式 (默认outer保留所有的列,将不存在的列设置为缺失)
    inner 表示保留两个表都出现过的列

  3. keys 在新表中指示来自于哪张旧表中的名字
    使用于多个表合并后,用户仍然想要知道新表中的数据来源于哪个原表,可以通过keys参数产生多级索引进行标记。

纵向合并表中人的信息 (axis=0)

df1=pd.DataFrame({'Name':['San Zhang','Si Li'],'Age':[20,30]})
df2=pd.DataFrame({'Name':['Wu Wang'],'Age':[40]})
pd.concat([df1,df2])
#注意concat传入的是一个列表的形式

横向合并表中人的信息 第二个参数传入 1(axis=1)

横向拼接根据行索引对齐

df1=pd.DataFrame({'Name':['San Zhang','Si Li'],'Age':[20,30]})
df2=pd.DataFrame({'Grade':[80,90]})
df3=pd.DataFrame({'Gender':['M','F']})
pd.concat([df1,df2,df3],1)
#部分交叉横向合并

pd.concat([df1,df2],axis=1,join='inner')
#内部交集横向合并

pd.concat([df1,df2],keys=['one','two'])
#合并后标注行或列的来源

在这里插入图片描述

keys

df1=pd.DataFrame({'Name':['San Zhang','Si Li'],'Age':[20,21]})
df2=pd.DataFrame({'Name':['Wu Wang'],'Age':[21]})
pd.concat([df1,df2],keys=['one','two'])
TIPS:

当确认使用多表连接的方向进行合并的时候,尤其是横向合并,可以先用reset_index方法恢复默认整数索引再进行合并,防止出现由索引的误对齐和重复索引的笛卡儿积带来的错误

2. Series序列与DataFrame表的合并 append,assign

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

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

s=pd.Series(['Wu Wang',21],index=df1.columns)
df1.append(s,ignore_index=True)

(3)对于assign而言,虽然可以利用其添加新的列,但一般通过df['new_col']=...的形式就可以等价地添加新列

#特性
使用[]修改的缺点是它会直接在原表上进行改动
assign返回的是一个临时副本

#检测特性
s=pd.Series([80,90])
df1.assign(Grade=s)
df['Grade']=s
df1

(3)

  1. to_frame()方法将Series转化成DataFrame,使用concat方法进行合并

三、类连接操作

可以对两个表进行某些操作(类连接操作)

1. 比较

compare是在1.1.0后引入的函
作用:比较两个表或者序列的不同之处,并将其汇总展示

df1=pd.DataFrame('Name':['San Zhang','Si Li','Wu Wang'],'Age':[20,21,21],'Class':['one','two','three']})
df2=pd.DataFrame({'Name':['San Zhang','Li Si','Wu Wang'],'Age':[20,21,21],'Class':['one','two'.'Three']})

df1.compare(df2)
如果想要完整展示表中所有元素的比较情况,
可以设置`keep_shape=True`

df1.compare(df2,keep_shape=True)

结果返回了不同值所在的行列,结果相同会被填充缺失值NAN,其中的self和other代表被调用表自身和传入的参数表,如果一列全部都相同则不显示

2. 组合

combine函数能够让两张表按照一定的规则进行组合
进行规则比较的时候会自动进行列索引的对齐
对于传入的函数而言,每次操作中输入的参数是来自两个表的同名Series,一次传入的列是两个表列名的并集

def choose_min(s1,s2):
	s2=s2.reindex_like(s1)
	res=s1.where(s1<s2,s2)
	print(res)
	res=res.mask(s1.isna())
	print('mask之后',res)
	return res
df1=pd.DataFrame({'A':[1,2],'B':[3,4],'C':[5,6]})
df2=pd.DataFrame({'B':[5,6],'C':[7,8],'D':[9,10]},index=[1,2])
df1.combine(df2,choose_min)
第一个参数是要比较的表,第二个参数是比较的函数
每列相比的结果结合起来
【练一练】

请在上述代码的基础上修改,保留df2中4个未被df1替换的相应位置原始值。

res=res.mask(s1.isna())作用是和一个值比较,而且在s1列不存在的时候

df1.combine(df2,lambda s1,s2:s1.where(s1<s2,s2))
#res=res.mask(s1.isna())和一个值进行比较,且s1列不存在时,不展示s2列

【END】

此外,设置overtwrite参数为False可以保留 被调用表 \color{red}{被调用表} 被调用表中未出现在传入的参数表中的列,而不会设置未缺失值:

df1.combine(df2,choose_min,overwrite=False)
【练一练】

除了combine之外,pandas中还有一个combine_first方法,其功能是在对两张表组合时,若第二张表中的值在第一张表中对应索引位置的值不是缺失状态,那么就使用第一张表的值填充。下面给出一个例子,请用combine函数完成相同的功能。

【END】
df1=pd.DataFrame({'A':[1,2],'B':[3,np.nan]})
df2=pd.DataFrame({'A':[5,6],'B':[7,8]},index=[1,2])
df1.combine_first(df2)
#combine_first 

df2填充df1缺失的地方

四、练习

Ex1:美国疫情数据集

现有美国4月12日至11月16日的疫情报表(在/data/us_report文件夹下),请将New YorkConfirmed, Deaths, Recovered, Active合并为一张表,索引为按如下方法生成的日期字符串序列:

date=pd.date_range('20200412','20201116').to_series()
date=date.dt.month.astype('string').str.zfill(2)+'-'+date.dt.day.astype('string').str.zfill(2)+'-'+'2020'
date=date.tolist()
#转换为list类型
date[:5]
#取出前5个数据

Ex2:实现join函数

join函数处理索引连接

  1. lsuffix rsuffix
  2. on ,how参数

左连接,右连接,外连接

def join(df1,df2,how='left'):
    res_col=df1.columns.tolist()+df2.columns.tolist()
    print('df1columns+df2columns',res_col)
    dup=df1.index.unique().intersection(df2.index.unique())
    #返回存在于df1和df2的行索引index
    res_df=pd.DataFrame(columns=res_col)
    print(res_df)
    #构造一个列索引为df1和df2的拼接而成的
    for label in dup:
        #遍历df1和df2的行索引的交集
        cartesian=[list(i)+list(j) for i in df1.loc[label
                  ].values.reshape(-1,1) for j in df2.loc[
                  label].values.reshape(-1,1)]
        #合并行索引相同的列
        dup_df=pd.DataFrame(cartesian,index=[label]*len(
        cartesian),columns=res_col)
        #结果构造一个dataframe
        res_df=pd.concat([res_df,dup_df])
        #合并res和dup
    if how in ['left','outer']:
        for label in df1.index.unique().difference(dup):
            if isinstance(df1.loc[label],pd.DataFrame):
                cat=[list(i)+[np.nan]*df2.shape[1
                    ] for i in df1.loc[label].values]
            else: cat=[list(i)+[np.nan]*df2.shape[1
                     ]for i in df1.loc[label].to_frame().values]
            dup_df=pd.DataFrame(cat,index=[label
                    ]*len(cat),columns=res_col)
            res_df=pd.concat([res_df,dup_df])
    if how in ['right','outer']:
        for label in df2.index.unique().difference(dup):
            #交集之外存在于df2的标签
            
            if isinstance(df2.loc[label],pd.DataFrame):
                cat=[[np.nan]+list(i)*df1.shape[1
                ] for i in df2.loc[label].values]
            #如果df2存在label的dataframe,
            # cat=[[np.nan]+list(i)*df1.shape[1
            #    ] for i in df2.loc[label].values]
            #这个是什么意思
            
            else: cat=[[np.nan]+list(i)*df1.shape[1
                      ] for i in df2.loc[label].to_frame().values]
            dup_df=pd.DataFrame(cat,index=[label
                   ]*len(cat),columns=res_col)
            res_df=pd.concat([res_df,dup_df])
    return res_df
df1 = pd.DataFrame({'col1':list('01234')}, index=list('AABCD'))
df2 = pd.DataFrame({'col2':list('opqrst')}, index=list('ABBCEE'))
join(df1, df2, how='outer')

请实现带有how参数的join函数

  • 假设连接的两表无公共列

  • 调用方式为 join(df1, df2, how="left")

  • 给出测试样例

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值