Панда умовне створення стовпця / колонки даних


314

У мене є кадр даних у відповідності до наведених нижче текстів:

    Type       Set
1    A          Z
2    B          Z           
3    B          X
4    C          Y

Я хочу додати ще один стовпець до фрейму даних (або генерувати серію) тієї ж довжини, що і кадр даних (= рівна кількість записів / рядків), який встановлює колір зеленого кольору, якщо Set = 'Z' та 'червоний', якщо Set = інше .

Який найкращий спосіб зробити це?

Відповіді:


711

Якщо у вас є лише два варіанти:

df['color'] = np.where(df['Set']=='Z', 'green', 'red')

Наприклад,

import pandas as pd
import numpy as np

df = pd.DataFrame({'Type':list('ABBC'), 'Set':list('ZZXY')})
df['color'] = np.where(df['Set']=='Z', 'green', 'red')
print(df)

врожайність

  Set Type  color
0   Z    A  green
1   Z    B  green
2   X    B    red
3   Y    C    red

Якщо у вас більше двох умов, тоді використовуйтеnp.select . Наприклад, якщо ти хочеш colorбути таким

  • yellow коли (df['Set'] == 'Z') & (df['Type'] == 'A')
  • інакше blueколи(df['Set'] == 'Z') & (df['Type'] == 'B')
  • інакше purpleколи(df['Type'] == 'B')
  • інакше black,

потім використовуйте

df = pd.DataFrame({'Type':list('ABBC'), 'Set':list('ZZXY')})
conditions = [
    (df['Set'] == 'Z') & (df['Type'] == 'A'),
    (df['Set'] == 'Z') & (df['Type'] == 'B'),
    (df['Type'] == 'B')]
choices = ['yellow', 'blue', 'purple']
df['color'] = np.select(conditions, choices, default='black')
print(df)

яка врожайність

  Set Type   color
0   Z    A  yellow
1   Z    B    blue
2   X    B  purple
3   Y    C   black

1
не працює, якщо я поставив дві умови всередині, де застереження з і
— Amol Sharma

2
df ['color'] = list (np.where (df ['Set'] == 'Z', 'green', 'red')) придушить попередження про панди: На копії намагається встановити значення фрагмента з DataFrame. Спробуйте використовувати .loc [row_indexer, col_indexer] = значення замість цього
— denon

3
"зелений" і "червоний" також можна замінити арифметикою стовпців. наприклад ,df['foo'] = np.where(df['Set']=='Z', df['Set'], df['Type'].shift(1))
— Алехандро

np. де створює новий стовпець? Я використовував цей код, і коли роблю df.color.head (), я отримую: 'numpy.ndarray' об’єкт не має атрибута 'head'
— vvv

3
Прикро, що я не можу підтвердити це кілька разів. Одного обґрунтування не здається достатньо.
— Харпер

120

Розуміння списку - це ще один спосіб умовного створення іншого стовпця. Якщо ви працюєте з типами об'єктів у стовпцях, як у вашому прикладі, розуміння списку зазвичай перевершує більшість інших методів.

Приклад розуміння списку:

df['color'] = ['red' if x == 'Z' else 'green' for x in df['Set']]

% тестів на час:

import pandas as pd
import numpy as np

df = pd.DataFrame({'Type':list('ABBC'), 'Set':list('ZZXY')})
%timeit df['color'] = ['red' if x == 'Z' else 'green' for x in df['Set']]
%timeit df['color'] = np.where(df['Set']=='Z', 'green', 'red')
%timeit df['color'] = df.Set.map( lambda x: 'red' if x == 'Z' else 'green')

1000 loops, best of 3: 239 µs per loop
1000 loops, best of 3: 523 µs per loop
1000 loops, best of 3: 263 µs per loop

4
Зауважте, що із значно більшими рамками даних (думайте- pd.DataFrame({'Type':list('ABBC')*100000, 'Set':list('ZZXY')*100000})розмір) вони numpy.whereперевищують map, але розуміння списку є корольним (приблизно на 50% швидше, ніж numpy.where).
— коваль

3
Чи можна використовувати метод розуміння списку, якщо умові потрібна інформація з кількох стовпців? Я шукаю що - щось на зразок цього (це не працює):df['color'] = ['red' if (x['Set'] == 'Z') & (x['Type'] == 'B') else 'green' for x in df]
— Mappi

2
Додайте повторення до фрейму даних, тоді ви можете отримати доступ до кількох стовпців через рядок: ['red' if (рядок ['Set'] == 'Z') & (рядок ['Type'] == 'B') else 'green 'для індексу, рядок у df.iterrow ()]
— чіпкий козирок

1
Зауважте, це приємне рішення не спрацює, якщо вам потрібно буде взяти значення заміни з іншої серії в кадрі даних, наприкладdf['color_type'] = np.where(df['Set']=='Z', 'green', df['Type'])
— Пол Руж

@cheekybastard Або не варто, оскільки .iterrows()це сумно мляво і DataFrame не слід змінювати під час ітерації.
— AMC

21

Ще один шлях, якого можна досягти, це

df['color'] = df.Set.map( lambda x: 'red' if x == 'Z' else 'green')

Хороший підхід, це може бути запам'ятоване для більш швидкої ефективності (у великих наборах даних), хоча це вимагає додаткового кроку.
— Яков

21

Ось ще один спосіб зібрати цю кішку, використовуючи словник для відображення нових значень на клавіші у списку:

def map_values(row, values_dict):
    return values_dict[row]

values_dict = {'A': 1, 'B': 2, 'C': 3, 'D': 4}

df = pd.DataFrame({'INDICATOR': ['A', 'B', 'C', 'D'], 'VALUE': [10, 9, 8, 7]})

df['NEW_VALUE'] = df['INDICATOR'].apply(map_values, args = (values_dict,))

Як це виглядає:

df
Out[2]: 
  INDICATOR  VALUE  NEW_VALUE
0         A     10          1
1         B      9          2
2         C      8          3
3         D      7          4

Цей підхід може бути дуже потужним, коли у вас є багато ifelseзаявок типу (тобто багато унікальних значень для заміни).

І звичайно, ви завжди могли це зробити:

df['NEW_VALUE'] = df['INDICATOR'].map(values_dict)

Але такий підхід більш ніж утричі повільніший, ніж applyпідхід зверху, на моїй машині.

І ви також можете це зробити, використовуючи dict.get:

df['NEW_VALUE'] = [values_dict.get(v, None) for v in df['INDICATOR']]

Мені подобається ця відповідь, тому що вона показує, як робити кілька замін значень
— Моніка Хеднек

Але такий підхід більш ніж утричі повільніший, ніж підхід застосувати зверху, на моїй машині. Як ви оцінювали їх? З моїх швидких вимірювань, .map()рішення ~ в 10 разів швидше, ніж .apply().
— AMC

Оновлення: 100 000 000 рядків, 52 рядкові значення, .apply()займають 47 секунд проти 5,91 секунди .map().
— AMC

19

Далі йде повільніше, ніж підходи, присвячені тут , але ми можемо обчислити додатковий стовпець на основі вмісту більше одного стовпця, і для додаткового стовпця можна обчислити більше двох значень.

Простий приклад, використовуючи лише стовпець "Встановити":

def set_color(row):
    if row["Set"] == "Z":
        return "red"
    else:
        return "green"

df = df.assign(color=df.apply(set_color, axis=1))

print(df)
  Set Type  color
0   Z    A    red
1   Z    B    red
2   X    B  green
3   Y    C  green

Приклад із врахуванням більшої кількості кольорів та більше стовпців:

def set_color(row):
    if row["Set"] == "Z":
        return "red"
    elif row["Type"] == "C":
        return "blue"
    else:
        return "green"

df = df.assign(color=df.apply(set_color, axis=1))

print(df)
  Set Type  color
0   Z    A    red
1   Z    B    red
2   X    B  green
3   Y    C   blue

Редагувати (21.06.2019): Використання плідату

Можна також використовувати плідати для подібних речей (це здається навіть повільніше, ніж використання assignта apply, хоча).

from plydata import define, if_else

Простий if_else:

df = define(df, color=if_else('Set=="Z"', '"red"', '"green"'))

print(df)
  Set Type  color
0   Z    A    red
1   Z    B    red
2   X    B  green
3   Y    C  green

Вкладено if_else:

df = define(df, color=if_else(
    'Set=="Z"',
    '"red"',
    if_else('Type=="C"', '"green"', '"blue"')))

print(df)                            
  Set Type  color
0   Z    A    red
1   Z    B    red
2   X    B   blue
3   Y    C  green

10

Можливо, це можливо завдяки новішим оновленням Pandas, але я вважаю, що наступне - найкоротший і, можливо, найкращий відповідь на це питання поки що. Ви можете використовувати .locметод і використовувати одну умову або кілька, залежно від ваших потреб.

Код:

df=pd.DataFrame(dict(Type='A B B C'.split(), Set='Z Z X Y'.split()))
df['Color'] = "red"
df.loc[(df['Set']=="Z"), 'Color'] = "green"

#practice!
df.loc[(df['Set']=="Z")&(df['Type']=="B")|(df['Type']=="C"), 'Color'] = "purple"

Пояснення:

df=pd.DataFrame(dict(Type='A B B C'.split(), Set='Z Z X Y'.split()))

# df so far: 
  Type Set  
0    A   Z 
1    B   Z 
2    B   X 
3    C   Y

додайте стовпець "колір" і встановіть усі значення "червоний"

df['Color'] = "red"

Застосуйте свою єдину умову:

df.loc[(df['Set']=="Z"), 'Color'] = "green"


# df: 
  Type Set  Color
0    A   Z  green
1    B   Z  green
2    B   X    red
3    C   Y    red

або кілька умов, якщо ви хочете:

df.loc[(df['Set']=="Z")&(df['Type']=="B")|(df['Type']=="C"), 'Color'] = "purple"

Про логічні оператори Pandas та умовний вибір ви можете прочитати тут: Логічні оператори для булевої індексації в Pandas


2
Найкращий поки що. Можливо, ви могли б додати ще більше умов, які б були кодомdf.loc[(df['Set']=="Z") & (df['Type']=="A"), 'Color'] = "green"
— Сальвадор Віго

2
Це має бути прийнятою відповіддю. Насправді ідіоматичний та розширюваний.
— AMC

1

Один вкладиш із .apply()методом такий:

df['color'] = df['Set'].apply(lambda set_: 'green' if set_=='Z' else 'red')

Після цього dfкадр даних виглядає приблизно так:

>>> print(df)
  Type Set  color
0    A   Z  green
1    B   Z  green
2    B   X    red
3    C   Y    red

0

Якщо ви працюєте з масовими даними, найкраще запам'ятовується підхід:

# First create a dictionary of manually stored values
color_dict = {'Z':'red'}

# Second, build a dictionary of "other" values
color_dict_other = {x:'green' for x in df['Set'].unique() if x not in color_dict.keys()}

# Next, merge the two
color_dict.update(color_dict_other)

# Finally, map it to your column
df['color'] = df['Set'].map(color_dict)

Цей підхід буде найшвидшим, коли у вас буде багато повторених значень. Моє загальне правило: пам'ятати, коли: data_size> 10**4& n_distinct<data_size/4

Ex Memoize у випадку 10 000 рядків з 2500 або меншими різними значеннями.


Гаразд, тож з лише двома різними значеннями для відображення 100 000 000 рядків потрібно 6,67 секунди, щоб пройти без «запам'ятовування», і 9,86 секунди з.
— AMC

100 000 000 рядків, 52 чіткі значення, де 1 з цих карт відповідає першому вихідному значенню, а інший 51 - всі відповідають іншому: 7,99 секунд без запам'ятовування, 11,1 секунди с.
— AMC

Чи є ваші значення у випадковому порядку? Або вони повертаються назад? Висока швидкість панд може бути з - за кешування @AMC
— Яаков Bressler

1
Чи є ваші значення у випадковому порядку? Або вони повертаються назад? Значення випадкові, вибираються за допомогою random.choices().
— AMC
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.