大pandas:特殊的性能下降后,在dropna重新命名

我已经把这个报告为pandas问题的一个问题 。 同时我在这里张贴这个希望能节省时间,以防遇到类似的问题。

在分析一个需要优化的进程时,我发现重命名不在位的列会提高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 ms

%%prun第一个输出行:

ncalls tottime percall cumtime percall文件名:lineno(函数)

 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 ms

避免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

这是github的解释副本。

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

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

(df1-df2).dropna()调用创builddataframe的一个片段。 当你应用一个新的操作时,这会触发一个SettingWithCopy检查,因为它可能是一个副本(但通常不是)。

此检查必须执行垃圾回收来清除一些caching引用,以查看它是否是副本。 不幸的是Python语法使得这个不可避免。

通过简单地制作一个副本,你不可能发生这种情况。

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

其次是一个inplace操作将像以前一样高性能。

我个人的看法:我从来不使用就地操作。 语法很难阅读,并没有提供任何优势。