将替代值分配给 pandas dataFrame 以其值为条件

assigning an alternative value to pandas dataFrame conditional on its value

我正在尝试为 pandas dataFrame 对象中的列分配替代值。分配替代值的条件是该元素现在的值为零。

这是我的代码片段:

df = pd.DataFrame({'A': [0, 1, 2, 0, 0, 1, 1 ,0], 'B': [1, 2, 3, 4, 1, 2, 3, 4]})

for i, row in df.iterrows():
    if row['A'] == 0.0:
        df.iloc[i]['A'] = df.iloc[i-1]['A'] + df.iloc[i]['B'] - df.iloc[i-1]['B']

然而,事实证明,这些元素中的值仍然为零!以上的效果为零。

怎么回事?

怎么样:

df = pd.DataFrame({'A': [0, 1, 2, 0, 0, 1, 1 ,0], 'B': [1, 2, 3, 4, 1, 2, 3, 4]})
df['A'] = df.where(df[['A']] != 0, 
                   df['A'].shift() + df['B'] - df['B'].shift(),
                   axis=0)['A']
print(df)

     A  B
0  NaN  1
1  1.0  2
2  2.0  3
3  3.0  4
4 -3.0  1
5  1.0  2
6  1.0  3
7  2.0  4

NaN 在那里,因为第一个元素之前没有元素

下面的原始答案适用于某些输入,但并不完全正确。使用您问题中的数据框测试您的代码,我发现它可以工作,但不能保证它适用于所有数据框。这是一个不起作用的示例:

df = pd.DataFrame(np.random.randn(6,4), index=list(range(0,12,2)), columns=['A', 'B', 'C', 'D'])

此数据框将导致您的代码失败,因为索引不是您的算法预期的 0、1、2...,它们是 0、2、4...,如 [=12] 所定义=].

这意味着迭代器返回的 i 的值也将是 0, 2, 4,..., 所以当您尝试将 i-1 用作iloc.

的参数

简而言之,当您使用 for i, row in df.iterrows(): 遍历数据框时,i 会采用您正在遍历的维度的索引值 正如它们的定义在数据框中。在循环内使用带有偏移量的值时,请确保您知道这些值是什么。


原回答:

我无法弄清楚为什么您的代码不起作用,但我可以验证它是否不起作用。它可能与在迭代数据帧时修改数据帧有关,因为您可以使用 df.iloc[1]['A'] = 0.0 在循环外毫无问题地设置值。

尝试使用 DataFrame.at 代替:

for i, row in df.iterrows():
    if row['A'] == 0.0:
        df.at[i, 'A'] = df.iloc[i-1]['A'] + df.iloc[i]['B'] - df.iloc[i-1]['B']

这不会解释 df.iloc[i-1] 返回数据框中的最后一行,因此请注意当 A 列中的第一个值为 0.0 时。

您正在使用与著名的 SettingWithCopy 警告相关的 chained indexing。检查 Tom Augspurger modern pandas 中的 SettingWithCopy 设置。

一般来说,这意味着不鼓励使用 df['A']['B']= ... 形式的赋值。在那里使用 loc acessor 并不重要。

如果您在代码中添加打印语句:

for i, row in df.iterrows():
    print(df)
    if row['A'] == 0.0:
        df.iloc[i]['A'] = df.iloc[i-1]['A'] + df.iloc[i]['B'] - df.iloc[i-1]['B']

你看到奇怪的事情发生了。当且仅当第一行 'A' 列为 0 时,数据帧 df 才会被修改。

正如 Bill the Lizard 所指出的,您需要一个访问器。但是,请注意 Bill 的方法具有提供基于标签的访问的缺点。当具有不同索引的数据框时,这可能不是您想要的。那么更好的解决方案是使用 loc

for i, row in df.iterrows():
    if row['A'] == 0.0:
        df.loc[df.index[i], 'A'] = df.iloc[i-1]['A'] + df.iloc[i]['B'] - df.iloc[i-1]['B']

或 iloc

    for i, row in df.iterrows():
        if row['A'] == 0.0:
            df.iloc[i, df.columns.get_loc('A')] = df.iloc[i-1]['A'] + df.iloc[i]['B'] - df.iloc[i-1]['B']

假设索引在最后一种情况下是唯一的。 请注意,链接索引发生在设置值时。

虽然这种方法有效,但根据上面的引述,不鼓励这样做!