Условная замена панд


123

У меня есть DataFrame, и я хочу заменить значения в определенном столбце, которые превышают значение на ноль. Я думал, что это способ добиться этого:

df[df.my_channel > 20000].my_channel = 0

Если я скопирую канал в новый фрейм данных, это просто:

df2 = df.my_channel 

df2[df2 > 20000] = 0

Это делает именно то, что я хочу, но, похоже, не работает с каналом как частью исходного DataFrame.


Нашел то, что я думаю, вы искали здесь .
feetwet

Ответы:


181

.ixindexer работает нормально для версий pandas до 0.20.0, но начиная с pandas 0.20.0, .ixиндексатор устарел , поэтому вам следует избегать его использования. Вместо этого вы можете использовать индексаторы .locили iloc. Решить эту проблему можно следующими способами:

mask = df.my_channel > 20000
column_name = 'my_channel'
df.loc[mask, column_name] = 0

Или в одной строке

df.loc[df.my_channel > 20000, 'my_channel'] = 0

maskпомогает выбрать строки, в которых df.my_channel > 20000есть True, в то время как df.loc[mask, column_name] = 0устанавливает значение 0 для выбранных строк, где maskсодержится в столбце с именем column_name.

Обновление: в этом случае вы должны использовать, locпотому что, если вы используете iloc, вы получите NotImplementedErrorсообщение о том, что логическая индексация на основе iLocation для целочисленного типа недоступна .


82

Пытаться

df.loc[df.my_channel > 20000, 'my_channel'] = 0

Примечание. Начиная с версии 0.20.0, ix устарел в пользу loc/ iloc.


8
Спасибо. Я также нашел свое собственное решение, которое было: df.my_channel [df.my_channel> 20000] = 0
BMichell

2
@BMichell Я думаю, что ваше решение может начать выдавать вам предупреждения в 0.13, еще не было возможности попробовать
lowtech

ошибка выхода: /opt/anaconda3/envs/python35/lib/python3.5/site-packages/ipykernel_launcher.py:1: SettingWithCopyWarning: значение пытается быть установлено на копии фрагмента из DataFrame См. предостережения в документация: pandas.pydata.org/pandas-docs/stable/… "" "Точка входа для запуска ядра IPython.
Рутгер Хофсте,

@RutgerHofste, спасибо, что упомянули, что еще один аргумент никогда не использовать Python3
lowtech

34

np.where функция работает следующим образом:

df['X'] = np.where(df['Y']>=50, 'yes', 'no')

В вашем случае вам нужно:

import numpy as np
df['my_channel'] = np.where(df.my_channel > 20000, 0, df.my_channel)

19

Причина, по которой ваш исходный фрейм данных не обновляется, заключается в том, что цепное индексирование может привести к изменению копии, а не представления вашего фрейма данных. В документах дают такой совет:

При установке значений в объекте pandas необходимо соблюдать осторожность, чтобы избежать того, что называется цепной индексацией.

У вас есть несколько альтернатив: -

loc + Булево индексирование

loc может использоваться для установки значений и поддерживает логические маски:

df.loc[df['my_channel'] > 20000, 'my_channel'] = 0

mask + Булево индексирование

Вы можете назначить своей серии:

df['my_channel'] = df['my_channel'].mask(df['my_channel'] > 20000, 0)

Или вы можете обновить свою серию на месте:

df['my_channel'].mask(df['my_channel'] > 20000, 0, inplace=True)

np.where + Булево индексирование

Вы можете использовать NumPy, назначив исходную серию, когда ваше условие не выполняется; однако первые два решения более чистые, поскольку они явно изменяют только указанные значения.

df['my_channel'] = np.where(df['my_channel'] > 20000, 0, df['my_channel'])

0

Я хотел бы использовать lambdaфункцию на Seriesоператоре А DataFrameкак это:

f = lambda x: 0 if x>100 else 1
df['my_column'] = df['my_column'].map(f)

Я не утверждаю, что это эффективный способ, но он работает нормально.


3
Это неэффективно и не рекомендуется, так как включает цикл уровня Python в строковой операции.
jpp

Спасибо, я думаю, мы можем использовать locздесь, например df.loc[: , 'my_column'] = df['my_column'].map(f). Я не знаю, быстро ли это, как те, которые вы добавили ниже.
Озкан Серттас

2
Нет, все еще медленно, поскольку вы все еще работаете по строкам, а не по столбцам.
jpp

0

Попробуй это:

df.my_channel = df.my_channel.where(df.my_channel <= 20000, other= 0)

или

df.my_channel = df.my_channel.mask(df.my_channel > 20000, other= 0)

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.