熊猫:dropna之后的原地重命名表现特别下降

eld*_*d-a 32 python performance in-place pandas

我已将此报告为大熊猫问题.与此同时,我发布此处希望节省其他时间,以防他们遇到类似的问题.

在分析需要优化的进程时,我发现重命名列不在适当位置可以提高x120的性能(执行时间).分析表明这与垃圾收集有关(见下文).

此外,通过避免使用dropna方法来恢复预期的性能.

以下简短示例演示了因子x12:

import pandas as pd
import numpy as np
Run Code Online (Sandbox Code Playgroud)

就地=真

%%timeit
np.random.seed(0)
r,c = (7,3)
t = np.random.rand(r)
df1 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
indx = np.random.choice(range(r),r/3, replace=False)
t[indx] = np.random.rand(len(indx))
df2 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
df = (df1-df2).dropna()
## inplace rename:
df.rename(columns={col:'d{}'.format(col) for col in df.columns}, inplace=True)
Run Code Online (Sandbox Code Playgroud)

100个循环,最佳3:每循环15.6毫秒

第一输出线%%prun:

ncalls tottime percall cumtime percall filename:lineno(function)

1  0.018 0.018 0.018 0.018 {gc.collect}
Run Code Online (Sandbox Code Playgroud)

就地=假

%%timeit
np.random.seed(0)
r,c = (7,3)
t = np.random.rand(r)
df1 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
indx = np.random.choice(range(r),r/3, replace=False)
t[indx] = np.random.rand(len(indx))
df2 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
df = (df1-df2).dropna()
## avoid inplace:
df = df.rename(columns={col:'d{}'.format(col) for col in df.columns})
Run Code Online (Sandbox Code Playgroud)

1000循环,最佳3:每循环1.24毫秒

避免拉网

通过避免以下dropna方法恢复预期的性能:

%%timeit
np.random.seed(0)
r,c = (7,3)
t = np.random.rand(r)
df1 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
indx = np.random.choice(range(r),r/3, replace=False)
t[indx] = np.random.rand(len(indx))
df2 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
#no dropna:
df = (df1-df2)#.dropna()
## inplace rename:
df.rename(columns={col:'d{}'.format(col) for col in df.columns}, inplace=True)
Run Code Online (Sandbox Code Playgroud)

1000个循环,最佳3:每循环865μs

%%timeit
np.random.seed(0)
r,c = (7,3)
t = np.random.rand(r)
df1 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
indx = np.random.choice(range(r),r/3, replace=False)
t[indx] = np.random.rand(len(indx))
df2 = pd.DataFrame(np.random.rand(r,c), columns=range(c), index=t)
## no dropna
df = (df1-df2)#.dropna()
## avoid inplace:
df = df.rename(columns={col:'d{}'.format(col) for col in df.columns})
Run Code Online (Sandbox Code Playgroud)

1000个循环,最佳3:902μs/循环

Jef*_*eff 47

这是关于github的解释的副本.

没有保证,一个inplace操作实际上要快.通常它们实际上是在副本上运行的相同操作,但是重新分配了顶级引用.

在这种情况下性能差异的原因如下.

(df1-df2).dropna()调用创建了一个数据帧切片.当您应用新操作时,会触发SettingWithCopy检查,因为它可能是副本(但通常不是).

此检查必须执行垃圾收集以清除某些缓存引用以查看它是否为副本.不幸的是,python语法使这不可避免.

你不能通过简单地制作副本来实现这一点.

df = (df1-df2).dropna().copy()
Run Code Online (Sandbox Code Playgroud)

随后的inplace操作将与以前一样高效.

我的个人意见:我从不使用就地操作.语法更难阅读,它没有任何优势.

  • 我说这个的原因是pandas操作的核心是链接,每个操作返回一个副本,例如``df.dropna().rename(....).sum()``非常直观/可读.当您注入就地操作时,您无法链接. (8认同)
  • 我不会说语法没有任何优势 - 它允许你避免在等号的两边放置长规格.它的优点是`some_long_complicated_expression [some:long_slice,more_information_here] + = 1`超过`some_long_complicated_expression [some:long_slice,more_information_here] = some_long_complicated_expression [some:long_slice,more_information_here] + 1`. (8认同)