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


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.