逐行迭代时更新熊猫数据框


213

我有一个看起来像这样的熊猫数据框(非常大)

           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

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


2
我想你要df.ix[i,'ifor']df.ix[i]['ifor']这是有问题的,因为它是链式索引(在熊猫中不可靠)。
Karl D.

1
您能提供其他框架吗<something>?您的代码是否可以向量化取决于这些因素。通常,请避免iterrows。在您的情况下,您绝对应该避免使用它,因为每一行都是objectdtype Series
菲利普·

您最好为您的条件创建一个布尔掩码,更新所有这些行,然后将其余行设置为其他值
EdChum 2014年

请不要使用iterrows()。它是大熊猫历史上最恶劣的反模式的公然推动者。
cs95

Answers:


232

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

更新

从0.21.0版开始不推荐使用df.set_value(),而可以使用df.at():

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

6
参见pandas.pydata.org/pandas-docs/stable/generated/…,第二点:“ 2。您永远都不要修改正在迭代的内容”
Davor Josipovic,2016年

32
我不确定我们是否完全一样阅读。如果您查看我的伪代码,则会对数据帧进行修改,而不是对迭代器的值进行修改。迭代器值仅用于值/对象的索引。由于文档中提到的原因,将失败的是row ['ifor'] = some_thing。
拉克

3
谢谢你的澄清。
达沃·乔西波维奇

8
现在set_value也不再使用了,应该使用.at(或.iat),所以我的循环看起来像这样:对于i,df.iterrows()中的行:ifor_val = if if <condition>:ifor_val = something_else df.at [ i,'ifor'] = ifor_val
complexM

2
set_value已过时,将在以后的版本中删除。请改用.at []或.iat []访问器
RoyaumeIX,

75

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

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

此职位之前的建议包括:

  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

您甚至可以将其更改为:

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添加滞后列?
尤卡

在效率方面,与添加滞后列相比,您的方法更好吗?对于小型数据集,这种影响可以忽略吗?(<1万行)
尤卡

那要看。我会选择使用滞后列。此答案显示了如果必须循环该怎么办。但是,如果您不必循环,则不必。
piRSquared

知道了,如果您可能对stackoverflow.com/q/51753001/9754169有反馈,那就
太好了

尼斯对比.AT []与旧方案
Justas

35

可以使用的方法是itertuples(),将DataFrame行作为namedtuple进行迭代,将index值作为tuple的第一个元素。与相比,它要快得多iterrows()。对于itertuples(),每个都在DataFrame中row包含其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()速度比iat或快at

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


3
由于您仅指向精确的索引,因此您可能会考虑使用.at而不是.loc来提高性能。有关此问题的更多信息,请参阅此问题
SantiStSupery

奇怪的想法,但df.loc[row.Index, 3] = x不起作用。另一方面,df.loc[row.Index, 'ifor'] = x作品!
seralouk

19

您应该用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建议的方法。


10

好吧,如果您要进行迭代,为什么不使用所有最简单的方法, 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
for i, row in df.iterrows():
    if <something>:
        df.at[i, 'ifor'] = x
    else:
        df.at[i, 'ifor'] = y

0

最好lambda使用df.apply()-

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

-3

从一列增加最大数。例如 :

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。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.