【问题标题】:iterate over pandas dataframe and update the value - AttributeError: can't set attribute遍历 pandas 数据框并更新值 - AttributeError:无法设置属性
【发布时间】:2019-02-17 19:47:39
【问题描述】:

我正在尝试遍历 pandas 数据帧并在满足条件时更新值,但出现错误。

for line, row in enumerate(df.itertuples(), 1):
    if row.Qty:
        if row.Qty == 1 and row.Price == 10:
            row.Buy = 1
AttributeError: can't set attribute

【问题讨论】:

    标签: python pandas dataframe


    【解决方案1】:

    可以在 pandas 中进行第一次迭代,但速度很慢,因此使用了另一种矢量化解决方案。

    如果你需要迭代,我认为你可以使用iterrows

    for idx, row in df.iterrows():
        if  df.loc[idx,'Qty'] == 1 and df.loc[idx,'Price'] == 10:
            df.loc[idx,'Buy'] = 1
    

    但更好的是使用矢量化解决方案 - 通过布尔掩码设置值 loc

    mask = (df['Qty'] == 1) & (df['Price'] == 10)
    df.loc[mask, 'Buy'] = 1
    

    或者mask的解决方案:

    df['Buy'] = df['Buy'].mask(mask, 1)
    

    或者如果您需要if...else,请使用numpy.where

    df['Buy'] = np.where(mask, 1, 0)
    

    样本

    按条件设置值:

    df = pd.DataFrame({'Buy': [100, 200, 50], 
                       'Qty': [5, 1, 1], 
                       'Name': ['apple', 'pear', 'banana'], 
                       'Price': [1, 10, 10]})
    
    print (df)
       Buy    Name  Price  Qty
    0  100   apple      1    5
    1  200    pear     10    1
    2   50  banana     10    1
    

    mask = (df['Qty'] == 1) & (df['Price'] == 10)
    
    
    df['Buy'] = df['Buy'].mask(mask, 1)
    print (df)
       Buy    Name  Price  Qty
    0  100   apple      1    5
    1    1    pear     10    1
    2    1  banana     10    1
    
    df['Buy'] = np.where(mask, 1, 0)
    print (df)
       Buy    Name  Price  Qty
    0    0   apple      1    5
    1    1    pear     10    1
    2    1  banana     10    1
    

    【讨论】:

      【解决方案2】:

      好的,如果您打算在df 中设置值,那么您需要跟踪index 的值。

      选项 1
      使用itertuples

      # keep in mind `row` is a named tuple and cannot be edited
      for line, row in enumerate(df.itertuples(), 1):  # you don't need enumerate here, but doesn't hurt.
          if row.Qty:
              if row.Qty == 1 and row.Price == 10:
                  df.set_value(row.Index, 'Buy', 1)
      

      选项 2
      使用iterrows

      # keep in mind that `row` is a `pd.Series` and can be edited...
      # ... but it is just a copy and won't reflect in `df`
      for idx, row in df.iterrows():
          if row.Qty:
              if row.Qty == 1 and row.Price == 10:
                  df.set_value(idx, 'Buy', 1)
      

      选项 3
      使用 get_value 直接向上循环

      for idx in df.index:
          q = df.get_value(idx, 'Qty')
          if q:
              p = df.get_value(idx, 'Price')
              if q == 1 and p == 10:
                  df.set_value(idx, 'Buy', 1)
      

      【讨论】:

      • 感谢您的回复 piRSquared。我们可以使用 itertuples 或 iterrows 就地替换吗?
      • itertuples.. 不! tuples 是不可变的……意味着它们无法更改。 iterrows...不是吗? pd.Series 绝对可以更改。但row 只是df 中行的副本。我将再添加一个循环选项。但是,pandas 的矢量化操作更有效,正如@jezrael 指出的那样。
      • 你仍然可以使用 itertuples。 index 0 给出数据帧的索引,因此您可以简单地使用它来插入值,方法是执行 i = row[0] 后跟 df.loc[i, 'column_name'] = some_value
      • 您的评论指出那里不需要枚举?但是获取索引不需要枚举(例如,数据帧中的索引可以跳过数字甚至不是数字)
      • 仅供参考..提交编辑以替换 set_value。它已被 atiat 弃用。
      【解决方案3】:

      pandas.DataFrame.set_value 方法在 0.21.0 pd.DataFrame.set_value 已被弃用

      使用pandas.Dataframe.at

      for index, row in df.iterrows():
              if row.Qty and row.Qty == 1 and row.Price == 10:
                  df.at[index,'Buy'] = 1
      

      【讨论】:

      • 来自文档:You should never modify something you are iterating over. This is not guaranteed to work in all cases. Depending on the data types, the iterator returns a copy and not a view, and writing to it will have no effect.
      • @techkuz 正确,但此示例未修改迭代对象的值,而是修改了原始数据框。
      猜你喜欢
      • 2015-02-08
      • 2020-04-06
      • 2014-08-09
      • 2016-12-19
      • 1970-01-01
      • 2014-11-21
      • 2021-07-03
      • 1970-01-01
      • 2020-04-21
      相关资源
      最近更新 更多