Pandas 合并 101

2024-11-15 08:37:00
admin
原创
24
摘要:问题描述:我怎样才能用熊猫表演( INNER|( LEFT|| ) ) ?RIGHT`FULLOUTERJOIN`合并后如何为缺失的行添加 NaN?合并后如何摆脱 NaN?我可以在索引上合并吗?如何合并多个 DataFrame?与 Pandas 交叉连接merge?????join谁concat?update...

问题描述:

  • 我怎样才能用熊猫表演( INNER|( LEFT|| ) ) ?RIGHT`FULLOUTERJOIN`

  • 合并后如何为缺失的行添加 NaN?

  • 合并后如何摆脱 NaN?

  • 我可以在索引上合并吗?

  • 如何合并多个 DataFrame?

  • 与 Pandas 交叉连接

  • merge?????joinconcatupdate什么?为什么?!

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

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

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


目录

为了方便访问。

  • 合并基础 - 连接的基本类型(先阅读此内容)

  • 基于索引的连接

  • 推广到多个 DataFrames

  • 交叉连接


解决方案 1:

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

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

  • 基础知识 - 连接类型(LEFT、RIGHT、OUTER、INNER)

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

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

  • 与性能相关的讨论和时间安排(目前)。在适当的情况下,主要提到了更好的替代方案。

  • 处理后缀、删除多余的列、重命名输出和其他特定用例。还有其他(阅读:更好的)帖子处理这些,所以弄清楚吧!

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

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

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



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

IT科技

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

  • 蓝色表示合并结果中存在的行

  • 红色表示从结果中排除的行(即删除)

  • 绿色NaN表示结果中用 s替换的缺失值

要执行 INNER JOIN,请调用merge左侧 DataFrame,并指定右侧 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

这将仅返回共享公共键的行(在此示例中为“B”和“D”)leftright

LEFT OUTER JOIN或 LEFT JOIN 表示为

IT科技
这可以通过指定来执行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'的键,并且中的缺失数据将替换为 NaN。left`right`

类似地,对于RIGHT OUTER JOIN或 RIGHT JOIN 来说......

IT科技
...指定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,由以下公式给出

IT科技
指定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-Exclusion、RIGHT-Exclusion 和 FULL-Exclusion/ANTI JOIN

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

对于 LEFT-Exclusive JOIN,表示为

IT科技
首先执行 LEFT OUTER JOIN,然后筛选出left仅来自以下位置的行(排除右侧的所有内容),

(left.merge(right, on='key', how='left', indicator=True)
     .query('_merge == "left_only"')
     .drop('_merge', axis=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,

IT科技

(left.merge(right, on='key', how='right', indicator=True)
     .query('_merge == "right_only"')
     .drop('_merge', axis=1))

  key  value_x   value_y
2   E      NaN  0.950088
3   F      NaN -0.151357

最后,如果您需要执行仅保留左侧或右侧键但不同时保留两者的合并(换句话说,执行ANTI-JOIN),

IT科技
你可以用类似的方式做到这一点 -

(left.merge(right, on='key', how='outer', indicator=True)
     .query('_merge != "both"')
     .drop('_merge', axis=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

关键列的不同名称

如果键列的名称不同(例如,lefthaskeyLeftrighthaskeyRight而不是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

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

keyLeft当根据来自leftkeyRight来自进行合并时right,如果您只希望输出中包括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

如果您只需要合并“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_onright_on,视情况而定)的列表。

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

或者,如果名称不同,

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

其他有用的merge*操作和功能

  • 按索引将 DataFrame 与 Series 合并:请参阅此答案。

  • 此外mergeDataFrame.updateDataFrame.combine_first在某些情况下也用于用一个 DataFrame 来更新另一个 DataFrame。

  • pd.merge_ordered对于有序 JOIN 来说,这是一个有用的函数。

  • pd.merge_asof(阅读:merge_asOf) 对于近似连接很有用。

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



继续阅读

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

  • 合并基础 - 基本连接类型 *

  • 基于索引的连接

  • 推广到多个 DataFrames

  • 交叉连接

*您在这里。

解决方案 2:

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


![在 pd.concat([df0, df1]) 上](https://i.sstatic.net/1rb1R.jpg)

解决方案 3:

加入 101

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

内连接

在此处输入图片描述

外连接或全连接

在此处输入图片描述

右连接

在此处输入图片描述

左连接

在此处输入图片描述

解决方案 4:

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

  1. pandas.concat

  2. pandas.DataFrame.merge合并一个索引和另一个列的数据框。

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


1.pandas.concat

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

  • 价格2018含尺寸(8784, 5)

   Year  Month  Day  Hour  Price
0  2018      1    1     1   6.74
1  2018      1    1     2   4.74
2  2018      1    1     3   3.66
3  2018      1    1     4   2.30
4  2018      1    1     5   2.30
5  2018      1    1     6   2.06
6  2018      1    1     7   2.06
7  2018      1    1     8   2.06
8  2018      1    1     9   2.30
9  2018      1    1    10   2.30
  • 价格2019含尺寸(8760, 5)

   Year  Month  Day  Hour  Price
0  2019      1    1     1  66.88
1  2019      1    1     2  66.88
2  2019      1    1     3  66.00
3  2019      1    1     4  63.64
4  2019      1    1     5  58.85
5  2019      1    1     6  55.47
6  2019      1    1     7  56.00
7  2019      1    1     8  61.09
8  2019      1    1     9  61.01
9  2019      1    1    10  61.00

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

import pandas as pd

frames = [Price2018, Price2019]

df_merged = pd.concat(frames)

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

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

concat 的工作原理

(来源)


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')

其中GeoPrice是先前的数据框。

得出以下数据框

   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

解决方案 5:

本文将讨论以下主题:

  • 不同条件下与索引合并

    • 基于索引的连接选项:merge,,join`concat`

    • 索引合并

    • 合并一个索引,另一个列

  • 有效地使用命名索引来简化合并语法

返回顶部



基于索引的连接

总结

有几个选项,根据使用情况,有些选项比其他选项更简单。

  1. DataFrame.merge使用left_indexright_index(或left_onright_on使用命名索引)

    • 支持内/左/右/全

    • 一次只能加入两个

    • 支持列-列、索引-列、索引-索引连接

  2. DataFrame.join(按索引加入)

    • 支持内/左(默认)/右/全

    • 可以一次连接多个 DataFrame

    • 支持索引索引连接

  3. pd.concat(按索引连接)

    • 支持内部/完整(默认)

    • 可以一次连接多个 DataFrame

    • 支持索引索引连接


索引到索引连接

设置与基础知识

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. 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
  1. 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,请参阅此帖子。


索引到列的连接

要使用左索引、右列执行内连接,您将使用和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]

如果您的索引已命名,那么从 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_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 中的其他主题继续学习:

  • 合并基础 - 连接的基本类型

  • 基于索引的连接*

  • 推广到多个 DataFrames

  • 交叉连接

  • 你在这里

解决方案 6:

本文将讨论以下主题:

  • 如何正确地推广到多个 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在索引上连接 DataFrames

# 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

对于 FULL OUTER JOIN 则省略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在索引merge上连接 DataFrames;除非另有说明,否则它会在后台调用并执行 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 中的其他主题继续学习:

  • 合并基础 - 连接的基本类型

  • 基于索引的连接

  • 推广到多个 DataFrames *

  • 交叉连接

  • 你在这里

解决方案 7:

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

# 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

解决方案 8:

我认为你应该在你的解释中包括这一点,因为这是我经常看到的相关合并,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', axis=1)

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

df_合并:

      A     B      C      D
0  Jack  Jill  Tommy  Tammy
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用