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

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

Ни один из этих подходов не работает. Я не вижу обновленных значений в кадре данных.

AMM
источник
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
Rakke
источник
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
piRSquared
источник
а что если мне нужно использовать значение предыдущей строки для условия 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 .

GoingMyWay
источник
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.

CT Zhu
источник
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
Pranzell
источник
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

Увеличьте номер 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.

Шазир Джаббар
источник