The where
中的方法Pandas允许您根据条件过滤 DataFrame 或 Series,类似于 SQL 的 WHERE 子句。
您是否曾经发现自己需要根据特定条件替换 DataFrame 中的某些值,或者可能想要屏蔽不符合某些条件的数据?
The where
方法用于执行此类转换任务。
Pandas where
语法和参数
The where
方法语法如下所示:
DataFrame.where(cond, other=nan, inplace=False, axis=None, level=None)
我们来分解一下这些参数:
-
cond
:这是一个条件,当满足时,保留原始值。如果不满足,则该值将被替换为中指定的值other
范围。
-
other
:条件为假时要替换的值。默认情况下,它是 NaN。
-
inplace
:是直接修改调用对象还是返回一个新的对象。默认值为 False。
-
axis
:用于对齐目的的可选参数。可以是索引 (0) 或列 (1)。
-
level
:对于具有多级索引的DataFrame,应应用操作的级别。
使用过滤数据框where
method
让我们根据标量条件执行基本过滤where
method:
import pandas as pd
data = {'Scores': [85, 90, 78, 88, 76, 95, 89]}
df = pd.DataFrame(data)
high_scores = df.where(df['Scores'] > 80)
print(high_scores)
Output:
Scores
0 85.0
1 90.0
2 NaN
3 88.0
4 NaN
5 95.0
6 89.0
从输出中,您可以看到小于或等于 80 的分数被 NaN 替换。
在接下来的部分中,我们将学习如何处理这些 NaN 值。
仅保留 80 分以上的分数high_scores
数据框。
返回类型where
Method
When where
方法应用于DataFrame,它返回一个DataFrame;当用于系列时,它返回一个系列。
让我们通过一些例子来探讨这个概念:
使用数据框
import pandas as pd
data = {
'Age': [25, 30, 35, 40],
'Salary': [50000, 60000, 70000, 80000]
}
df = pd.DataFrame(data)
young_employees = df.where(df['Age'] < 35)
print(young_employees)
Output:
Age Salary
0 25.0 50000.0
1 30.0 60000.0
2 NaN NaN
3 NaN NaN
请注意,如果不满足条件,即使只指定了一列的条件,整行也会被屏蔽(用 NaN 替换)。
使用系列
如果我们应用where
方法到 DataFrame 的单列(系列):
young_ages = df['Age'].where(df['Age'] < 35)
print(young_ages)
Output:
0 25.0
1 30.0
2 NaN
3 NaN
Name: Age, dtype: float64
这里,输出是一个 Series,而不是 DataFrame,因为我们将该方法应用于单个列。
创建带有条件的布尔掩码
的基础where
方法的功能在于根据指定条件创建布尔掩码。
这些掩码本质上是数组或一系列True
and False
值,表示每个元素是否满足给定的条件。
给定一个数据框:
import pandas as pd
data = {
'Name': ['Alice', 'Bob', 'Charlie', 'David'],
'Age': [25, 28, 30, 23]
}
df = pd.DataFrame(data)
要为 28 岁以下创建布尔掩码:
age_mask = df['Age'] < 28
print(age_mask)
Output:
0 True
1 False
2 False
3 True
Name: Age, dtype: bool
该掩码表示第一个和最后一个条目满足条件。
使用布尔蒙版应用where
然后面膜就可以直接使用了where
method:
younger_ages = df['Age'].where(age_mask)
print(younger_ages)
Output:
0 25.0
1 NaN
2 NaN
3 23.0
Name: Age, dtype: float64
使用等于和不等于
比较运算符允许我们创建条件来根据需要过滤数据。
让我们看看这些运算符在 Pandas 领域如何运作。
对于示例数据帧:
import pandas as pd
data = {
'Grade': [85, 90, 78, 88, 76, 95, 89]
}
df = pd.DataFrame(data)
Using ==
(平等):
仅过滤恰好为 88 的成绩:
exact_grade = df['Grade'].where(df['Grade'] == 88)
print(exact_grade)
Output:
0 NaN
1 NaN
2 NaN
3 88.0
4 NaN
5 NaN
6 NaN
Name: Grade, dtype: float64
不平等与不平等
The !=
当您想要排除特定值时,(不等于)运算符至关重要:
# All grades that are not 88
not_eighty_eight = df['Grade'].where(df['Grade'] != 88)
print(not_eighty_eight)
Output:
0 85.0
1 90.0
2 78.0
3 NaN
4 76.0
5 95.0
6 89.0
Name: Grade, dtype: float64
组合多个条件
通过使用链接多个条件&
, |
, and ~
运算符,您可以在 Pandas 中定义更复杂的数据过滤器。
为此,让我们继续我们的成绩示例:
import pandas as pd
data = {
'Grade': [85, 90, 78, 88, 76, 95, 89],
'Status': ['Pass', 'Pass', 'Fail', 'Pass', 'Fail', 'Pass', 'Pass']
}
df = pd.DataFrame(data)
使用 & (AND) 运算符
要过滤大于 80 且状态为“通过”的成绩:
high_passing_grades = df.where((df['Grade'] > 80) & (df['Status'] == 'Pass'))
print(high_passing_grades)
Output:
Grade Status
0 85.0 Pass
1 90.0 Pass
2 NaN NaN
3 88.0 Pass
4 NaN NaN
5 95.0 Pass
6 89.0 Pass
使用 | (或)运算符
要过滤大于 90 或状态为“失败”的成绩:
high_or_fail = df.where((df['Grade'] > 90) | (df['Status'] == 'Fail'))
print(high_or_fail)
Output:
Grade Status
0 NaN NaN
1 NaN NaN
2 78.0 Fail
3 NaN NaN
4 76.0 Fail
5 95.0 Pass
6 NaN NaN
使用 ~(非)运算符
要过滤不大于 90 的等级:
not_high_grades = df.where(~(df['Grade'] > 90))
print(not_high_grades)
Output:
Grade Status
0 85.0 Pass
1 90.0 Pass
2 78.0 Fail
3 88.0 Pass
4 76.0 Fail
5 NaN NaN
6 89.0 Pass
请记住,组合条件时,将每个条件括在括号中至关重要。
使用函数作为条件
的一个强大功能where
方法是使用可调用对象(如函数)作为过滤条件的能力。
以下是如何在where
method:
我们以下面的 DataFrame 为例:
import pandas as pd
data = {
'Grade': [85, 90, 78, 88, 76, 95, 89]
}
df = pd.DataFrame(data)
假设我们要保留奇数的成绩:
def is_odd(num):
return num % 2 == 1
odd_grades = df['Grade'].where(is_odd)
print(odd_grades)
Output:
0 85.0
1 NaN
2 NaN
3 NaN
4 NaN
5 95.0
6 89.0
Name: Grade, dtype: float64
就地修改
默认情况下,where
方法不会改变原始的 DataFrame 或 Series。相反,它返回一个经过修改的新对象:
通过设置inplace
参数为True
,原始对象被修改:
df.where(df['Grade'] > 85, inplace=True)
Now, df
直接反映了变化,保留了85以上的成绩,其他的则用NaN代替。没有返回新对象;所有修改均在df
.
Note: 当使用大量 DataFrame 时,就地修改可以提高内存效率,因为它不会创建新对象。
处理 NaN 值
正如我们在前面的示例中看到的,不满足指定条件的条目将被替换为NaN
values.
假设您希望将不满足条件的条目替换为静态值,例如 -1 而不是 NaN。
import pandas as pd
data = {
'Grade': [85, 90, 78, 88, 76, 95, 89]
}
df = pd.DataFrame(data)
graded_df = df['Grade'].where(df['Grade'] > 85, other=-1)
Output:
0 -1
1 90
2 -1
3 88
4 -1
5 95
6 89
Name: Grade, dtype: int64
对不同的条目使用不同的值
如果您希望用不同的值替换每个不合格条目怎么办?这other
参数可以处理数组或系列:
replacement_values = [10, 20, 30, 40, 50, 60, 70]
graded_df = df['Grade'].where(df['Grade'] > 85, other=replacement_values)
Output:
0 10
1 90
2 30
3 88
4 50
5 95
6 89
Name: Grade, dtype: int64
使用函数进行动态替换
对于更复杂的替换逻辑,还可以使用函数:
def custom_replacement(grade):
return grade / 2
graded_df = df['Grade'].where(df['Grade'] > 85, other=custom_replacement)
Output:
0 42.5
1 90.0
2 39.0
3 88.0
4 38.0
5 95.0
6 89.0
Name: Grade, dtype: float64
The other
中的参数where
方法提供了额外的定制层。
where 与查询或布尔索引的性能
过滤数据时,会出现三种流行的选择:where
方法,将查询方式,以及直接布尔索引。
让我们来衡量这些方法的性能。
首先,我们需要创建一个示例 DataFrame:
import pandas as pd
import numpy as np
np.random.seed(42)
df = pd.DataFrame({
'A': np.random.randint(1, 1000, 1000000),
'B': np.random.randint(1, 1000, 1000000)
})
对 where 方法进行计时
def using_where(df):
return df.where(df['A'] > 50)
定时布尔索引
def using_boolean_indexing(df):
return df[df['A'] > 50]
计时查询方法
def using_query(df):
return df.query('A > 50')
现在,让我们使用 timeit 对这些函数进行计时:
import timeit
n_repeat = 3
n_iter = 10
where_time = timeit.timeit('using_where(df)', globals=globals(), number=n_iter) / n_iter
bool_idx_time = timeit.timeit('using_boolean_indexing(df)', globals=globals(), number=n_iter) / n_iter
query_time = timeit.timeit('using_query(df)', globals=globals(), number=n_iter) / n_iter
print(f"Average time using 'where' method over {n_iter} iterations: {where_time:.6f} seconds")
print(f"Average time using boolean indexing over {n_iter} iterations: {bool_idx_time:.6f} seconds")
print(f"Average time using 'query' method over {n_iter} iterations: {query_time:.6f} seconds")
Output:
Average time using 'where' method over 10 iterations: 0.061809 seconds
Average time using boolean indexing over 10 iterations: 0.025589 seconds
Average time using 'query' method over 10 iterations: 0.029168 seconds
从上面的结果来看,布尔索引绝对是更快的方法。
Nested where
Calls
如果您想顺序应用多个条件。实现此目的的一种方法是通过链接或嵌套where
calls.
考虑一个简单的数据集:
import pandas as pd
data = {
'Grade': [85, 90, 78, 88, 76, 95, 89],
'Subject': ['Math', 'English', 'History', 'Math', 'English', 'History', 'Math']
}
df = pd.DataFrame(data)
想象一下,您想要过滤成绩高于 85 且主题为“数学”的条目的数据。
你可以连锁where
calls:
filtered_df = df.where(df['Grade'] > 85).where(df['Subject'] == 'Math')
print(filtered_df)
Output:
Grade Subject
0 NaN NaN
1 NaN NaN
2 NaN NaN
3 88.0 Math
4 NaN NaN
5 NaN NaN
6 89.0 Math
Nested where
与other
范围
您可以结合other
复杂替换的参数:
replaced_df = df['Grade'].where(df['Grade'] > 85, other=-1).where(df['Subject'] == 'Math', other=-2)
print(replaced_df)
Output:
0 -1
1 -2
2 -2
3 88
4 -2
5 -2
6 89
Name: Grade, dtype: int64
此处,低于 85 的成绩将替换为 -1,任何不是“数学”的行将进一步替换为 -2。
Resource
https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.where.html