在迭代行时更新pandas数据框

361 投票
9 回答
530462 浏览
提问于 2025-04-18 04:31

我有一个很大的 pandas 数据框,长得像这样:

           date      exer exp     ifor         mat  
1092  2014-03-17  American   M  528.205  2014-04-19 
1093  2014-03-17  American   M  528.205  2014-04-19 
1094  2014-03-17  American   M  528.205  2014-04-19 
1095  2014-03-17  American   M  528.205  2014-04-19    
1096  2014-03-17  American   M  528.205  2014-05-17 

现在我想逐行遍历这个数据框。在查看每一行的时候,ifor 的值可能会根据一些条件发生变化,这时我需要查找另一个数据框。

那么,在我遍历的时候,怎么才能更新这个值呢?我试过几种方法,但都没有成功。

for i, row in df.iterrows():
    if <something>:
        row['ifor'] = x
    else:
        row['ifor'] = y

    df.ix[i]['ifor'] = x

这些方法似乎都不管用。我没有看到数据框里的值被更新。

9 个回答

24

你应该用 df.ix[i, 'exp']=X 或者 df.loc[i, 'exp']=X 来给值赋值,而不是用 df.ix[i]['ifor'] = x

否则你是在操作一个视图,这样会收到一个警告:

-c:1: SettingWithCopyWarning: 你试图在一个DataFrame的切片副本上设置值。 请尝试使用 .loc[行索引, 列索引] = 值 来代替

而且,正如 @Phillip Cloud 所建议的,循环最好用一些向量化的算法来替代,这样可以更好地利用 DataFrame

29

使用 df.apply() 来调用 lambda 函数会更好 -

df["ifor"] = df.apply(lambda x: {value} if {condition} else x["ifor"], axis=1)
74

你可以使用一个叫做 itertuples()的方法,它可以让你逐行遍历数据表(DataFrame),每一行会以命名元组的形式出现,元组的第一个元素是行的索引值。这个方法比 iterrows() 快得多。使用 itertuples() 时,每一行的数据都会包含它在数据表中的索引,你还可以用 loc 来设置值。

for row in df.itertuples():
    if <something>:
        df.at[row.Index, 'ifor'] = x
    else:
        df.at[row.Index, 'ifor'] = x

    df.loc[row.Index, 'ifor'] = x

在大多数情况下,itertuples() 的速度比 iatat 要快。

感谢 @SantiStSupery, 使用 .at 的速度比 loc 快得多

128

Pandas的DataFrame对象可以理解为一系列的Series。换句话说,你可以把它看作是由多列组成的。这样理解很重要,因为当你使用pd.DataFrame.iterrows时,你是在逐行遍历这些数据,而每一行都是一个新的Series。但要注意,这些并不是DataFrame里原本存储的Series,而是在遍历时临时创建的新Series。这意味着,如果你对这些新Series进行修改,这些修改不会反映到原来的DataFrame中。

好了,了解这些之后,我们该怎么做呢?

在这篇帖子之前,有一些建议:

  1. pd.DataFrame.set_value在Pandas版本0.21中已经被弃用
  2. pd.DataFrame.ix也已经被弃用
  3. pd.DataFrame.loc是可以用的,但它可以处理数组索引,你可以找到更好的方法

我的推荐
使用pd.DataFrame.at

for i in df.index:
    if <something>:
        df.at[i, 'ifor'] = x
    else:
        df.at[i, 'ifor'] = y

你甚至可以把它改成:

for i in df.index:
    df.at[i, 'ifor'] = x if <something> else y

对评论的回应

如果我需要用到上一行的值来做条件判断怎么办?

for i in range(1, len(df) + 1):
    j = df.columns.get_loc('ifor')
    if <something>:
        df.iat[i - 1, j] = x
    else:
        df.iat[i - 1, j] = y
432

你可以使用 df.at 来进行操作:

for i, row in df.iterrows():
    ifor_val = something
    if <condition>:
        ifor_val = something_else
    df.at[i,'ifor'] = ifor_val

如果你使用的版本在 0.21.0 之前,可以用 df.set_value

for i, row in df.iterrows():
    ifor_val = something
    if <condition>:
        ifor_val = something_else
    df.set_value(i,'ifor',ifor_val)

如果你不需要行的值,其实可以直接遍历 df 的索引,不过我保留了原来的 for 循环,以防你需要行值来做其他事情,这里没有展示出来。

撰写回答