在熊猫中,inplace = True 是否有害?
- 2024-12-25 08:51:00
- admin 原创
- 142
问题描述:
这个问题之前已经讨论过,但答案相互矛盾:
就地就好!
就地不好!
我想知道的是:
为什么是
inplace = False
默认行为?什么时候更改它比较好?(好吧,我被允许更改它,所以我想这是有原因的)。
这是一个安全问题吗?也就是说,操作是否会因……而失败/出现错误
inplace = True
?我能否提前知道某项
inplace = True
操作是否“真正”会现场执行?
我目前的看法是:
许多 Pandas 操作都有一个
inplace
参数,始终默认为False
,这意味着原始 DataFrame 不受影响,并且该操作返回一个新的 DF。设置时
inplace = True
,操作可能在原始 DF 上起作用,但它可能仍在后台的副本上起作用,并且在完成后只需重新分配引用。
优点inplace = True
:
可以更快,更少占用内存(第一个链接显示
reset_index()
运行速度快两倍,但使用峰值内存只有一半!)。
优点inplace = False
:
允许链式/功能性语法:
df.dropna().rename().sum()...
这很好,并且提供了惰性求值或更有效的重新排序的机会(尽管我不认为 Pandas 正在这样做)。inplace = True
当在可能为底层 DF 的切片/视图的对象上使用时,Pandas 必须进行SettingWithCopy
检查,这是很昂贵的。inplace = False
避免这种情况。幕后一致且可预测的行为。
因此,抛开复制与查看问题不谈,inplace = True
除非专门编写链式语句,否则始终使用 似乎性能更高。但这不是 Pandas 的默认选择,那么我遗漏了什么?
解决方案 1:
在熊猫中,inplace = True 是否有害?
是的,它确实有害。它不只是有害。它非常有害。这个 GitHub 问题建议inplace
在不久的将来在 API 范围内弃用该参数。简而言之,该inplace
参数的所有错误如下:
inplace
与名称所暗示的相反,通常不会阻止创建副本,并且(几乎)不会带来任何性能优势inplace
不适用于方法链inplace
在调用 DataFrame 列时可能会导致可怕的结果SettingWithCopyWarning
,有时可能无法就地更新列
上面的痛点都是初学者常见的陷阱,因此删除此选项将大大简化 API。
我们将更深入地探讨上述观点。
性能
一个常见的误解是使用inplace=True
会导致更高效或优化的代码。通常,使用不会带来任何性能优势inplace=True
(但也有少数例外,这主要是库中的实现细节的结果,不应被用作支持此论点的借口)。方法的大多数就地和非就地版本无论如何都会创建数据的副本,而就地版本会自动将副本分配回去。无法避免复制。
方法链式调用
inplace=True
也会阻碍方法链式调用。对比
result = df.some_function1().reset_index().some_function2()
相反
temp = df.some_function1()
temp.reset_index(inplace=True)
result = temp.some_function2()
意想不到的陷阱
最后一个需要记住的警告是,呼叫inplace=True
可能会触发SettingWithCopyWarning
:
df = pd.DataFrame({'a': [3, 2, 1], 'b': ['x', 'y', 'z']})
df2 = df[df['a'] > 1]
df2['b'].replace({'x': 'abc'}, inplace=True)
# SettingWithCopyWarning:
# A value is trying to be set on a copy of a slice from a DataFrame
这可能会导致意外行为。
解决方案 2:
如果inplace
是默认值,那么 DataFrame 将会根据当前引用它的所有名称发生变异。
一个简单的例子,假设我有一个df
:
df = pd.DataFrame({'a': [3, 2, 1], 'b': ['x', 'y', 'z']})
现在,DataFrame 保留行顺序非常重要 - 假设它来自插入顺序是关键的数据源。
但是,我现在需要执行一些需要不同排序顺序的操作:
def f(frame):
df = frame.sort_values('a')
# if we did frame.sort_values('a', inplace=True) here without
# making it explicit - our caller is going to wonder what happened
# do something
return df
没问题 - 我的原始值df
保持不变。但是,如果inplace=True
是默认设置,那么我的原始值df
现在将被排序为副作用,f()
其中我必须相信调用者会记住不要在我不期望的位置执行某些操作,而不是故意在位置执行某些操作...因此,最好是任何可以就地改变对象的东西都明确地这样做,至少可以更清楚地了解发生了什么以及为什么发生。
即使使用基本的 Python 内置可变变量,你也可以观察到这一点:
data = [3, 2, 1]
def f(lst):
lst.sort()
# I meant lst = sorted(lst)
for item in lst:
print(item)
f(data)
for item in data:
print(item)
# huh!? What happened to my data - why's it not 3, 2, 1?