大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
操作将像以前一样高性能。
我个人的看法:我从来不使用就地操作。 语法很难阅读,并没有提供任何优势。