Python expndtw-1.apply意外地更改了在位的数据帧
据我所知,pandas.DataFrame.apply不会就地应用更改,我们应该使用其返回对象来持久化任何更改。但是,我发现以下不一致的行为: 为了确保原始df保持不变,让我们应用一个虚拟函数:Python expndtw-1.apply意外地更改了在位的数据帧,python,pandas,dataframe,pandas-apply,Python,Pandas,Dataframe,Pandas Apply,据我所知,pandas.DataFrame.apply不会就地应用更改,我们应该使用其返回对象来持久化任何更改。但是,我发现以下不一致的行为: 为了确保原始df保持不变,让我们应用一个虚拟函数: >>> def foo(row: pd.Series): ... row['b'] = '42' >>> df = pd.DataFrame([('a0','b0'),('a1','b1')], columns=['a', 'b']) >>>
>>> 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
这与预期的一样。但是,如果我们修改初始化此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
我还注意到,如果列数据类型不是“object”类型,则上述情况不成立。为什么apply()在这两种上下文中的行为不同
Python:3.6.5
熊猫:0.23.1有趣的问题!我相信您看到的行为是您使用
apply
方式的产物
正如您正确指出的,apply
不用于修改数据帧。但是,由于apply
采用任意函数,因此不能保证应用该函数将是幂等函数,并且不会更改数据帧。在这里,您已经找到了这种行为的一个很好的例子,因为您的函数foo
尝试修改它所传递的行apply
使用apply
修改行可能会导致这些副作用。这不是最好的做法
改为考虑<代码> >应用< /COD>的习惯用法。函数
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
请注意,pandas将一行或一个单元格作为apply
的第一个参数传递给函数,然后将函数的输出存储在您选择的列中
如果您想逐行修改数据帧,请查看
iterrows
和loc
以了解最惯用的路线。可能会晚些,但我认为这可能对解决此问题的人特别有帮助
当我们使用foo
时,如下所示:
def foo(row: pd.Series):
row['b'] = '42'
然后将其用于:
df.apply(foo, axis=1)
我们预计df不会发生任何变化,但会发生。为什么?
让我们回顾一下引擎盖下发生的事情:
apply
函数调用foo
并将一行传递给它。由于它不是python中特定类型的类型(如int、float、str等),而是一个对象,因此根据python规则,它是通过引用而不是通过值传递的。因此,它与apply
函数发送的行完全等效。(值相等,并且两个值都指向同一块ram。)
因此,在foo
函数中对行
的任何更改都将立即更改行
——其类型为pandas.series
,并指向df.row
驻留的内存块
我们可以重写foo
(我将其命名为bar
)函数,以不更改任何就地。(通过深度复制行
,这意味着在ram的另一个单元上创建具有相同值的另一行)。这是在apply
函数中使用lambda
时经常发生的情况
def bar(row: pd.Series):
row_temp=row.copy(deep=True)
row_temp['b'] = '42'
return row_temp
完整代码
将熊猫作为pd导入
#发生了变化——不像拉姆达
def foo(行:pd.系列):
行['b']='42'
#不要在原地更改df——就像lambda一样工作
def棒(世界其他地区:pd系列):
行临时=行复制(深=真)
行温度['b']='42'
返回行温度
df2=pd.DataFrame(列=['a','b'])
df2['a']=['a0','a1']
df2['b']=['b0','b1']
打印(df2)
#不变
df_b=df2.应用(杆,轴=1)
打印(df2)
#酒吧功能正常
打印(df_b)
打印(df2)
#就地变化
df2.应用(foo,轴=1)
打印(df2)
输出
#df2 before any change
a b
0 a0 b0
1 a1 b1
#calling df2.apply(bar, axis=1) not changed df2 inplace
a b
0 a0 b0
1 a1 b1
#df_b = df2.apply(bar, axis=1) #bar is working as expected
a b
0 a0 42
1 a1 42
#print df2 again to assure it is not changed
a b
0 a0 b0
1 a1 b1
#call df2.apply(foo, axis=1) -- as we see foo changed df2 inplace ( to compare with bar)
a b
0 a0 42
1 a1 42
您正在df2['a']
中插入值['a0','b0']
。但在df2输出中,数据是不同的。为什么?编辑:更新了df2。谢谢@roganjosh和Arihant,结果发现这与你看到的行为无关。好问题:)这真的回答了为什么它不在第一个实例中修改数据帧吗?