Обновите фрейм данных в пандах, повторяя построчно


214

У меня есть фрейм данных панд, который выглядит следующим образом (это довольно большой)

           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']проблематично, потому что это цепная индексация (которая не надежна в пандах).
— Карл Д.

1
Можете ли вы предоставить другой кадр, а также <something>. Возможность векторизации вашего кода будет зависеть от этих вещей. В общем, избегайте iterrows. В вашем случае вам определенно следует избегать этого, поскольку каждая строка будет objectdtype Series.
— Филлип Облако,

Вам лучше создать булеву маску для вашего состояния, обновить все эти строки, а затем установить для остальных значение
— EdChum

Пожалуйста, не используйте iterrows (). Это явный стимул худшего анти-паттерна в истории панд.
— cs95

Ответы:


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. Никогда не изменяйте то, что вы повторяете»
— Давор Йосипович

32
Я не уверен, если мы прочитаем это точно так же. Если вы посмотрите на мой псевдокод, я внесу изменения в кадр данных, а не в значение из итератора. Значение итератора используется только для индекса значения / объекта. Что не получится, так это row ['ifor'] = some_thing по причинам, указанным в документации.
— Ракке

3
Спасибо за разъяснение.
— Давор Йосипович

8
теперь set_value также удалено и должно использовать .at (или .iat), поэтому мой цикл выглядит следующим образом: для i строка в df.iterrows (): ifor_val = что-то if <условие>: ifor_val =thing_else df.at [ я 'СВС'] = ifor_val
— complexM

2
set_value устарела и будет удалена в следующем выпуске. Пожалуйста, используйте вместо этого .at [] или .iat [] аксессоры
— RoyaumeIX

75

Объект Pandas 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?
— Юка

С точки зрения эффективности, ваш подход лучше, чем добавление столбца с запаздыванием, или эффект незначителен для небольших наборов данных? (<10 тыс. Строк)
— Юка

Это зависит. Я бы пошел за использование колонки с задержкой. Этот ответ показывает, что делать, если вы должны зацикливаться. Но если вам не нужно зацикливаться, то не надо.
— piRSquared

Понял, а также, если есть возможность оставить свой отзыв на stackoverflow.com/q/51753001/9754169, то это будет здорово: D
— Yuca

Хорошо для сравнения .at [] со старыми альтернативами
— Justas

35

Метод, который вы можете использовать, заключается в том itertuples(), что он перебирает строки DataFrame в виде именованных кортежей со значением индекса в качестве первого элемента кортежа. И это намного намного быстрее по сравнению с 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

В большинстве случаев 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']=Xили df.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


0

Лучше использовать lambdaфункции, используя df.apply()-

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

-3

Увеличьте номер 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 был создан и инкрементно увеличен до MAX из df1.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.