43

我已将此报告为pandas issues上的一个问题。与此同时,我在这里发布这个希望节省其他人的时间,以防他们遇到类似的问题。

在分析需要优化的进程时,我发现重命名列未就地提高 x120 的性能(执行时间)。分析表明这与垃圾收集有关(见下文)。

此外,通过避免 dropna 方法恢复了预期的性能。

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

import pandas as pd
import numpy as np

就地=真

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

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

第一个输出行%%prun

ncalls tottime percall cumtime percall filename:lineno(function)

1  0.018 0.018 0.018 0.018 {gc.collect}

就地=假

%%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})

1000 次循环,3 次中的最佳:每个循环 1.24 毫秒

避免dropna

通过避免使用以下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)

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

1000 个循环,3 个循环中的最佳:每个循环 902 µs

4

1 回答 1

69

这是github上解释的副本。

不能保证操作inplace实际上更快。通常它们实际上是在副本上工作的相同操作,但重新分配了顶级引用。

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

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

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

你不可能发生这种情况,只需先制作一个副本。

df = (df1-df2).dropna().copy()

接下来的inplace操作将与以前一样高效。

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

于 2014-03-20T12:30:32.710 回答