Pandas多条件赋值:np.where、loc、apply详解
2025-03-03 08:23:01
多条件赋值:Pandas 数据处理技巧
处理数据时,根据多个条件给已有列赋值挺常见的。这次碰到的问题是这样:有一个 DataFrame,包含 "gender"、"pet1" 和 "pet2" 三列,要根据这三列的值来确定一个新列 "points" 的值。
具体规则如下:
a. 如果 "gender" 是 "male",并且 "pet1" 和 "pet2" 相等,那么 "points" 等于 5。
b. 如果 "gender" 是 "female",并且 "pet1" 是 "cat" 或者 "dog",那么 "points" 等于 5。
c. 其他所有情况,"points" 等于 0。
原始数据长这样:
gender pet1 pet2
0 male dog dog
1 male cat cat
2 male dog cat
3 female cat squirrel
4 female dog dog
5 female squirrel cat
6 squirrel dog cat
目标结果如下:
gender pet1 pet2 points
0 male dog dog 5
1 male cat cat 5
2 male dog cat 0
3 female cat squirrel 5
4 female dog dog 5
5 female squirrel cat 0
6 squirrel dog cat 0
下面来看看怎么用 Pandas 解决这个问题。
问题分析
这个问题的核心在于如何将多个条件组合起来,并根据这些条件判断来设置新列的值。Pandas 提供了多种方法来实现这种条件赋值,接下来我们将逐一介绍这些方法,并分析它们的优缺点。
解决方案
1. 使用 np.where()
np.where()
是 NumPy 库中的一个函数,可以根据条件返回不同的值。在 Pandas 中,可以结合多个条件使用 np.where()
来实现多条件赋值。
原理:
np.where()
函数接受三个参数:条件、满足条件时的值、不满足条件时的值。我们可以嵌套多个 np.where()
来处理多个条件。
代码示例:
import pandas as pd
import numpy as np
data = {'gender': ['male', 'male', 'male', 'female', 'female', 'female', 'squirrel'],
'pet1': ['dog', 'cat', 'dog', 'cat', 'dog', 'squirrel', 'dog'],
'pet2': ['dog', 'cat', 'cat', 'squirrel', 'dog', 'cat', 'cat']}
df = pd.DataFrame(data)
df['points'] = np.where((df['gender'] == 'male') & (df['pet1'] == df['pet2']), 5,
np.where((df['gender'] == 'female') & (df['pet1'].isin(['cat', 'dog'])), 5, 0))
print(df)
安全建议:
np.where
本身没有安全风险。 但在使用过程中,需确保条件的逻辑正确,否则可能产生意料之外的结果。
进阶使用技巧:
当条件很多,可以把条件拆分保存,增加可读性。
condition1 = (df['gender'] == 'male') & (df['pet1'] == df['pet2'])
condition2 = (df['gender'] == 'female') & (df['pet1'].isin(['cat', 'dog']))
df['points'] = np.where(condition1 | condition2, 5, 0)
2. 使用 pandas.DataFrame.loc[]
loc[]
是 Pandas 中用于基于标签进行索引和选择数据的方法。它可以用来设置满足特定条件的数据的值。
原理:
loc[]
可以接受一个布尔数组作为索引,选择 DataFrame 中满足条件的行,然后对这些行进行赋值。
代码示例:
import pandas as pd
data = {'gender': ['male', 'male', 'male', 'female', 'female', 'female', 'squirrel'],
'pet1': ['dog', 'cat', 'dog', 'cat', 'dog', 'squirrel', 'dog'],
'pet2': ['dog', 'cat', 'cat', 'squirrel', 'dog', 'cat', 'cat']}
df = pd.DataFrame(data)
df['points'] = 0 # 先全部赋值为0
df.loc[(df['gender'] == 'male') & (df['pet1'] == df['pet2']), 'points'] = 5
df.loc[(df['gender'] == 'female') & (df['pet1'].isin(['cat', 'dog'])), 'points'] = 5
print(df)
安全建议:
loc[]
没有安全风险,注意条件的正确性。
进阶使用技巧:
可以用.copy()
避免 SettingWithCopyWarning
某些 Pandas 操作可能会返回数据的视图(view)而不是副本(copy)。当尝试在视图上进行赋值时,Pandas 可能会发出 SettingWithCopyWarning
警告。
可以使用链式索引,但需要注意避免 SettingWithCopyWarning:
df = pd.DataFrame(data).copy() # 建立副本
3. 使用apply()
方法
apply()
方法可以将一个自定义函数应用到 DataFrame 的每一行或每一列。
原理:
我们可以定义一个函数,该函数接受一行数据作为输入,并根据条件返回 "points" 列的值。然后,使用 apply()
方法将这个函数应用到 DataFrame 的每一行。
代码示例:
import pandas as pd
data = {'gender': ['male', 'male', 'male', 'female', 'female', 'female', 'squirrel'],
'pet1': ['dog', 'cat', 'dog', 'cat', 'dog', 'squirrel', 'dog'],
'pet2': ['dog', 'cat', 'cat', 'squirrel', 'dog', 'cat', 'cat']}
df = pd.DataFrame(data)
def calculate_points(row):
if row['gender'] == 'male' and row['pet1'] == row['pet2']:
return 5
elif row['gender'] == 'female' and row['pet1'] in ['cat', 'dog']:
return 5
else:
return 0
df['points'] = df.apply(calculate_points, axis=1)
print(df)
安全建议:
因为apply()
使用自定义函数,要保证函数的逻辑没问题。如果自定义函数逻辑复杂或者引用外部变量,需要仔细检查。
进阶使用技巧:
可以和 lambda 函数结合使用。不过因为需要写多个判断,会显得可读性比较差。
对于非常复杂的逻辑,定义命名函数,然后在 apply()
中调用命名函数。
df['points'] = df.apply(lambda row: 5 if (row['gender'] == 'male' and row['pet1'] == row['pet2']) or \
(row['gender'] == 'female' and row['pet1'] in ['cat', 'dog']) else 0, axis=1)
4. 条件选择 + mask()
/where()
这种方法结合了条件选择和 Pandas 的 mask()
或 where()
方法。
原理:
先创建一个布尔掩码(mask),表示满足条件的行。然后,使用 mask()
或 where()
方法将不满足条件的行的值替换为 0,满足的则替换为 5。mask()
方法在条件为 True
时替换值,where()
方法在条件为 False
时替换值.
代码示例:
import pandas as pd
data = {'gender': ['male', 'male', 'male', 'female', 'female', 'female', 'squirrel'],
'pet1': ['dog', 'cat', 'dog', 'cat', 'dog', 'squirrel', 'dog'],
'pet2': ['dog', 'cat', 'cat', 'squirrel', 'dog', 'cat', 'cat']}
df = pd.DataFrame(data)
condition1 = (df['gender'] == 'male') & (df['pet1'] == df['pet2'])
condition2 = (df['gender'] == 'female') & (df['pet1'].isin(['cat', 'dog']))
df['points'] = (condition1 | condition2).mask(cond=(condition1 | condition2), other=5).fillna(0).astype(int) #或者.where(~(condition1 | condition2), 5)
# df['points']=(condition1 | condition2) 这一步会产生布尔值 True,False, .astype(int) 会把它们转换为 1,0
# 所以上一步可以简化
# df['points'] = (condition1 | condition2).astype(int) * 5
print(df)
安全建议
没有直接安全问题,但仍需小心条件和数据类型问题.
进阶使用技巧:
可以用.clip()
设置最大/最小值, 结合条件可以灵活地给某一列数值设置范围。
df['points'] = (condition1 | condition2).astype(int)
df['points'] = df['points'].clip(lower=0, upper=5) # 把结果限制到0-5之间, 小于0的变为0, 大于5的变为5. 本例中和直接 *5 等价。
总结
以上介绍了四种在 Pandas 中根据多个条件对已有列进行赋值的方法,包括使用 np.where()
、loc[]
、apply()
以及条件选择配合mask()
/where()
。
每种方法都有其适用的场景。通常来说:
np.where()
适合于条件较少的情况,代码简洁。loc[]
更直观,适合于直接修改满足条件的值。apply()
在需要使用自定义函数处理复杂逻辑时更灵活,但是要注意性能损耗。mask()
/where()
提供了基于条件的替换操作,可以对数据进行快速筛选。
根据具体数据和需求的复杂程度选择合适的方法,能更有效解决这类问题。