在逐行迭代时更新pandas中的数据帧

AMM*_*AMM 166 python updates dataframe 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 
Run Code Online (Sandbox Code Playgroud)

现在我想逐行迭代,当我遍历每一行时,每行的值ifor 可以根据某些条件改变,我需要查找另一个数据帧.

现在,我如何在迭代时更新它.尝试了一些他们都没有工作的事情.

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

    df.ix[i]['ifor'] = x
Run Code Online (Sandbox Code Playgroud)

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

rak*_*kke 175

您可以使用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)
Run Code Online (Sandbox Code Playgroud)

如果你不需要行值,你可以简单地迭代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
Run Code Online (Sandbox Code Playgroud)

  • 我不确定我们是否完全相同.如果查看我的伪代码,我会对数据帧进行修改,而不是对迭代器的值进行修改.迭代器值仅用于值/对象的索引.由于文档中提到的原因,row ['ifor'] = some_thing会失败的是什么. (27认同)
  • 现在set_value也被删除了,应该使用.at(或.iat),所以我的循环看起来像这样:对于i,在df.iterrows()中输入:ifor_val =如果<condition>:ifor_val = something_else df.at [我,'ifor'] = ifor_val (8认同)
  • 请参阅http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.iterrows.html,第二个子弹:"2.你永远不应该修改你正在迭代的东西" (5认同)
  • set_value 已弃用,将在未来版本中删除。请改用 .at[] 或 .iat[] 访问器 (4认同)
  • 谢谢你的澄清. (2认同)
  • 仅当索引唯一时才有效。否则会有非常奇怪的行为 (2认同)

piR*_*red 57

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

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

此帖之前的建议包括:

  1. pd.DataFrame.set_value弃用的熊猫版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
Run Code Online (Sandbox Code Playgroud)

您甚至可以将其更改为:

for i in df.index:
    df.at[i, 'ifor'] = x if <something> else y
Run Code Online (Sandbox Code Playgroud)

回应评论

如果我需要将前一行的值用于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
Run Code Online (Sandbox Code Playgroud)


Goi*_*Way 24

您可以使用的方法是itertuples(),它将DataFrame行作为namedtuples迭代,索引值作为元组的第一个元素.与之相比它要快得多iterrows().对于itertuples(),每个都row包含Index在DataFrame中,您可以使用它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
Run Code Online (Sandbox Code Playgroud)

谢谢@SantiStSupery,使用.at速度更快.

  • 由于您仅指向精确的索引,因此您可能会考虑使用.at而不是.loc来提高性能。[查看此问题](/sf/ask/2605153981/)了解更多信息 (2认同)

CT *_*Zhu 17

您应该通过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

但当然,循环可能应该更好地被一些矢量化算法取代,以充分利用DataFrame@Phillip Cloud建议.


小智 16

最好使用以下lambda功能使用df.apply()-

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

  • 这应该是新的更新答案。其他的似乎是本世纪初的。谁还在使用 for 循环呀 (3认同)
  • @steve 是逐行操作的。x 是行(系列)。Lambda 可以是任何函数,因此您可以在其中具有任意复杂的行为。 (2认同)

Pra*_*ell 8

好吧,如果你打算无论如何迭代,为什么不使用最简单的方法, df['Column'].values[i]

df['Column'] = ''

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

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

mylist, df['Column'] = [], ''

for <condition>:
    mylist.append(something/update/new_value)

df['Column'] = mylist
Run Code Online (Sandbox Code Playgroud)


Dua*_*ane 6

for i, row in df.iterrows():
    if <something>:
        df.at[i, 'ifor'] = x
    else:
        df.at[i, 'ifor'] = y
Run Code Online (Sandbox Code Playgroud)