熊猫 - 就位=真正被认为有害或无效?

Ome*_*erB 27 python pandas

这已在前面讨论过,但答案相互矛盾:

我想知道的是:

  • 为什么是inplace = False默认行为?
  • 什么时候改变呢?(好吧,我被允许改变它,所以我猜这是有原因的).
  • 这是安全问题吗?也就是说,操作失败/行为失误是由于inplace = True
  • 我是否可以提前知道某项inplace = True操作是否"真正"就地进行?

我到目前为止:

  • 许多Pandas操作都有一个inplace参数,总是默认为False,意味着原始的DataFrame不受影响,并且操作返回一个新的DF.
  • 设置时inplace = True,操作可能适用于原始DF,但它可能仍在幕后复制,只需在完成后重新分配参考.

专业人士inplace = False:

  • 允许链接/功能语法:df.dropna().rename().sum()...这很好,并提供延迟评估或更有效的重新排序的机会(虽然我不认为Pandas正在这样做).
  • inplace = True在可能是底层DF的切片/视图的对象上使用时,Pandas必须进行SettingWithCopy检查,这是昂贵的.inplace = False避免这种情况.
  • 幕后一致且可预测的行为.

专业人士inplace = True:

  • 可以更快,更少内存占用(第一个链接显示reset_index()运行速度快两倍,并使用峰值内存的一半!).

因此,inplace = True除了专门编写链式语句之外,将copy-vs-view问题放在一边,总是使用它似乎更高效.但这不是默认的熊猫选择,所以我错过了什么?

cs9*_*s95 50

在熊猫中, inplace = True 是否被认为有害?

是的。不仅有害。相当有害。这个 GitHub 问题提议inplace在不久的将来某个时候在 api-wide 范围内弃用该论点。简而言之,这里的inplace论点都是错误的:

  • inplace,与名称所暗示的相反,通常不会阻止创建副本,并且(几乎)从不提供任何性能优势
  • inplace 不适用于方法链
  • inplaceSettingWithCopyWarning在 DataFrame 列上调用时可能会导致可怕的情况,并且有时可能无法就地更新列

以上痛点都是初学者常见的陷阱,去掉这个选项会大大简化API。


我们更深入地看一下上面的几点。

性能
一个常见的误解是使用inplace=True将导致更高效或优化的代码。在一般情况下,有没有性能优势使用inplace=True。方法的大多数就地和非就地版本无论如何都会创建数据的副本,就地版本会自动将副本分配回来。副本无法避免。

方法链
inplace=True阻碍方法链接。对比工作

result = df.some_function1().reset_index().some_function2()
Run Code Online (Sandbox Code Playgroud)

temp = df.some_function1()
temp.reset_index(inplace=True)
result = temp.some_function2()
Run Code Online (Sandbox Code Playgroud)

意外陷阱
要记住的最后一个警告是,调用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
Run Code Online (Sandbox Code Playgroud)

这可能会导致意外行为。

  • 链式操作和就地操作本质上是不同的方法 - 您可以争论其中一种方法,但它们彼此不兼容的事实并不是任何一种方法的真正弱点(编辑:好吧,我想您可以设计 API 来执行操作到位并返回一个指向输入的指针。我想我明白你在说什么) (6认同)
  • 它阻碍了方法链接,因为当“inplace”为 True 时 API 返回 None。但这是 API 设计决策,而不是就地操作的固有限制。 (4认同)
  • FWIW Github 问题自 2017 年 5 月以来一直开放,有 41 条评论,所以它似乎并没有快速进展 (3认同)
  • @NicholasM 这是一个糟糕的 API 设计决策,这就是为什么它被考虑弃用。 (2认同)

Jon*_*nts 6

如果inplace是默认值,那么DataFrame将针对当前引用它的所有名称进行变异.

一个简单的例子,说我有一个df:

df = pd.DataFrame({'a': [3, 2, 1], 'b': ['x', 'y', 'z']})
Run Code Online (Sandbox Code Playgroud)

现在,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
Run Code Online (Sandbox Code Playgroud)

那没关系 - 我的原版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?     
Run Code Online (Sandbox Code Playgroud)

  • python引用系统是一个重要的提升点,个人我对当前的工作方法没有问题,但从我记得这个行为和numpy一样,所以它只是遵循这个语义+1 (2认同)
  • @OmerB不可变的数据帧对其使用是不切实际的。简而言之,我要说的是明确就位,您要让开发人员明确地说:“我知道我在做什么,并且我知道这可能会影响范围的后果”。这比反向更明智,并且必须知道您应该提供一个选项来阻止其他可能破坏的事情。 (2认同)