条件替换Pandas

201

我有一个DataFrame,我想用零替换超过某个值的特定列中的值。我曾认为这是实现此目的的一种方式:

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

如果我将通道复制到一个新的数据帧中,那就很简单:

df2 = df.my_channel 

df2[df2 > 20000] = 0

这正是我想要的,但似乎无法与原始DataFrame中的通道一起使用。


在这里找到了我认为你正在寻找的内容 here - feetwet
7个回答

274

.ix索引器对于pandas 0.20.0之前的版本工作正常,但自从pandas 0.20.0以后,.ix索引器已经被弃用,因此你应该避免使用它。相反,你可以使用.lociloc索引器。你可以通过以下方式解决这个问题:

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条件的行,而df.loc[mask, column_name] = 0则将值0赋给那些在column_name列中mask为真的行。

更新: 在这种情况下,应该使用loc,因为如果你使用iloc,你将会得到一个NotImplementedError,告诉你基于位置的整数类型布尔索引不可用


102

尝试

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

注意:从v0.20.0开始,ix已经被弃用,推荐使用loc/iloc


10
谢谢你。我也找到了自己的解决方案,就是:df.my_channel[df.my_channel >20000] = 0。这行代码的作用是将DataFrame中名为“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的切片副本上设置值请参阅文档中的注意事项:http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy """启动IPython内核的入口点。 - Rutger Hofste
@RutgerHofste 谢谢你提到这个,又一个理由永远不要使用Python3。 - lowtech

76

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)

35
你的原始数据框未更新的原因是链式索引可能导致修改数据框的副本而不是视图。文档给出以下建议:

在 pandas 对象中设置值时,必须注意避免所谓的链式索引。

您有几个替代方案:

loc + 布尔索引

loc 可用于设置值并支持布尔掩码:

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

掩码 + 布尔索引

您可以为您的系列分配值:

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'])

如果您有多个条件需要屏蔽,该怎么办? - Egidius
@Egidius 我在 Pandas 中使用 &| 运算符,如果有多个条件则使用 np.select - Asclepius

6
尝试这个:
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)

4

我会像这样在DataFrameSeries上使用lambda函数:

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

我不断言这是一种高效的方式,但它可以正常运行。

5
这种方法效率低下,不推荐使用,因为它涉及对每一行进行 Python 级别的循环操作。 - jpp
谢谢,我想我们可以在这里使用 loc,像这样 df.loc[: , 'my_column'] = df['my_column'].map(f)。我不知道它是否像您下面添加的那些一样快。 - Ozkan Serttas
2
不行,因为你仍然是按行而非列进行操作,所以速度仍然很慢。 - jpp

0

我想通过将数据框的值与列表进行比较来执行相同的操作:

df.loc[df['value'] in [1,2,3], 'another_column'] = 'yes'

到目前为止,我得到了一个错误。

ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

但是如果我尝试执行任意([1,2,3]),就会出现错误:
TypeError: argument of type 'bool' is not iterable

如果您有新的问题,请通过单击提问按钮来提出。如果它有助于提供上下文,请包含此问题的链接。- 来自审核 - Christian Geier

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接