【问题标题】:Pandas: peculiar performance drop for inplace rename after dropnaPandas:在 dropna 后就地重命名的特殊性能下降
【发布时间】:2014-04-27 06:12:28
【问题描述】:

我已在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方法恢复了预期的性能:

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

【问题讨论】:

    标签: python performance pandas in-place


    【解决方案1】:

    这是复制github上的解释。

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

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

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

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

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

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

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

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

    【讨论】:

    • “我从不使用就地操作。语法更难阅读,而且没有任何优势。”有趣的一点。我以后应该考虑这个。 .copy() 的建议确实解决了这个问题。感谢您详细及时的回复!
    • 我这么说的原因是 pandas 操作的核心是链式的,每个操作都返回一个副本,例如 df.dropna().rename(....).sum() 非常直观/可读。当您注入就地操作时,您无法链接。
    • 我不会说这种语法没有任何优势——它可以让你避免在等号两边都放长的规范。这是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 的优势的一种变体。
    • @DSM 公平点;我通常只使用一个临时变量,比如mask,其含义很明确。 (尽管在您的示例中它实际上不需要在 rhs 上,因为框架将对齐,例如您可以简单地使用:some_long_complicated_expression + 1(尽管它们可能会影响性能)
    • 不争论整体观点,只是想问一个可能很幼稚的问题,当你说,[“语法更难阅读,它没有任何优势,”] 如果它真的做了什么到位并且它是巨大的内存效率不是积极的吗?假设在本地运营?
    猜你喜欢
    • 1970-01-01
    • 2017-07-22
    • 2014-02-16
    • 2015-02-09
    • 1970-01-01
    • 2021-12-03
    • 1970-01-01
    • 1970-01-01
    • 2018-03-18
    相关资源
    最近更新 更多