• 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')
    #正确的结果 必须确保唯一性 通过姓名和班级可以确定一个人
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    检查重复: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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    出现重复列的时候会报错

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    二、方向连接

    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
    • 2
    • 3
    • 4

    横向合并表中人的信息 第二个参数传入 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'])
    #合并后标注行或列的来源
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    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'])
    
    • 1
    • 2
    • 3
    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)
    
    • 1
    • 2

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

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

    #检测特性
    s=pd.Series([80,90])
    df1.assign(Grade=s)
    df['Grade']=s
    df1
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    结果返回了不同值所在的行列,结果相同会被填充缺失值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)
    第一个参数是要比较的表,第二个参数是比较的函数
    每列相比的结果结合起来
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    【练一练】

    请在上述代码的基础上修改,保留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列
    
    
    • 1
    • 2
    • 3
    【END】

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

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

    除了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 
    
    • 1
    • 2
    • 3
    • 4

    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个数据
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

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

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

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

    • 给出测试样例

  • 相关阅读:
    Docker 基本管理
    kubeadm搭建kubernetes(k8s)
    将邻接矩阵转换成图
    Linux运行级别指定
    ==和equals
    汇编语言快速入门(非常详细)
    Linux内核中ideapad-laptop.c文件全解析5
    Linux下编译libevent源码
    如何制作一个英语单词二维码?
    【C语言】指针经典笔试题(上)
  • 原文地址:https://blog.csdn.net/m0_52024881/article/details/126678462