Обновление фрейма данных в pandas при повторении строки за строкой
У меня есть кадр данных 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
теперь я хотел бы повторять строку за строкой, и по мере прохождения каждой строки значение ifor
в каждой строке может меняться в зависимости от некоторых условий, и мне нужно искать другой фрейм данных.
Теперь, как мне это обновить, когда я повторяю.
Пробовал несколько вещей, из которых никто не работал.
for i, row in df.iterrows():
if <something>:
row['ifor'] = x
else:
row['ifor'] = y
df.ix[i]['ifor'] = x
Ни один из этих подходов, похоже, не работает. Я не вижу значения, обновленные в фрейме данных.
Ответы
Ответ 1
Вы можете назначить значения в цикле, используя 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
Ответ 2
Объект Pandas DataFrame следует рассматривать как серию серий. Другими словами, вы должны думать об этом в терминах столбцов. Причина, по которой это важно, заключается в том, что когда вы используете pd.DataFrame.iterrows
вы перебираете строки как Series. Но это не серии, которые хранит фрейм данных, и поэтому они являются новыми сериями, которые создаются для вас во время итерации. Это означает, что когда вы пытаетесь назначить их, эти изменения не будут отражены в исходном фрейме данных.
Хорошо, теперь это не так: что мы делаем?
Предложения до этого поста включают в себя:
-
pd.DataFrame.set_value
устарела с версии pd.DataFrame.set_value
0.21 -
pd.DataFrame.ix
устарела -
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
Ответ 3
Вы должны назначить значение 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.
Ответ 4
Метод, который вы можете использовать, это 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
Благодаря @SantiStSupery, использование .at
намного быстрее.
Ответ 5
Что ж, если вы все равно собираетесь повторять, почему бы не использовать самый простой метод из всех, 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
Ответ 6
for i, row in df.iterrows():
if <something>:
df.at[i, 'ifor'] = x
else:
df.at[i, 'ifor'] = y