53_Pandas中的条件替换值(where, mask)
我会解释如何在pandas中根据条件赋值。虽然它不使用 if 语句,但它可以处理条件分支,如 if then … 或 if then … else …。
具体值的替换见后面的文章,替换或删除缺失值NaN。
以下面的 pandas.DataFrame 为例。
import pandas as pd
import numpy as np
df = pd.DataFrame({'A': [-20, -10, 0, 10, 20],
'B': [1, 2, 3, 4, 5],
'C': ['a', 'b', 'b', 'b', 'a']})
print(df)
# A B C
# 0 -20 1 a
# 1 -10 2 b
# 2 0 3 b
# 3 10 4 b
# 4 20 5 a
以下内容进行说明。
- 带有 loc、iloc 的布尔索引引用
- pandas.DataFrame, Series 的 where() 方法
- False元素可以改变,True元素保持不变
- pandas.DataFrame, Series 的 mask() 方法
- True元素可以改变,False元素不变
- NumPy where() 函数
- True 和 False 元素都可以更改
带有 loc、iloc 的布尔索引引用
可以通过如下编写来根据条件替换标量值。
df.loc[df['A'] < 0, 'A'] = -100
df.loc[~(df['A'] < 0), 'A'] = 100
print(df)
# A B C
# 0 -100 1 a
# 1 -100 2 b
# 2 100 3 b
# 3 100 4 b
# 4 100 5 a
如果对 pandas.DataFrame 或 pandas.DataFrame的列(= pandas.Series)进行比较操作,将得到一个 bool 类型的 pandas.DataFrame 或 pandas.Series。
一个例子是处理 pandas.DataFrame (= pandas.Series) 的列。 ~ 是否定运算符。
print(df['A'] < 0)
# 0 True
# 1 True
# 2 False
# 3 False
# 4 False
# Name: A, dtype: bool
print(~(df['A'] < 0))
# 0 False
# 1 False
# 2 True
# 3 True
# 4 True
# Name: A, dtype: bool
使用 bool 类型 pandas.Series 作为 loc 或 iloc 行规范将只选择 True 行。 loc由行名和列名指定,iloc由行号和列号指定。
print(df.loc[df['A'] < 0, 'A'])
# 0 -100
# 1 -100
# Name: A, dtype: int64
带有loc和iloc的引用不仅可以用来取值,还可以用来赋值。 bool type pandas.Series 为 True 的行(满足条件的行)和指定的列元素更改为右侧的标量值。
df.loc[df['A'] < 0, 'A'] = -10
print(df)
# A B C
# 0 -10 1 a
# 1 -10 2 b
# 2 100 3 b
# 3 100 4 b
# 4 100 5 a
也可以指定 pandas.Series、列表或数组而不是标量值。相应的行值被替换。
df.loc[~(df['A'] < 0), 'A'] = df['B']
print(df)
# A B C
# 0 -10 1 a
# 1 -10 2 b
# 2 3 3 b
# 3 4 4 b
# 4 5 5 a
到目前为止的示例中,我们已经为现有列的元素赋值,但是指定新的列名会添加一个新列,并允许我们为满足条件的行赋值。
df.loc[df['B'] % 2 == 0, 'D'] = 'even'
df.loc[df['B'] % 2 != 0, 'D'] = 'odd'
print(df)
# A B C D
# 0 -10 1 a odd
# 1 -10 2 b even
# 2 3 3 b odd
# 3 4 4 b even
# 4 5 5 a odd
也可以用 and, or 指定多个条件。使用 &, | 将每个条件括在括号中。
添加新列时,不满足条件的元素将具有缺失值 NaN。请注意,包含 NaN 的列的类型 dtype 将为 float。
df.loc[~(df['A'] < 0) & (df['C'] == 'b'), 'E'] = df['B'] * 2
print(df)
# A B C D E
# 0 -10 1 a odd NaN
# 1 -10 2 b even NaN
# 2 3 3 b odd 6.0
# 3 4 4 b even 8.0
# 4 5 5 a odd NaN
根据列的值选择两列之一的过程可以编写如下。
df.loc[~(df['A'] < 0), 'A'] = 10
print(df)
# A B C D E
# 0 -10 1 a odd NaN
# 1 -10 2 b even NaN
# 2 10 3 b odd 6.0
# 3 10 4 b even 8.0
# 4 10 5 a odd NaN
df.loc[df['C'] == 'a', 'F'] = df['A']
df.loc[df['C'] == 'b', 'F'] = df['B']
print(df)
# A B C D E F
# 0 -10 1 a odd NaN -10.0
# 1 -10 2 b even NaN 2.0
# 2 10 3 b odd 6.0 3.0
# 3 10 4 b even 8.0 4.0
# 4 10 5 a odd NaN 10.0
也可以使用 loc 和 iloc 在列表中指定多个列。
df.loc[df['C'] == 'a', ['E', 'F']] = 100
print(df)
# A B C D E F
# 0 -10 1 a odd 100.0 100
# 1 -10 2 b even NaN 2
# 2 10 3 b odd 6.0 3
# 3 10 4 b even 8.0 4
# 4 10 5 a odd 100.0 100
对 pandas.DataFrame 进行比较操作会得到一个 bool 类型的 pandas.DataFrame,但是它不能被广播,所以像前面的例子一样给它赋值会导致错误。
print(df < 0)
# A B C D E F
# 0 True False True True False False
# 1 True False True True False False
# 2 False False True True False False
# 3 False False True True False False
# 4 False False True True False False
print(df[df < 0])
# A B C D E F
# 0 -10.0 NaN a odd NaN NaN
# 1 -10.0 NaN b even NaN NaN
# 2 NaN NaN b odd NaN NaN
# 3 NaN NaN b even NaN NaN
# 4 NaN NaN a odd NaN NaN
# df[df < 0] = 0
# TypeError: Cannot do inplace boolean setting on mixed-types with a non np.nan value
如果要对整个 pandas.DataFrame 应用条件,请使用下面描述的 where() 方法或 mask() 方法。
- pandas.DataFrame, Series 的 where() 方法
pandas.DataFrame、pandas.Series 方法有 where()。
如果指定 pandas.Series 或带有 bool 值元素的数组作为第一个参数,则 True 元素的值仍然是调用源的对象,而 False 元素的值变为 NaN。
df = pd.DataFrame({'A': [-20, -10, 0, 10, 20],
'B': [1, 2, 3, 4, 5],
'C': ['a', 'b', 'b', 'b', 'a']})
print(df)
# A B C
# 0 -20 1 a
# 1 -10 2 b
# 2 0 3 b
# 3 10 4 b
# 4 20 5 a
print(df['A'].where(df['C'] == 'a'))
# 0 -20.0
# 1 NaN
# 2 NaN
# 3 NaN
# 4 20.0
# Name: A, dtype: float64
如果指定标量值、pandas.Series 或数组作为第二个参数,则将使用该值而不是 NaN 作为 False 元素的值。与 NumPy 的 where() 函数不同,不能指定 True 值(保留原始值)。
print(df['A'].where(df['C'] == 'a', 100))
# 0 -20
# 1 100
# 2 100
# 3 100
# 4 20
# Name: A, dtype: int64
print(df['A'].where(df['C'] == 'a', df['B']))
# 0 -20
# 1 2
# 2 3
# 3 4
# 4 20
# Name: A, dtype: int64
也可以将其添加为新列。
df['D'] = df['A'].where(df['C'] == 'a', df['B'])
print(df)
# A B C D
# 0 -20 1 a -20
# 1 -10 2 b 2
# 2 0 3 b 3
# 3 10 4 b 4
# 4 20 5 a 20
参数 inplace=True 修改原始对象。
df['D'].where((df['D'] % 2 == 0) & (df['A'] < 0), df['D'] * 100, inplace=True)
print(df)
# A B C D
# 0 -20 1 a -20
# 1 -10 2 b 2
# 2 0 3 b 300
# 3 10 4 b 400
# 4 20 5 a 2000
pandas.DataFrame 也有一个 where() 方法。指定一个 pandas.DataFrame 或一个二维数组,其元素的 bool 值与调用者大小相同,作为第一个参数的条件。
print(df < 0)
# A B C D
# 0 True False True True
# 1 True False True False
# 2 False False True False
# 3 False False True False
# 4 False False True False
print(df.where(df < 0))
# A B C D
# 0 -20.0 NaN a -20.0
# 1 -10.0 NaN b NaN
# 2 NaN NaN b NaN
# 3 NaN NaN b NaN
# 4 NaN NaN a NaN
print(df.where(df < 0, df * 2))
# A B C D
# 0 -20 2 a -20
# 1 -10 4 b 4
# 2 0 6 b 600
# 3 20 8 b 800
# 4 40 10 a 4000
print(df.where(df < 0, 100))
# A B C D
# 0 -20 100 a -20
# 1 -10 100 b 100
# 2 100 100 b 100
# 3 100 100 b 100
# 4 100 100 a 100
pandas.DataFrame, Series 的 mask() 方法
pandas.DataFrame, pandas.Series 方法有 mask()。
mask() 方法与 where() 方法相反,第一个参数中条件为 False 的元素仍然是调用者的对象,True 元素变为 NaN 或第二个参数中指定的值。 其他用法同where()。
df = pd.DataFrame({'A': [-20, -10, 0, 10, 20],
'B': [1, 2, 3, 4, 5],
'C': ['a', 'b', 'b', 'b', 'a']})
print(df)
# A B C
# 0 -20 1 a
# 1 -10 2 b
# 2 0 3 b
# 3 10 4 b
# 4 20 5 a
print(df['C'].mask(df['C'] == 'a'))
# 0 NaN
# 1 b
# 2 b
# 3 b
# 4 NaN
# Name: C, dtype: object
print(df['C'].mask(df['C'] == 'a', 100))
# 0 100
# 1 b
# 2 b
# 3 b
# 4 100
# Name: C, dtype: object
df['D'] = df['A'].mask(df['C'] == 'a', df['B'])
print(df)
# A B C D
# 0 -20 1 a 1
# 1 -10 2 b -10
# 2 0 3 b 0
# 3 10 4 b 10
# 4 20 5 a 5
df['D'].mask(df['D'] % 2 != 0, df['D'] * 10, inplace=True)
print(df)
# A B C D
# 0 -20 1 a 10
# 1 -10 2 b -10
# 2 0 3 b 0
# 3 10 4 b 10
# 4 20 5 a 50
它似乎比 where() 更直观,因为第二个参数被分配给满足第一个参数条件(变为 True)的元素。 pandas.DataFrame 也有一个 mask() 方法。
print(df.mask(df < 0, -100))
# A B C D
# 0 -100 1 -100 10
# 1 -100 2 -100 -100
# 2 0 3 -100 0
# 3 10 4 -100 10
# 4 20 5 -100 50
如果只想将方法应用于包含数字和字符串的对象的数字列,如本例所示,您可以按如下方式使用 select_dtypes()。
print(df.select_dtypes(include='number').mask(df < 0, -100))
# A B D
# 0 -100 1 10
# 1 -100 2 -100
# 2 0 3 0
# 3 10 4 10
# 4 20 5 50
也可以在仅处理数字列之后连接非数字列。
df_mask = df.select_dtypes(include='number').mask(df < 0, -100)
df_mask = pd.concat([df_mask, df.select_dtypes(exclude='number')], axis=1)
print(df_mask.sort_index(axis=1))
# A B C D
# 0 -100 1 a 10
# 1 -100 2 b -100
# 2 0 3 b 0
# 3 10 4 b 10
# 4 20 5 a 50
NumPy where() 函数
使用NumPy的where()函数也可以根据条件赋值。
pandas where() 方法或 mask() 方法中,第二个参数只能是 False 或 True 时赋值的值,调用对象的值按原样用于另一个。。因此,无法执行根据条件选择值的处理(为 True 和 False 指定不同值的处理)。
在 NumPy where() 函数中,第一个参数是条件,第二个参数是要分配给条件为 True 的元素的值,第三个参数是要分配给条件为 False 的元素的值。可以为第二个和第三个参数指定标量值和数组,并通过广播分配。
numpy.where() 返回 NumPy 数组 ndarray。
一维 numpy.ndarray 可以指定为 pandas.DataFrame 的列。
df = pd.DataFrame({'A': [-20, -10, 0, 10, 20],
'B': [1, 2, 3, 4, 5],
'C': ['a', 'b', 'b', 'b', 'a']})
print(df)
# A B C
# 0 -20 1 a
# 1 -10 2 b
# 2 0 3 b
# 3 10 4 b
# 4 20 5 a
print(np.where(df['B'] % 2 == 0, 'even', 'odd'))
# ['odd' 'even' 'odd' 'even' 'odd']
print(np.where(df['C'] == 'a', df['A'], df['B']))
# [-20 2 3 4 20]
df['D'] = np.where(df['B'] % 2 == 0, 'even', 'odd')
print(df)
# A B C D
# 0 -20 1 a odd
# 1 -10 2 b even
# 2 0 3 b odd
# 3 10 4 b even
# 4 20 5 a odd
df['E'] = np.where(df['C'] == 'a', df['A'], df['B'])
print(df)
# A B C D E
# 0 -20 1 a odd -20
# 1 -10 2 b even 2
# 2 0 3 b odd 3
# 3 10 4 b even 4
# 4 20 5 a odd 20
如果指定 pandas.DataFrame 作为条件,则返回二维 numpy.ndarray。您可以使用原始 pandas.DataFrame 的索引和列创建一个 pandas.DataFrame。
print(np.where(df < 0, df, 100))
# [[-20 100 'a' 'odd' -20]
# [-10 100 'b' 'even' 100]
# [100 100 'b' 'odd' 100]
# [100 100 'b' 'even' 100]
# [100 100 'a' 'odd' 100]]
df_np_where = pd.DataFrame(np.where(df < 0, df, 100),
index=df.index, columns=df.columns)
print(df_np_where)
# A B C D E
# 0 -20 100 a odd -20
# 1 -10 100 b even 100
# 2 100 100 b odd 100
# 3 100 100 b even 100
# 4 100 100 a odd 100