pandas df.apply意外地改变了数据帧

Ped*_*lho 9 python dataframe pandas pandas-apply

根据我的理解,pandas.DataFrame.apply不会在内部应用更改,我们应该使用其返回对象来保留任何更改.但是,我发现了以下不一致的行为:

让我们应用一个虚函数,以确保原始df保持不变:

>>> def foo(row: pd.Series):
...     row['b'] = '42'

>>> df = pd.DataFrame([('a0','b0'),('a1','b1')], columns=['a', 'b'])
>>> df.apply(foo, axis=1)
>>> df
    a   b
0   a0  b0
1   a1  b1
Run Code Online (Sandbox Code Playgroud)

这表现得如预期.但是,如果我们修改初始化此df的方式,foo将在适用的位置应用更改:

>>> df2 = pd.DataFrame(columns=['a', 'b'])
>>> df2['a'] = ['a0','a1']
>>> df2['b'] = ['b0','b1']
>>> df2.apply(foo, axis=1)
>>> df2
    a   b
0   a0  42
1   a1  42
Run Code Online (Sandbox Code Playgroud)

我还注意到,如果列dtypes不是'object'类型,则上述情况不正确.为什么apply()在这两个上下文中表现不同?

Python:3.6.5

熊猫:0.23.1

Max*_*sky 5

有趣的问题!我相信您所看到的行为是您使用方式的产物apply

正如您正确指出的那样,apply它不旨在用于修改数据框。但是,由于apply采用任意函数,因此不能保证应用该函数是幂等的并且不会更改数据帧。在这里,您发现了该行为的一个很好的示例,因为您的函数foo尝试修改它所传递的行apply

使用apply修改行可能会导致这些副作用。这不是最佳实践。

相反,请考虑这种惯用的方法apply。该函数apply通常用于创建新列。下面是一个典型使用方式的示例apply,我相信它会引导您远离这个潜在的麻烦区域:

import pandas as pd
# construct df2 just like you did
df2 = pd.DataFrame(columns=['a', 'b'])
df2['a'] = ['a0','b0']
df2['b'] = ['a1','b1']

df2['b_copy'] = df2.apply(lambda row: row['b'], axis=1) # apply to each row
df2['b_replace'] = df2.apply(lambda row: '42', axis=1) 
df2['b_reverse'] = df2['b'].apply(lambda val: val[::-1]) # apply to each value in b column

print(df2)

# output:
#     a   b b_copy b_replace b_reverse
# 0  a0  a1     a1        42        1a
# 1  b0  b1     b1        42        1b
Run Code Online (Sandbox Code Playgroud)

请注意,pandas 将一行或一个单元格传递给您作为第一个参数提供的函数apply,然后将函数的输出存储在您选择的列中。

如果您想逐行修改数据框,请查看iterrowsloc以获得最惯用的路线。