8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

Pandas 合并 101

mate 1月前

60 0

如何使用 pandas 执行 (INNER| (LEFT|RIGHT|FULL) OUTER) JOIN?合并后如何为缺失行添加 NaN?合并后如何去除 NaN?我可以在索引上合并吗?如何...

  • 用熊猫 INNER 执行 ( LEFT | ( RIGHT | FULL ) OUTER ) JOIN |
  • 合并后如何为缺失的行添加 NaN?
  • 合并后如何摆脱 NaN?
  • 我可以在索引上合并吗?
  • 如何合并多个 DataFrame?
  • 与 Pandas 交叉连接
  • merge ????? join concat update 什么?为什么?!

... 等等。我看到这些反复出现的问题询问 pandas 合并功能的各个方面。目前,有关合并及其各种用例的大部分信息分散在数十个措辞不当、无法搜索的帖子中。这里的目的是整理一些更重要的要点以供后人参考。

本问答旨在成为有关常见熊猫习语的一系列实用用户指南的下一部分(请参阅 有关枢轴的这篇帖子 ,以及 有关串联的这篇帖子 ,我稍后将会谈到)。

请注意,这篇文章 并非 旨在替代 文档 ,因此也请阅读文档!一些示例取自文档。


目录

为了方便访问。

  • p5

  • p6

  • p7

  • p8

帖子版权声明 1、本帖标题:Pandas 合并 101
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由mate在本站《dataframe》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 这篇文章旨在为读者提供有关使用 Pandas 进行 SQL 风格合并的入门知识、如何使用它以及何时不使用它。

    具体来说,本文将讨论以下内容:

    • p2

      • merging with different column names
      • merging with multiple columns
      • avoiding duplicate merge key column in output

    本帖(以及我在这个主题下发布的其他帖子)不会涉及以下内容:

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

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

    此外,这里的所有 DataFrames 都可以复制和复制,因此您可以使用它们。此外,请参阅 此帖子 ,了解如何从剪贴板读取 DataFrames。

    最后,JOIN 操作的所有视觉表示都是使用 Google 绘图手绘的。灵感来自 这里 .



    说得够多了 - 只需告诉我如何使用 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

    注意 :本图表以及接下来的图表均遵循以下惯例:

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

    要执行 INNER JOIN,请 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
    

    这将仅返回 left 共享公共键的行(在此示例中为 \'B\' 和 \'D) right

    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 插入到两个框架中以表示缺失的行。

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

    Enter image description here


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

    如果需要 LEFT-Exclusion JOIN RIGHT-Exclusion JOIN, 则分两个步骤。

    对于 LEFT-Exclusive 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-Exclusive 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

    最后,如果您需要执行仅保留左侧或右侧键但不同时保留两者的合并(换句话说,执行 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 has keyLeft right has keyRight 而不是 key ),那么您必须指定 left_on right_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
    

    避免输出中出现重复的键列

    当根据 keyLeft 来自 left keyRight 来自 right 输出中 keyLeft 包括 keyRight

    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 操作时可能很重要。


    仅合并某个 DataFrame 中的一列

    例如,考虑

    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
    

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

    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_on right_on ,视情况而定)的列表。

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

    或者,如果名称不同,

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

    其他有用的合并*操作和函数

    • p38

    • p39

    • p40

    • p41

    本节仅介绍最基础的内容,旨在激发您的兴趣。有关更多示例和案例,请参阅有关 merge 、 join 和 concat 的文档以及函数规范的链接。



    继续阅读

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

    • p44

    • p45

    • p46

    • p47

    *您在这里。

  • 如果有人对每篇文章末尾的目录感到困惑,我将这个庞大的答案分成 4 个,3 个针对这个问题,1 个针对另一个问题。以前的设置方式使得人们更难找到特定主题。现在,您可以轻松为单独的主题添加书签!

  • h3n 1月前 0 只看Ta
    引用 4

    这是一个很棒的资源!我唯一的疑问是为什么叫它合并而不是连接,叫它连接而不是合并?

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


    on pd.concat([df0, df1])

  • google doc 内置的“插入 ==> 绘图... ==> 新建” (截至 2019 年 5 月)。但需要明确的是:我使用 google doc 绘制这张图片的唯一原因是我的笔记存储在 google doc 中,我想要一张可以在 google doc 中快速修改的图片。实际上,既然您提到了这一点,google doc 的绘图工具就非常简洁。

  • 哇,这太棒了。从 SQL 世界来看,\'垂直\' 连接在我看来并不是连接,因为表的结构始终是固定的。现在甚至认为 pandas 应该合并 concat 和 merge,方向参数为水平或垂直。

  • 是的,现在有合并、连接、轴等等。但是,正如@eliu 所示,它们都只是与“左”和“右”以及“水平”或“垂直”合并相同的概念。就我个人而言,每次我必须记住哪个“轴”是 0,哪个是 1 时,我都必须查看文档。

  • 加入 101

    这些动画可能更能直观地解释你。来源: Garrick Aden-Buie tidyexplain repo

    内连接

    enter image description here

    外连接或全连接

    enter image description here

    右连接

    enter image description here

    左连接

    enter image description here

  • 引用 10

    在这个回答中,我将考虑以下实际例子:

    1. p1

    2. p2

    我们将针对每种情况使用不同的数据框。


    1. pandas.concat

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

    • p6

         Year  Month  Day  Hour  Price0  2018      1    1     1   6.741  2018      1    1     2   4.742  2018      1    1     3   3.663  2018      1    1     4   2.304  2018      1    1     5   2.305  2018      1    1     6   2.066  2018      1    1     7   2.067  2018      1    1     8   2.068  2018      1    1     9   2.309  2018      1    1    10   2.30
    • p7

         Year  Month  Day  Hour  Price0  2019      1    1     1  66.881  2019      1    1     2  66.882  2019      1    1     3  66.003  2019      1    1     4  63.644  2019      1    1     5  58.855  2019      1    1     6  55.476  2019      1    1     7  56.007  2019      1    1     8  61.098  2019      1    1     9  61.019  2019      1    1    10  61.00

    可以使用 pandas.concat ,只需

    import pandas as pd
    
    frames = [Price2018, Price2019]
    
    df_merged = pd.concat(frames)
    

    这将产生一个大小为 (17544, 5)

    如果想清楚地了解发生了什么,可以这样做

    How concat works

    来源 )


    2. pandas.DataFrame.merge

    在本节中,我们将考虑一个具体的情况: 合并一个数据框的索引和另一个数据框的列 .

    假设有一个 Geo 包含 54 列的数据框,其中一列为 Date ,其类型为 datetime64[ns] .

                     Date         1         2  ...        51        52        53
    0 2010-01-01 00:00:00  0.565919  0.892376  ...  0.593049  0.775082  0.680621
    1 2010-01-01 01:00:00  0.358960  0.531418  ...  0.734619  0.480450  0.926735
    2 2010-01-01 02:00:00  0.531870  0.221768  ...  0.902369  0.027840  0.398864
    3 2010-01-01 03:00:00  0.475463  0.245810  ...  0.306405  0.645762  0.541882
    4 2010-01-01 04:00:00  0.954546  0.867960  ...  0.912257  0.039772  0.627696
    

    并且数据框 Price 有一列名为价格的列 Price ,索引对应于日期( Date )

                         Price
    Date                      
    2010-01-01 00:00:00  29.10
    2010-01-01 01:00:00   9.57
    2010-01-01 02:00:00   0.00
    2010-01-01 03:00:00   0.00
    2010-01-01 04:00:00   0.00
    

    为了合并它们,可以使用 pandas.DataFrame.merge 如下

    df_merged = pd.merge(Price, Geo, left_index=True, right_on='Date')
    

    其中 Geo Price 是先前的数据框。

    得出以下数据框

       Price                Date         1  ...        51        52        53
    0  29.10 2010-01-01 00:00:00  0.565919  ...  0.593049  0.775082  0.680621
    1   9.57 2010-01-01 01:00:00  0.358960  ...  0.734619  0.480450  0.926735
    2   0.00 2010-01-01 02:00:00  0.531870  ...  0.902369  0.027840  0.398864
    3   0.00 2010-01-01 03:00:00  0.475463  ...  0.306405  0.645762  0.541882
    4   0.00 2010-01-01 04:00:00  0.954546  ...  0.912257  0.039772  0.627696
    
  • 本文将讨论以下主题:

    • 不同条件下与索引合并
      • options for index-based joins: merge , join , concat
      • merging on indexes
      • merging on index of one, column of other
    • 有效地使用命名索引来简化合并语法

    返回顶部



    基于索引的连接

    长话短说

    有几个选项,根据用例的不同,有些选项比其他选项更简单。

    1. DataFrame.merge left_index and right_index (或 left_on and right_on 使用命名索引)
      • supports inner/left/right/full
      • can only join two at a time
      • supports column-column, index-column, index-index joins
    2. DataFrame.join (按索引连接)
      • supports inner/left (default)/right/full
      • can join multiple DataFrames at a time
      • supports index-index joins
    3. pd.concat (按索引连接)
      • supports inner/full (default)
      • can join multiple DataFrames at a time
      • supports index-index joins

    索引到索引连接

    设置与基础知识

    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
    

    通常, 索引上的内连接 看起来像这样:

    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. p7

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

      p8

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

      p9

       left.rename(columns={'value':'leftvalue'}).join(right, how='inner')         leftvalue     value idxkey                      B       -0.402655  0.543843 D       -0.524349  0.013135
    2. 第10页

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

      第11页


    索引到列的连接

    要使用左索引、右列执行内连接,您将使用 DataFrame.merge left_index=True 的组合 right_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 只能执行索引到列的连接。您可以连接多个列,前提是左侧的索引级别数等于右侧的列数。

    join 并且 concat 无法进行混合合并。您需要使用 DataFrame.set_index .


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

    如果您的索引已命名,那么从 p(或as >= 0.23 开始, DataFrame.merge 根据需要 on 指定索引名称 left_on and right_on

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

    对于前面与左侧索引、右侧列合并的示例,可以使用 left_on 左侧的索引名称:

    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 中的其他主题继续学习:

    • p18

    • 第19页

    • p20

    • p21

    * 你在这里

  • 引用 12

    本文将讨论以下主题:

    • 如何正确地推广到多个 DataFrames(以及为什么 merge 这里有缺点)
    • 根据唯一键进行合并
    • 根据非唯一键进行合并

    返回顶部



    推广到多个 DataFrames

    通常,当需要将多个 DataFrame 合并在一起时,就会出现这种情况。简单来说,这可以通过链接 merge 调用来完成:

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

    然而,对于许多 DataFrames 来说,这很快就会失控。此外,可能需要对未知数量的 DataFrames 进行概括。

    这里我介绍 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 joins DataFrames on the index .

    # 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 在其索引上连接 DataFrames; 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([B2, C2], 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 中的其他主题继续学习:

    • p10

    • p11

    • p12

    • p13

    * 你在这里

  • Pandas 目前不支持合并语法中的不等式连接;一个选项是使用 conditional_join pyjanitor ——我是这个库的贡献者:

    # pip install pyjanitor
    import pandas as pd
    import janitor 
    
    left.conditional_join(right, ('value', 'value', '>'))
    
       left           right
        key     value   key     value
    0     A  1.764052     D -0.977278
    1     A  1.764052     F -0.151357
    2     A  1.764052     E  0.950088
    3     B  0.400157     D -0.977278
    4     B  0.400157     F -0.151357
    5     C  0.978738     D -0.977278
    6     C  0.978738     F -0.151357
    7     C  0.978738     E  0.950088
    8     D  2.240893     D -0.977278
    9     D  2.240893     F -0.151357
    10    D  2.240893     E  0.950088
    11    D  2.240893     B  1.867558
    
    left.conditional_join(right, ('value', 'value', '<'))
    
      left           right
       key     value   key     value
    0    A  1.764052     B  1.867558
    1    B  0.400157     E  0.950088
    2    B  0.400157     B  1.867558
    3    C  0.978738     B  1.867558
    

    列作为元组的变量参数传递,每个元组由左数据框中的一列、右数据框中的一列和连接运算符组成,连接运算符可以是以下任意一种 (>, <, >=, <=, !=) 。在上面的示例中,由于列名重叠,因此返回了一个 MultiIndex 列。

    从性能角度来看,这比简单的交叉连接要好:

    np.random.seed(0)
    dd = pd.DataFrame({'value':np.random.randint(100000, size=50_000)})
    df = pd.DataFrame({'start':np.random.randint(100000, size=1_000), 
                       'end':np.random.randint(100000, size=1_000)})
    
    dd.head()
    
       value
    0  68268
    1  43567
    2  42613
    3  45891
    4  21243
    
    df.head()
    
       start    end
    0  71915  47005
    1  64284  44913
    2  13377  96626
    3  75823  38673
    4  29151    575
    
    
    %%timeit
    out = df.merge(dd, how='cross')
    out.loc[(out.start < out.value) & (out.end > out.value)]
    5.12 s ± 19 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    %timeit df.conditional_join(dd, ('start', 'value' ,'<'), ('end', 'value' ,'>'))
    280 ms ± 5.56 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    %timeit df.conditional_join(dd, ('start', 'value' ,'<'), ('end', 'value' ,'>'), use_numba=True)
    124 ms ± 12.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    out = df.merge(dd, how='cross')
    out = out.loc[(out.start < out.value) & (out.end > out.value)]
    A = df.conditional_join(dd, ('start', 'value' ,'<'), ('end', 'value' ,'>'))
    columns = A.columns.tolist()
    A = A.sort_values(columns, ignore_index = True)
    out = out.sort_values(columns, ignore_index = True)
    
    A.equals(out)
    True
    

    根据数据大小,当存在等值连接时,您可以获得更高的性能。在这种情况下,使用 pandas 合并函数,但最终数据帧被延迟,直到计算出非等值连接。让我们从 这里 :

    import pandas as pd
    import numpy as np
    import random
    import datetime
    
    def random_dt_bw(start_date,end_date):
        days_between = (end_date - start_date).days
        random_num_days = random.randrange(days_between)
        random_dt = start_date + datetime.timedelta(days=random_num_days)
        return random_dt
    
    def generate_data(n=1000):
        items = [f"i_{x}" for x in range(n)]
        start_dates = [random_dt_bw(datetime.date(2020,1,1),datetime.date(2020,9,1)) for x in range(n)]
        end_dates = [x + datetime.timedelta(days=random.randint(1,10)) for x in start_dates]
        
        offerDf = pd.DataFrame({"Item":items,
                                "StartDt":start_dates,
                                "EndDt":end_dates})
        
        transaction_items = [f"i_{random.randint(0,n)}" for x in range(5*n)]
        transaction_dt = [random_dt_bw(datetime.date(2020,1,1),datetime.date(2020,9,1)) for x in range(5*n)]
        sales_amt = [random.randint(0,1000) for x in range(5*n)]
        
        transactionDf = pd.DataFrame({"Item":transaction_items,"TransactionDt":transaction_dt,"Sales":sales_amt})
    
        return offerDf,transactionDf
    
    offerDf,transactionDf = generate_data(n=100000)
    
    
    offerDf = (offerDf
               .assign(StartDt = offerDf.StartDt.astype(np.datetime64), 
                       EndDt = offerDf.EndDt.astype(np.datetime64)
                      )
               )
    
    transactionDf = transactionDf.assign(TransactionDt = transactionDf.TransactionDt.astype(np.datetime64))
    
    # you can get more performance when using ints/datetimes
    # in the equi join, compared to strings
    
    offerDf = offerDf.assign(Itemr = offerDf.Item.str[2:].astype(int))
    
    transactionDf = transactionDf.assign(Itemr = transactionDf.Item.str[2:].astype(int))
    
    transactionDf.head()
          Item TransactionDt  Sales  Itemr
    0  i_43407    2020-05-29    692  43407
    1  i_95044    2020-07-22    964  95044
    2  i_94560    2020-01-09    462  94560
    3  i_11246    2020-02-26    690  11246
    4  i_55974    2020-03-07    219  55974
    
    offerDf.head()
      Item    StartDt      EndDt  Itemr
    0  i_0 2020-04-18 2020-04-19      0
    1  i_1 2020-02-28 2020-03-07      1
    2  i_2 2020-03-28 2020-03-30      2
    3  i_3 2020-08-03 2020-08-13      3
    4  i_4 2020-05-26 2020-06-04      4
    
    # merge on strings 
    merged_df = pd.merge(offerDf,transactionDf,on='Itemr')
    classic_int = merged_df[(merged_df['TransactionDt']>=merged_df['StartDt']) &
                            (merged_df['TransactionDt']<=merged_df['EndDt'])]
    
    # merge on ints ... usually faster
    merged_df = pd.merge(offerDf,transactionDf,on='Item')
    classic_str = merged_df[(merged_df['TransactionDt']>=merged_df['StartDt']) &            
                            (merged_df['TransactionDt']<=merged_df['EndDt'])]
    
    # merge on integers
    cond_join_int = (transactionDf
                     .conditional_join(
                         offerDf, 
                         ('Itemr', 'Itemr', '=='), 
                         ('TransactionDt', 'StartDt', '>='), 
                         ('TransactionDt', 'EndDt', '<=')
                      )
                     )
    
    # merge on strings
    cond_join_str = (transactionDf
                     .conditional_join(
                         offerDf, 
                         ('Item', 'Item', '=='), 
                         ('TransactionDt', 'StartDt', '>='), 
                         ('TransactionDt', 'EndDt', '<=')
                      )
                    )
    
    %%timeit
    merged_df = pd.merge(offerDf,transactionDf,on='Item')
    classic_str = merged_df[(merged_df['TransactionDt']>=merged_df['StartDt']) &
                            (merged_df['TransactionDt']<=merged_df['EndDt'])]
    292 ms ± 3.84 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    %%timeit
    merged_df = pd.merge(offerDf,transactionDf,on='Itemr')
    classic_int = merged_df[(merged_df['TransactionDt']>=merged_df['StartDt']) &
                            (merged_df['TransactionDt']<=merged_df['EndDt'])]
    253 ms ± 2.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    %%timeit 
    (transactionDf
    .conditional_join(
        offerDf, 
        ('Item', 'Item', '=='), 
        ('TransactionDt', 'StartDt', '>='), 
        ('TransactionDt', 'EndDt', '<=')
       )
    )
    256 ms ± 9.66 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    %%timeit 
    (transactionDf
    .conditional_join(
        offerDf, 
        ('Itemr', 'Itemr', '=='), 
        ('TransactionDt', 'StartDt', '>='), 
        ('TransactionDt', 'EndDt', '<=')
       )
    )
    71.8 ms ± 2.24 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
    
    # check that both dataframes are equal
    cols = ['Item', 'TransactionDt', 'Sales', 'Itemr_y','StartDt', 'EndDt', 'Itemr_x']
    cond_join_str = cond_join_str.drop(columns=('right', 'Item')).set_axis(cols, axis=1)
    
    (cond_join_str
    .sort_values(cond_join_str.columns.tolist())
    .reset_index(drop=True)
    .reindex(columns=classic_str.columns)
    .equals(
        classic_str
        .sort_values(classic_str.columns.tolist())
        .reset_index(drop=True)
    ))
    
    True
    
  • 我认为你应该在你的解释中包括这一点,因为这是我经常看到的相关合并, cross-join 我相信这是术语。这是在唯一 df 不共享任何列时发生的合并,它只是并排合并 2 个 df:

    设置:

    names1 = [{'A':'Jack', 'B':'Jill'}]
    
    names2 = [{'C':'Tommy', 'D':'Tammy'}]
    
    df1=pd.DataFrame(names1)
    df2=pd.DataFrame(names2)
    df_merged= pd.merge(df1.assign(X=1), df2.assign(X=1), on='X').drop('X', 1)
    

    这将创建一个虚拟的 X 列,在 X 上合并,然后将其删除以生成

    df_合并:

          A     B      C      D
    0  Jack  Jill  Tommy  Tammy
    
  • 请查看问题下的第二条评论。交叉连接最初是其中的一部分(参见编辑历史),但后来为了增加篇幅而被编辑到自己的帖子中。

返回
作者最近主题: