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


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.