【问题标题】:Update a dataframe in pandas while iterating row by row逐行迭代时更新熊猫中的数据框
【发布时间】:2014-06-13 09:12:23
【问题描述】:

我有一个看起来像这样的 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

这些方法似乎都不起作用。我没有看到数据框中更新的值。

【问题讨论】:

  • 我想你想要df.ix[i,'ifor']df.ix[i]['ifor'] 是有问题的,因为它是链式索引(这在 pandas 中不可靠)。
  • 能否提供其他框架以及&lt;something&gt;。您的代码是否可以矢量化将取决于这些事情。一般来说,避免iterrows。在您的情况下,您应该绝对避免它,因为每一行都是object dtype Series
  • 您最好为您的条件创建一个布尔掩码,更新所有这些行,然后将其余行设置为其他值
  • Please do not use iterrows(). 它是 pandas 历史上最糟糕的反模式的公然推动者。

标签: python pandas updates dataframe


【解决方案1】:

您应该通过df.ix[i, 'exp']=Xdf.loc[i, 'exp']=X 而不是df.ix[i]['ifor'] = x 分配值。

否则你正在处理一个视图,并且应该得到一个温暖:

-c:1: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_index,col_indexer] = value instead

但当然,循环可能最好用一些矢量化算法代替,以充分利用@Phillip Cloud 建议的DataFrame

【讨论】:

    【解决方案2】:

    您可以使用 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 循环,以防您需要此处未显示的行值。

    更新

    df.set_value() 自 0.21.0 版以来已被弃用 你可以使用 df.at() 代替:

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

    【讨论】:

    • pandas.pydata.org/pandas-docs/stable/generated/…,第二个项目符号:“2.你不应该修改你正在迭代的东西”
    • 我不确定我们读的是否完全一样。如果您查看我的伪代码,我会修改数据框,而不是迭代器的值。迭代器值仅用于值/对象的索引。由于文档中提到的原因,会失败的是 row['ifor']=some_thing。
    • 感谢您的澄清。
    • 现在 set_value 也已被弃用,应该使用 .at(或 .iat),所以我的循环如下所示:for i, row in df.iterrows(): ifor_val = something if : ifor_val = something_else df.at[i,'ifor'] = ifor_val
    • set_value 已弃用,将在未来版本中删除。请改用 .at[] 或 .iat[] 访问器
    【解决方案3】:

    您可以使用的方法是itertuples(),它将 DataFrame 行作为命名元组进行迭代,索引值作为元组的第一个元素。与iterrows() 相比,它要快得多。对于itertuples(),每个row在DataFrame中都包含它的Index,你可以使用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,using .at is much faster than loc

    【讨论】:

    • 由于您只指向精确的索引,您可能会考虑使用 .at 而不是 .loc 来提高性能。 See this question 了解更多信息
    • 奇怪的想法,但df.loc[row.Index, 3] = x 不起作用。另一方面,df.loc[row.Index, 'ifor'] = x 有效!
    【解决方案4】:

    Pandas DataFrame 对象应该被认为是一系列系列。换句话说,您应该从列的角度来考虑它。这很重要的原因是因为当您使用pd.DataFrame.iterrows 时,您正在将行作为系列进行迭代。但这些不是数据框正在存储的系列,因此它们是在您迭代时为您创建的新系列。这意味着当您尝试分配它们时,这些编辑最终不会反映在原始数据框中。

    好的,现在已经不碍事了:我们该怎么办?

    在这篇文章之前的建议包括:

    1. pd.DataFrame.set_valuedeprecated as of Pandas version 0.21
    2. pd.DataFrame.ixdeprecated
    3. pd.DataFrame.loc 很好,但 can work on array indexers 你可以做得更好

    我的建议
    使用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
    

    回复评论

    如果我需要将前一行的值用于 if 条件怎么办?

    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
    

    【讨论】:

    • 如果我需要将前一行的值用于 if 条件怎么办?向 OG df 添加滞后列?
    • 效率方面,您的方法比添加滞后列更好,还是对小型数据集的影响可以忽略不计? (
    • 这取决于。我会使用滞后列。这个答案显示了如果你必须循环该怎么做。但如果你不必循环,那就不要。
    • 知道了,如果有可能得到您对stackoverflow.com/q/51753001/9754169 的反馈,那就太好了:D
    • 很好地将 .at[] 与旧的替代品进行对比
    【解决方案5】:
    for i, row in df.iterrows():
        if <something>:
            df.at[i, 'ifor'] = x
        else:
            df.at[i, 'ifor'] = y
    

    【讨论】:

      【解决方案6】:

      好吧,如果你要进行迭代,为什么不使用最简单的方法,df['Column'].values[i]

      df['Column'] = ''
      
      for i in range(len(df)):
          df['Column'].values[i] = something/update/new_value
      

      或者,如果您想将新值与旧值或类似的值进行比较,为什么不将其存储在列表中,然后追加到最后。

      mylist, df['Column'] = [], ''
      
      for <condition>:
          mylist.append(something/update/new_value)
      
      df['Column'] = mylist
      

      【讨论】:

        【解决方案7】:

        增加一列的 MAX 数。例如:

        df1 = [sort_ID, Column1,Column2]
        print(df1)
        

        我的输出:

        Sort_ID Column1 Column2
        12         a    e
        45         b    f
        65         c    g
        78         d    h
        

        MAX = df1['Sort_ID'].max() #This returns my Max Number 
        

        现在,我需要在 df2 中创建一个列并填充列值,使 MAX 递增。

        Sort_ID Column1 Column2
        79      a1       e1
        80      b1       f1
        81      c1       g1
        82      d1       h1
        

        注意:df2 最初将仅包含 Column1 和 Column2 。我们需要创建 Sortid 列并从 df1 增加 MAX 。

        【讨论】:

          【解决方案8】:

          最好使用lambda函数使用df.apply() -

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

          【讨论】:

          • 这应该是新的更新答案。其他的似乎是十年前的。谁还在使用 for 循环 哎呀
          • 如果您希望添加的值必须逐行更改,而不是一次更改一整列,那么上述工作/应用将如何?
          • @steve 逐行操作。 x 是行(系列)。 Lambda 可以是任何函数,因此您可以在那里进行任意复杂的行为。
          【解决方案9】:

          列表理解可能是一种选择。

          df['new_column'] = [your_func(x) for x in df['column']]
          

          这将遍历 df['column'] 列,使用来自 df['column'] 的值调用函数 your_func,并为新列 df['new_column'] 中的行分配一个值。

          请不要忘记创建一个函数。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-05-17
            • 2015-12-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-01-08
            相关资源
            最近更新 更多