Оновіть кадр даних у пандах під час ітерації рядок за рядком


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. У вашому випадку вам обов'язково слід уникати цього, оскільки кожен ряд буде objectтипом Series.
Філліп Хмара

Вам було б краще створити булеву маску для свого стану, оновити всі ці рядки, а потім встановити інше значення на інше значення
EdChum,

Не використовуйте iterrow (). Це кричущий фактор найгіршого антидіаграму за всю історію панд.
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, якщо вам потрібне значення рядка для чогось не показаного тут.

оновлення

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

6
Дивіться pandas.pydata.org/pandas-docs/stable/generated/… , друга куля: "2.Николи не слід змінювати щось, над чим ти повторюєшся"
Давор Йосипович

32
Я не впевнений, чи читатимемо ми точно так само. Якщо ви дивитесь на мій псевдо-код, я вношу модифікацію на кадр даних, а не на значення з ітератора. Значення ітератора використовується лише для індексу значення / об'єкта. Не вдасться - рядок ['ifor'] = some_thing, з причин, зазначених у документації.
rakke

3
Дякую за роз’яснення.
Давор Йосипович

8
тепер set_value також застаріла, і вона повинна використовувати. i, 'ifor'] = ifor_val
комплексM

2
set_value застаріле і буде видалено в майбутньому випуску. Будь ласка, використовуйте замість цього пристосування .at [] або .iat []
RoyaumeIX

75

Об'єкт Pandas DataFrame слід розглядати як серію Series. Іншими словами, ви повинні продумати це з точки зору стовпців. Причина, чому це важливо, полягає в тому, що при використанні pd.DataFrame.iterrowsви перебираєте рядки як рядки. Але це не так ті серії, які зберігає фрейм даних, і тому це нові серії, які створені для вас під час ітерації. Це означає, що при спробі призначити їх ці зміни не будуть відображені в початковому кадрі даних.

Гаразд, тепер це вже не виходить: що ми робимо?

Пропозиції до цієї публікації включають:

  1. pd.DataFrame.set_valueє застарілим у версії Pandas 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?
Юка

Ефективність розумна, чи краще ваш підхід проти додавання стовпця, що відстає, чи ефект незначний для невеликих наборів даних? (<10k рядків)
Юка

Це залежить. Я б хотів використовувати стовпчик із відставанням. Ця відповідь показує, що робити, якщо вам потрібно зробити цикл. Але якщо вам не потрібно циклічно, тоді не робіть.
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, щоб покращити свою ефективність. Дивіться це запитання для отримання додаткової інформації з цього
приводу

дивно думати, але 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 спочатку містить лише стовпчик1 і стовпець2. нам потрібно створити стовпчик Sortid і наростити MAX від df1.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.