首页 > 其他分享 >Pandas的join和merge到底哪个快

Pandas的join和merge到底哪个快

时间:2023-01-28 17:40:58浏览次数:56  
标签:index join start merge 耗时 time Pandas


大家好,我是小小明。上次我们的云朵君同学在不严谨的测试下,得出了join可以比merge快5倍的结论。虽然默认参数用法下,join确实比merge快一些,但实际上join并不见得会比merge快。链接:《​​再见!不再使用 Pandas 中的 Merge 方法​​》

下面我们继续深入测试,首先构造如下生成测试数据的方法:

import pandas as pd
import numpy as np
from time import time

def create_df(col_prefix, nrows=1000_0000):
data = np.random.randint(0, 255, size=(nrows, 4))
df = pd.DataFrame(data, columns=map(f"{col_prefix}_{{}}".format, range(4)), dtype="uint8")
df["idx"] = df.index.values
return df

生成的测试数据格式如下:

print(create_df("c"))
c_0  c_1  c_2  c_3      idx
0 111 32 70 73 0
1 12 78 38 205 1
2 9 52 203 171 2
3 200 22 99 19 3
4 124 205 233 65 4
... ... ... ... ... ...
9999995 41 136 246 162 9999995
9999996 209 16 73 51 9999996
9999997 132 41 46 234 9999997
9999998 163 101 120 184 9999998
9999999 116 189 205 40 9999999

[10000000 rows x 5 columns]

默认生成1千万条数据,连接字段idx确保唯一。

初步测试

我们按照原文的逻辑简单测试一下:

df1 = create_df("x")
df2 = create_df("y")

start = time()
df1.merge(df2, on="idx")
print(f"merge耗时:{time() - start:.2f}s")

start = time()
df1.set_index("idx", inplace=True)
df2.set_index("idx", inplace=True)
df1.join(df2, how="inner")
print(f"join耗时:{time() - start:.2f}s")
merge 耗时:4.68s
join 耗时:1.13s

确实默认参数情况下join比merge快4倍。

join函数的本质

但是为什么join会比merge快呢?pandas开发者总不能开发两套完全不相同的处理逻辑把?所以我们点开源码一探究竟。

下面我的pandas环境是基于1.5.1版本进行测试。

这里我使用pycharm通过Ctrl+点击对应函数查看源码,首先看看join的核心源码:

def join(
self,
other: DataFrame | Series | list[DataFrame | Series],
on: IndexLabel | None = None,
how: str = "left",
lsuffix: str = "",
rsuffix: str = "",
sort: bool = False,
validate: str | None = None,
) -> DataFrame:
return self._join_compat(
other,
on=on,
how=how,
lsuffix=lsuffix,
rsuffix=rsuffix,
sort=sort,
validate=validate,
)


def _join_compat(
self,
other: DataFrame | Series,
on: IndexLabel | None = None,
how: str = "left",
lsuffix: str = "",
rsuffix: str = "",
sort: bool = False,
validate: str | None = None,
):
from pandas.core.reshape.merge import merge
return merge(
self,
other,
left_on=on,
how=how,
left_index=on is None,
right_index=True,
suffixes=(lsuffix, rsuffix),
sort=sort,
validate=validate,
)

咦,join不就是最终依然调用merge方法吗?

再看看merge的源码:

def merge(
self,
right: DataFrame | Series,
how: str = "inner",
on: IndexLabel | None = None,
left_on: IndexLabel | None = None,
right_on: IndexLabel | None = None,
left_index: bool = False,
right_index: bool = False,
sort: bool = False,
suffixes: Suffixes = ("_x", "_y"),
copy: bool = True,
indicator: bool = False,
validate: str | None = None,
) -> DataFrame:
from pandas.core.reshape.merge import merge
return merge(
self,
right,
how=how,
on=on,
left_on=left_on,
right_on=right_on,
left_index=left_index,
right_index=right_index,
sort=sort,
suffixes=suffixes,
copy=copy,
indicator=indicator,
validate=validate,
)

merge也是调用了​​pandas.core.reshape.merge​​中的merge方法。

**结论:**join本质上是merge传入了left_index和right_index为True。

再次测试

基于上述结论,我们再次测试一下:

df1 = create_df("x")
df2 = create_df("y")

df1.set_index("idx", inplace=True)
df2.set_index("idx", inplace=True)
start = time()
df1.merge(df2, on="idx")
print(f"merge on耗时:{time() - start:.2f}s")

start = time()
df1.merge(df2, left_index=True, right_index=True)
print(f"merge index耗时:{time() - start:.2f}s")

start = time()
df1.join(df2, how="inner")
print(f"join耗时:{time() - start:.2f}s")
merge on耗时:4.30s
merge index耗时:1.15s
join耗时:0.11s

merge index依然比merge on快了4倍,可是join为啥又比merge index快10倍?按照前面的分析,join多调用了2层,应该比merge index更慢才对呀?难道是因为缓存?

现在我们调换一下merge index和join的执行顺序再试试:

df1 = create_df("x")
df2 = create_df("y")
df1.set_index("idx", inplace=True)
df2.set_index("idx", inplace=True)

start = time()
df1.join(df2, how="inner")
print(f"join耗时:{time() - start:.2f}s")

start = time()
df1.merge(df2, left_index=True, right_index=True)
print(f"merge index耗时:{time() - start:.2f}s")
join耗时:1.12s
merge index耗时:0.13s

结果这次变成merge index比join快10倍了。那么我们干脆让数据都重新生成后再测试:

df1 = create_df("x")
df2 = create_df("y")
df1.set_index("idx", inplace=True)
df2.set_index("idx", inplace=True)
start = time()
df1.join(df2, how="inner")
print(f"join耗时:{time() - start:.2f}s")

df1 = create_df("x")
df2 = create_df("y")
df1.set_index("idx", inplace=True)
df2.set_index("idx", inplace=True)
start = time()
df1.merge(df2, left_index=True, right_index=True)
print(f"merge index耗时:{time() - start:.2f}s")
join耗时:1.21s
merge index耗时:1.10s

这次相差不大,merge index比join略快一点,符合对源码的认知。但是明显第二次join或merge时,会比第一次快10倍,初步推测肯定是存在某种缓存。

缓存存在的位置

在join函数打上断点后,一步步内层跟,发现如下缓存位置:

Pandas的join和merge到底哪个快_python

而self是​​Int64Index​​对象,说明索引对象缓存是否具备唯一性的属性。索引是否具备唯一性决定了索引连接的形式:

Pandas的join和merge到底哪个快_数据分析_02

我们可以再对比前先判断索引唯一性再测试:

df1 = create_df("x")
df2 = create_df("y")
df1.set_index("idx", inplace=True)
df2.set_index("idx", inplace=True)
start = time()
print(df1.index.is_unique, df2.index.is_unique)
print(f"索引判断唯一性耗时:{time() - start:.2f}s")

time1s = []
time2s = []
repeat = 10
for _ in range(repeat):
start = time()
df1.join(df2)
time2s.append(time() - start)

start = time()
df1.merge(df2, left_index=True, right_index=True)
time1s.append(time() - start)

print(f"merge index耗时:{sum(time1s) / repeat :.2f}s")
print(f"join耗时:{sum(time2s) / repeat :.2f}s")
True True
索引判断唯一性耗时:0.99s
merge index耗时:0.12s
join耗时:0.12s

可以看到索引判断唯一性耗时占了大头,取消这部分耗时后,两者耗时相差无几。

索引不唯一时速度对比

以上对比都是基于索引唯一的情景下,这次我们重新设计数据,假设索引存在五分之一的重复:

import pandas as pd
import numpy as np
from time import time


def create_df(col_prefix, nrows=100_0000):
data = np.random.randint(0, 255, size=(nrows, 4))
df = pd.DataFrame(data, columns=map(f"{col_prefix}_{{}}".format, range(4)))
df["idx"] = np.random.choice(np.arange(nrows * 4 // 5), nrows)
return df

再次测试:

start = time()
df1.merge(df2, on="idx")
print(f"merge on耗时:{time() - start:.2f}s")

df1.set_index("idx", inplace=True)
df2.set_index("idx", inplace=True)
start = time()
print(df1.index.is_unique, df2.index.is_unique)
print(f"索引判断唯一性耗时:{time() - start:.2f}s")

start = time()
df1.merge(df2, left_index=True, right_index=True)
print(f"merge index耗时:{time() - start:.2f}s")

start = time()
df1.join(df2, how="inner")
print(f"join耗时:{time() - start:.2f}s")

df1.reset_index(inplace=True)
df2.reset_index(inplace=True)
start = time()
df1.merge(df2, on="idx")
print(f"merge on耗时:{time() - start:.2f}s")
merge on耗时:0.53s
False False
索引判断唯一性耗时:0.06s
merge index耗时:0.91s
join耗时:0.93s
merge on耗时:0.50s

可以看到,在索引存在重复时,直接merge on反而更快。

但是不对劲啊,云朵君的测试明显是基于索引重复的情况下,得到join比merge on快的结论。其实这是因为云朵君的测试数据中,数据的重复度过大导致的,并不符合实际的数据情况。

云朵君给出的测试数据中,索引几乎达到100倍的重复,就是一个id能够重复出现100次左右。假如我们的测试也调整到10倍的索引重复度差距,结论就会不一样:

import pandas as pd
import numpy as np
from time import time


def create_df(col_prefix, nrows=100_0000):
data = np.random.randint(0, 255, size=(nrows, 4))
df = pd.DataFrame(data, columns=map(f"{col_prefix}_{{}}".format, range(4)))
df["idx"] = np.random.choice(np.arange(nrows // 10), nrows)
return df


df1 = create_df("x")
df2 = create_df("y")

start = time()
df1.merge(df2, how="left", on="idx")
print(f"merge on耗时:{time() - start:.2f}s")

df1.set_index("idx", inplace=True)
df2.set_index("idx", inplace=True)
start = time()
df1.join(df2)
print(f"join耗时:{time() - start:.2f}s")

df1.reset_index(inplace=True)
df2.reset_index(inplace=True)
start = time()
df1.merge(df2, how="left", on="idx")
print(f"merge on耗时:{time() - start:.2f}s")
merge on耗时:1.58s
join耗时:1.42s
merge on耗时:1.64s

很明显的看到这时join就比merge on快了。

如果提升到50倍:

merge on耗时:6.02s
join耗时:4.16s
merge on耗时:6.95s

结论

  1. join等价于merge传入left_index和right_index参数为True
  2. 索引唯一时,join比merge默认的on指定字段速度快
  3. 索引不唯一时,少量重复的索引时,merge默认的on指定字段比join快;每个索引能够重复10次以上时,join比merge默认的on指定字段快


标签:index,join,start,merge,耗时,time,Pandas
From: https://blog.51cto.com/u_11866025/6025017

相关文章

  • join与split
    pythonjoin和split方法简单的说是:join用来连接字符串,split恰好相反,拆分字符串的。join将容器对象拆分并以指定的字符将列表内的元素(element)连接起来,返回字符串(注:容......
  • sql基础之from和ansi join一起使用(parse)总结
    createtabletemp1(aidVARCHAR(5)notnull,carVARCHAR(10)notnull);createtabletemp2(bidVARCHAR(5)notnull,usernameVARCH......
  • java多线程基础小白指南--关键字识别(start,run,sleep,wait,join,yield)
    在学习java多线程基础上,会遇到几个关键字,理解并识别它们是掌握多线程的必备知识,下面,我将通过源码或者程序演示给出我对这几个关键字的理解,如果有不同意见,欢迎在评论区或者......
  • 这20个Pandas函数可以完成80%的数据科学工作
    Pandas是数据科学社区中使用最广泛的库之一,它是一个强大的工具,可以进行数据操作、清理和分析。本文将提供最常用的Pandas函数以及如何实际使用它们的样例。我们将涵盖从......
  • Hive SQL Join关联查询Apache Hadoop概述Hadoop YARN架构、组件及其交互流程Apache Hi
    Hadoop离线是大数据生态圈的核心与基石,是整个大数据开发的入门。本次分享内容让初学者能高效、快捷掌握Hadoop必备知识,大大缩短Hadoop离线阶段学习时间,下面一起开始今天的学......
  • sql基础之from和ansi join一起使用(parse)总结
    createtabletemp1(aidVARCHAR(5)notnull,carVARCHAR(10)notnull);createtabletemp2(bidVARCHAR(5)notnull,username......
  • 并查集(Disjoint-Set)
    并查集目录并查集定义模板题目原题链接题目描述输入格式输出格式数据范围分析并查集基本原理实现步骤问题1问题2问题3优化代码实现定义并查集(Disjoint-Set)是一种可以......
  • 多表查询与7种JOINS的实现
    多表查询,也称为关联查询,指两个或更多个表一起完成查询操作。前提条件:这些一起查询的表之间是有关系的(一对一、一对多),它们之间一定是有关联字段,这个关联字段可能建立了外键,也......
  • 【python】pandas库学习笔记
    北京理工大学嵩天Pandas课程学习笔记。部分内容补充自菜鸟教程。Pandas库提供了共性能易用数据类型和分析工具的第三方python库。Pandas库基于Numpy库实现。Pandas......
  • gitlab Merge Requests操作流程
    引用https://juejin.cn/post/6993548883729055780一、配置受保护的分支创建一个分支例如prd将prd分支也设置为protect分支配置保护分支二、提交者MergeRequests......