【问题标题】:Pandas Merging 101熊猫合并101
【发布时间】:2023-03-06 14:09:01
【问题描述】:
  • 如何使用 pandas 执行 (INNER| (LEFT|RIGHT|FULL) OUTER) JOIN
  • 如何在合并后为缺失的行添加 NaN?
  • 合并后如何去除 NaN?
  • 我可以合并索引吗?
  • 如何合并多个 DataFrame?
  • 与 pandas 交叉联接
  • merge? join? concat? update?谁?什么?为什么?!

...等等。我已经看到这些反复出现的问题询问熊猫合并功能的各个方面。今天,关于合并及其各种用例的大部分信息都分散在数十个措辞不当、无法搜索的帖子中。这里的目的是为后代整理一些更重要的观点。

本问答旨在成为有关 Pandas 常用习语的一系列有用用户指南的下一部分(请参阅 this post on pivotingthis post on concatenation,稍后我会谈到)。

请注意,这篇文章不是要替代the documentation,所以也请阅读!部分示例取自那里。


目录

为了方便访问。

【问题讨论】:

    标签: python pandas join merge concatenation


    【解决方案1】:

    这篇文章旨在为读者提供 SQL 风格与 Pandas 合并的入门知识,如何使用它以及何时不使用它。

    特别是,本文将介绍以下内容:

    • 基础 - 连接类型(左、右、外、内)

      • 与不同的列名合并
      • 与多列合并
      • 避免输出中出现重复的合并键列

    这篇文章(以及我在这个线程上的其他帖子)不会经过:

    • 与性能相关的讨论和时间安排(目前)。在适当的情况下,主要提到了更好的替代方案。
    • 处理后缀、删除多余的列、重命名输出和其他特定用例。还有其他(阅读:更好的)帖子可以解决这个问题,所以弄清楚吧!

    注意 大多数示例在演示各种功能时默认使用 INNER JOIN 操作,除非另有说明。

    此外,这里的所有 DataFrame 都可以复制和复制,所以 你可以和他们一起玩。另见this post 关于如何从剪贴板中读取 DataFrame。

    最后,JOIN 操作的所有可视化表示都是使用 Google 绘图手绘的。灵感来自here



    说够了 - 告诉我如何使用merge

    设置与基础

    np.random.seed(0)
    left = pd.DataFrame({'key': ['A', 'B', 'C', 'D'], 'value': np.random.randn(4)})
    right = pd.DataFrame({'key': ['B', 'D', 'E', 'F'], 'value': np.random.randn(4)})
    
    left
    
      key     value
    0   A  1.764052
    1   B  0.400157
    2   C  0.978738
    3   D  2.240893
    
    right
    
      key     value
    0   B  1.867558
    1   D -0.977278
    2   E  0.950088
    3   F -0.151357
    

    为简单起见,键列具有相同的名称(目前)。

    INNER JOIN

    表示

    注意 这和即将到来的数字都遵循这个约定:

    • 蓝色表示合并结果中存在的行
    • 红色表示从结果中排除(即删除)的行
    • 绿色表示在结果中被NaNs 替换的缺失值

    要执行 INNER JOIN,请在左侧 DataFrame 上调用 merge,指定右侧 DataFrame 和连接键(至少)作为参数。

    left.merge(right, on='key')
    # Or, if you want to be explicit
    # left.merge(right, on='key', how='inner')
    
      key   value_x   value_y
    0   B  0.400157  1.867558
    1   D  2.240893 -0.977278
    

    这仅返回来自leftright 的行,它们共享一个公共键(在本例中为“B”和“D”)。

    LEFT OUTER JOIN,或 LEFT JOIN 表示为

    这可以通过指定how='left'来执行。

    left.merge(right, on='key', how='left')
    
      key   value_x   value_y
    0   A  1.764052       NaN
    1   B  0.400157  1.867558
    2   C  0.978738       NaN
    3   D  2.240893 -0.977278
    

    仔细注意此处 NaN 的位置。如果您指定how='left',则仅使用来自left 的键,而来自right 的缺失数据将被NaN 替换。

    同样,对于 RIGHT OUTER JOIN,或 RIGHT JOIN 是...

    ...指定how='right':

    left.merge(right, on='key', how='right')
    
      key   value_x   value_y
    0   B  0.400157  1.867558
    1   D  2.240893 -0.977278
    2   E       NaN  0.950088
    3   F       NaN -0.151357
    

    这里使用来自right 的键,来自left 的缺失数据被NaN 替换。

    最后,对于 FULL OUTER JOIN,由

    给出

    指定how='outer'

    left.merge(right, on='key', how='outer')
    
      key   value_x   value_y
    0   A  1.764052       NaN
    1   B  0.400157  1.867558
    2   C  0.978738       NaN
    3   D  2.240893 -0.977278
    4   E       NaN  0.950088
    5   F       NaN -0.151357
    

    这使用两个帧中的键,并为两个帧中的缺失行插入 NaN。

    文档很好地总结了这些不同的合并:


    其他 JOIN - LEFT-Excluding、RIGHT-Excluding 和 FULL-Excluding/ANTI JOIN

    如果您需要分两步LEFT-Excluding JOINRIGHT-Excluding JOIN

    对于LEFT-Excluding JOIN,表示为

    首先执行 LEFT OUTER JOIN,然后仅过滤(排除!)来自 left 的行,

    (left.merge(right, on='key', how='left', indicator=True)
         .query('_merge == "left_only"')
         .drop('_merge', 1))
    
      key   value_x  value_y
    0   A  1.764052      NaN
    2   C  0.978738      NaN
    

    在哪里,

    left.merge(right, on='key', how='left', indicator=True)
    
      key   value_x   value_y     _merge
    0   A  1.764052       NaN  left_only
    1   B  0.400157  1.867558       both
    2   C  0.978738       NaN  left_only
    3   D  2.240893 -0.977278       both

    同样,对于 RIGHT-Excluding JOIN,

    (left.merge(right, on='key', how='right', indicator=True)
         .query('_merge == "right_only"')
         .drop('_merge', 1))
    
      key  value_x   value_y
    2   E      NaN  0.950088
    3   F      NaN -0.151357

    最后,如果您需要进行合并,只保留左侧或右侧的键,但不能同时保留两者(IOW,执行 ANTI-JOIN),

    你可以用类似的方式来做这件事——

    (left.merge(right, on='key', how='outer', indicator=True)
         .query('_merge != "both"')
         .drop('_merge', 1))
    
      key   value_x   value_y
    0   A  1.764052       NaN
    2   C  0.978738       NaN
    4   E       NaN  0.950088
    5   F       NaN -0.151357
    

    键列的不同名称

    如果键列的名称不同——例如,left 具有 keyLeftright 具有 keyRight 而不是 key——那么您必须将 left_onright_on 指定为参数而不是on:

    left2 = left.rename({'key':'keyLeft'}, axis=1)
    right2 = right.rename({'key':'keyRight'}, axis=1)
    
    left2
    
      keyLeft     value
    0       A  1.764052
    1       B  0.400157
    2       C  0.978738
    3       D  2.240893
    
    right2
    
      keyRight     value
    0        B  1.867558
    1        D -0.977278
    2        E  0.950088
    3        F -0.151357
    
    left2.merge(right2, left_on='keyLeft', right_on='keyRight', how='inner')
    
      keyLeft   value_x keyRight   value_y
    0       B  0.400157        B  1.867558
    1       D  2.240893        D -0.977278
    

    避免在输出中重复键列

    当从left 合并keyLeft 和从right 合并keyRight 时,如果您只想在输出中使用keyLeftkeyRight(但不是两者)之一,您可以通过设置索引作为初步步骤。

    left3 = left2.set_index('keyLeft')
    left3.merge(right2, left_index=True, right_on='keyRight')
    
        value_x keyRight   value_y
    0  0.400157        B  1.867558
    1  2.240893        D -0.977278
    

    将此与之前命令的输出(即left2.merge(right2, left_on='keyLeft', right_on='keyRight', how='inner') 的输出)进行对比,您会注意到缺少keyLeft。您可以根据将哪个帧的索引设置为键来确定要保留的列。这在执行某些 OUTER JOIN 操作时可能很重要。


    仅合并来自DataFrames 之一的单个列

    例如,考虑

    right3 = right.assign(newcol=np.arange(len(right)))
    right3
      key     value  newcol
    0   B  1.867558       0
    1   D -0.977278       1
    2   E  0.950088       2
    3   F -0.151357       3
    

    如果您只需要合并“new_val”(没有任何其他列),您通常可以在合并之前只对列进行子集:

    left.merge(right3[['key', 'newcol']], on='key')
    
      key     value  newcol
    0   B  0.400157       0
    1   D  2.240893       1
    

    如果您正在执行 LEFT OUTER JOIN,则更高效的解决方案将涉及 map

    # left['newcol'] = left['key'].map(right3.set_index('key')['newcol']))
    left.assign(newcol=left['key'].map(right3.set_index('key')['newcol']))
    
      key     value  newcol
    0   A  1.764052     NaN
    1   B  0.400157     0.0
    2   C  0.978738     NaN
    3   D  2.240893     1.0
    

    如前所述,这类似于,但比

    left.merge(right3[['key', 'newcol']], on='key', how='left')
    
      key     value  newcol
    0   A  1.764052     NaN
    1   B  0.400157     0.0
    2   C  0.978738     NaN
    3   D  2.240893     1.0
    

    合并多列

    要加入多个列,请指定on(或left_onright_on,视情况而定)的列表。

    left.merge(right, on=['key1', 'key2'] ...)
    

    或者,如果名称不同,

    left.merge(right, left_on=['lkey1', 'lkey2'], right_on=['rkey1', 'rkey2'])
    

    其他有用的merge*操作和函数

    本部分仅涵盖最基本的内容,旨在激发您的兴趣。有关更多示例和案例,请参阅documentation on merge, join, and concat 以及功能规范的链接。



    继续阅读

    跳到 Pandas Merging 101 中的其他主题以继续学习:

    *你在这里。

    【讨论】:

    • 如果有人对每篇文章末尾的目录感到困惑,我将这个庞大的答案分成 4 个单独的答案,3 个在这个问题上,1 个在另一个问题上。以前的设置方式使得向人们推荐特定主题变得更加困难。这使您现在可以轻松地为单独的主题添加书签!
    • 这是一个很棒的资源!我唯一的问题是为什么叫它合并而不是加入,加入而不是合并?
    【解决方案2】:

    pd.concat([df0, df1], kwargs) 的补充视觉视图。 请注意,kwarg axis=0axis=1 的含义不如 df.mean()df.apply(func) 直观


    【讨论】:

    • 这是一个很好的图表。请问你们是怎么制作的?
    • google doc 的内置“插入 ==> 绘图... ==> 新”(截至 2019 年 5 月)。但是,要明确一点:我为这张图片使用 google doc 的唯一原因是因为我的笔记存储在 google doc 中,我想要一张可以在 google doc 本身中快速修改的图片。其实现在你提到了,google doc 的绘图工具很漂亮。
    • 哇,这太棒了。来自 SQL 世界,“垂直”连接在我的脑海中并不是一个连接,因为表的结构总是固定的。现在甚至认为 pandas 应该合并 concatmerge,方向参数为 horizontalvertical
    • @Ufos 这不正是axis=1axis=0 的样子吗?
    • 是的,现在有 mergeconcat 和轴等等。但是,正如@eliu 所示,这都是 merge 与“左”和“右”以及“水平”或“垂直”的相同概念。就个人而言,每次我必须记住哪个“轴”是 0 和哪个是 1 时,我都必须查看文档。
    【解决方案3】:

    加入 101

    这些动画可能更能直观地向您解释。 致谢:Garrick Aden-Buie tidyexplain repo

    内连接

    外部联接或完全联接

    右连接

    左连接

    【讨论】:

    • 这些太棒了!
    • 我很感激为实现这一目标付出的​​努力。做得很好。
    【解决方案4】:

    在这个答案中,我将考虑实际的例子。

    第一个,是pandas.concat

    第二个,从一个索引和另一个列合并数据帧。


    1pandas.concat

    考虑以下具有相同列名的DataFrames

    Preco2018,尺寸为 (8784, 5)

    Preco 2019 尺寸为 (8760, 5)

    具有相同的列名。

    您可以使用pandas.concat 将它们组合起来,只需

    import pandas as pd
    
    frames = [Preco2018, Preco2019]
    
    df_merged = pd.concat(frames)
    

    这会导致 DataFrame 具有以下大小 (17544, 5)

    如果你想可视化,它最终会像这样工作

    (Source)


    2。按列和索引合并

    在这一部分,我会考虑一个具体的案例:如果想合并一个数据框的索引和另一个数据框的列。

    假设一个数据框 Geo 有 54 列,是日期 Data 的列之一,其类型为 datetime64[ns]

    数据框Price 有一列价格,索引对应日期

    在这种特定情况下,要合并它们,可以使用pd.merge

    merged = pd.merge(Price, Geo, left_index=True, right_on='Data')
    

    这会导致以下数据框

    【讨论】:

      【解决方案5】:

      这篇文章将涉及以下主题:

      • 不同条件下与索引合并
        • 基于索引的连接选项:mergejoinconcat
        • 合并索引
        • 合并一个索引,另一个列
      • 有效地使用命名索引来简化合并语法

      BACK TO TOP



      基于索引的联接

      TL;DR

      有几个选项,根据用途,有些比其他更简单 案例。

      1. DataFrame.mergeleft_indexright_index(或 left_onright_on 使用名称索引)
        • 支持内/左/右/全
        • 一次只能加入两个
        • 支持列-列、索引-列、索引-索引连接
      2. DataFrame.join(加入索引)
        • 支持内/左(默认)/右/全
        • 可以一次加入多个 DataFrames
        • 支持索引-索引连接
      3. pd.concat(加入索引)
        • 支持内部/完整(默认)
        • 可以一次加入多个 DataFrames
        • 支持索引-索引连接

      索引到索引连接

      设置与基础

      import pandas as pd
      import numpy as np
      
      np.random.seed([3, 14])
      left = pd.DataFrame(data={'value': np.random.randn(4)}, 
                          index=['A', 'B', 'C', 'D'])    
      right = pd.DataFrame(data={'value': np.random.randn(4)},  
                           index=['B', 'D', 'E', 'F'])
      left.index.name = right.index.name = 'idxkey'
      
      left
                 value
      idxkey          
      A      -0.602923
      B      -0.402655
      C       0.302329
      D      -0.524349
      
      right
       
                 value
      idxkey          
      B       0.543843
      D       0.013135
      E      -0.326498
      F       1.385076
      

      通常,inner join on index 如下所示:

      left.merge(right, left_index=True, right_index=True)
      
               value_x   value_y
      idxkey                    
      B      -0.402655  0.543843
      D      -0.524349  0.013135
      

      其他连接遵循类似的语法。

      值得注意的替代品

      1. DataFrame.join 默认加入索引。 DataFrame.join 默认执行 LEFT OUTER JOIN,所以这里需要 how='inner'

         left.join(right, how='inner', lsuffix='_x', rsuffix='_y')
        
                  value_x   value_y
         idxkey                    
         B      -0.402655  0.543843
         D      -0.524349  0.013135
        

        请注意,我需要指定 lsuffixrsuffix 参数,否则 join 会出错:

         left.join(right)
         ValueError: columns overlap but no suffix specified: Index(['value'], dtype='object')
        

        由于列名相同。如果它们的名称不同,这将不是问题。

         left.rename(columns={'value':'leftvalue'}).join(right, how='inner')
        
                 leftvalue     value
         idxkey                     
         B       -0.402655  0.543843
         D       -0.524349  0.013135
        
      2. pd.concat 加入索引,可以一次加入两个或多个 DataFrame。默认情况下它会进行完全外连接,所以这里需要how='inner'..

         pd.concat([left, right], axis=1, sort=False, join='inner')
        
                    value     value
         idxkey                    
         B      -0.402655  0.543843
         D      -0.524349  0.013135
        

        有关concat 的更多信息,请参阅this post


      索引到列连接

      要使用左侧索引、右侧列执行内连接,您将使用 DataFrame.merge 组合 left_index=Trueright_on=...

      right2 = right.reset_index().rename({'idxkey' : 'colkey'}, axis=1)
      right2
       
        colkey     value
      0      B  0.543843
      1      D  0.013135
      2      E -0.326498
      3      F  1.385076
      
      left.merge(right2, left_index=True, right_on='colkey')
      
          value_x colkey   value_y
      0 -0.402655      B  0.543843
      1 -0.524349      D  0.013135
      

      其他联接遵循类似的结构。请注意,只有merge 可以对列连接执行索引。您可以连接多个列,前提是左侧的索引级别数等于右侧的列数。

      joinconcat 不能混合合并。您需要使用DataFrame.set_index 将索引设置为前置步骤。


      有效地使用命名索引 [pandas >= 0.23]

      如果您的索引已命名,则从 pandas >= 0.23 开始,DataFrame.merge 允许您将索引名称指定为 on(或 left_onright_on 根据需要)。

      left.merge(right, on='idxkey')
      
               value_x   value_y
      idxkey                    
      B      -0.402655  0.543843
      D      -0.524349  0.013135
      

      前面用left的索引,right的column合并的例子,可以用left_on和left的索引名:

      left.merge(right2, left_on='idxkey', right_on='colkey')
      
          value_x colkey   value_y
      0 -0.402655      B  0.543843
      1 -0.524349      D  0.013135
      


      继续阅读

      跳转到 Pandas Merging 101 中的其他主题以继续学习:

      *你在这里

      【讨论】:

        【解决方案6】:

        这篇文章将涉及以下主题:

        • 如何正确泛化到多个 DataFrame(以及为什么 merge 在这里有缺点)
        • 合并唯一键
        • 在非唯一键上合并

        BACK TO TOP



        泛化到多个 DataFrames

        通常情况下,当多个 DataFrame 要合并在一起时会出现这种情况。天真地,这可以通过链接 merge 调用来完成:

        df1.merge(df2, ...).merge(df3, ...)
        

        但是,对于许多 DataFrame,这很快就会失控。此外,可能需要对未知数量的 DataFrame 进行泛化。

        这里我介绍pd.concat 用于唯一 键上的多路连接,DataFrame.join 用于非唯一 键上的多路连接。首先,设置。

        # Setup.
        np.random.seed(0)
        A = pd.DataFrame({'key': ['A', 'B', 'C', 'D'], 'valueA': np.random.randn(4)})    
        B = pd.DataFrame({'key': ['B', 'D', 'E', 'F'], 'valueB': np.random.randn(4)})
        C = pd.DataFrame({'key': ['D', 'E', 'J', 'C'], 'valueC': np.ones(4)})
        dfs = [A, B, C] 
        
        # Note, the "key" column values are unique, so the index is unique.
        A2 = A.set_index('key')
        B2 = B.set_index('key')
        C2 = C.set_index('key')
        
        dfs2 = [A2, B2, C2]
        

        唯一键的多路合并

        如果您的键(这里的键可以是列或索引)是唯一的,那么您可以使用pd.concat。请注意,pd.concat 在索引上加入 DataFrame

        # merge on `key` column, you'll need to set the index before concatenating
        pd.concat([
            df.set_index('key') for df in dfs], axis=1, join='inner'
        ).reset_index()
        
          key    valueA    valueB  valueC
        0   D  2.240893 -0.977278     1.0
        
        # merge on `key` index
        pd.concat(dfs2, axis=1, sort=False, join='inner')
        
               valueA    valueB  valueC
        key                            
        D    2.240893 -0.977278     1.0
        

        省略join='inner' 以获得完整的外部连接。请注意,您不能指定 LEFT 或 RIGHT OUTER 连接(如果需要,请使用 join,如下所述)。


        重复键的多路合并

        concat 速度很快,但也有缺点。它不能处理重复。

        A3 = pd.DataFrame({'key': ['A', 'B', 'C', 'D', 'D'], 'valueA': np.random.randn(5)})
        
        pd.concat([df.set_index('key') for df in [A3, B, C]], axis=1, join='inner')
        ValueError: Shape of passed values is (3, 4), indices imply (3, 2)
        

        在这种情况下,我们可以使用join,因为它可以处理非唯一键(请注意,join 在其索引上连接 DataFrame;它在后台调用 merge 并执行 LEFT OUTER JOIN,除非另有说明)。

        # join on `key` column, set as the index first
        # For inner join. For left join, omit the "how" argument.
        A.set_index('key').join(
            [df.set_index('key') for df in (B, C)], how='inner').reset_index()
        
          key    valueA    valueB  valueC
        0   D  2.240893 -0.977278     1.0
        
        # join on `key` index
        A3.set_index('key').join([B2, C2], how='inner')
        
               valueA    valueB  valueC
        key                            
        D    1.454274 -0.977278     1.0
        D    0.761038 -0.977278     1.0
        


        继续阅读

        跳到 Pandas Merging 101 中的其他主题以继续学习:

        *你在这里

        【讨论】:

          猜你喜欢
          相关资源
          最近更新 更多