Pandas 合并 101
- 2024-11-15 08:37:00
- admin 原创
- 25
问题描述:
我怎样才能用熊猫表演(
INNER
|(LEFT
|| ) ) ?RIGHT
`FULLOUTER
JOIN`合并后如何为缺失的行添加 NaN?
合并后如何摆脱 NaN?
我可以在索引上合并吗?
如何合并多个 DataFrame?
与 Pandas 交叉连接
merge
?????join
谁concat
?update
什么?为什么?!
... 等等。我看到这些反复出现的问题询问 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表示为
注意
:本图表以及接下来的图表均遵循以下惯例:
蓝色表示合并结果中存在的行
红色表示从结果中排除的行(即删除)
绿色
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”)left
。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'
的键,并且中的缺失数据将替换为 NaN。left
`right`
类似地,对于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-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', 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,
(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),
你可以用类似的方式做到这一点 -
(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
关键列的不同名称
如果键列的名称不同(例如,left
haskeyLeft
和right
haskeyRight
而不是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 操作时可能很重要。
仅合并其中一个列中的一列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_on
和right_on
,视情况而定)的列表。
left.merge(right, on=['key1', 'key2'] ...)
或者,如果名称不同,
left.merge(right, left_on=['lkey1', 'lkey2'], right_on=['rkey1', 'rkey2'])
其他有用的merge*
操作和功能
按索引将 DataFrame 与 Series 合并:请参阅此答案。
此外
merge
,DataFrame.update
和DataFrame.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=0
oraxis=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:
在这个回答中,我将考虑以下实际例子:
pandas.concat
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)
如果想清楚地了解发生了什么,可以这样做
(来源)
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
解决方案 5:
本文将讨论以下主题:
不同条件下与索引合并
基于索引的连接选项:
merge
,,join
`concat`索引合并
合并一个索引,另一个列
有效地使用命名索引来简化合并语法
返回顶部
基于索引的连接
总结
有几个选项,根据使用情况,有些选项比其他选项更简单。
DataFrame.merge
使用left_index
和right_index
(或left_on
和right_on
使用命名索引)
支持内/左/右/全
一次只能加入两个
支持列-列、索引-列、索引-索引连接
DataFrame.join
(按索引加入)
支持内/左(默认)/右/全
可以一次连接多个 DataFrame
支持索引索引连接
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
其他连接遵循类似的语法。
值得注意的替代方案
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
请注意,我需要指定lsuffix
和rsuffix
参数,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
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_on
和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 中的其他主题继续学习:
合并基础 - 连接的基本类型
基于索引的连接*
推广到多个 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
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件