Pandas筛选中的==与~相互成就
做审计工作,不要求我们成为数据科学家,然而我们要审计的数据日益庞大,小伙伴反馈,原来用PowerQuery设计的会计分录测试工具直接趴窝。能想到的趁手工具可能得是pandas。
处理数据过程中七八成的工作是清洗数据,而筛选是清洗的重要一环,筛选不出预期的数据,下一步就没法开展。然而,不同的语言有其特点,对于Pandas来讲——明明是很简单的字段列筛选,正向筛选能跑通,一到反向筛选就报错,对于新手排查半天可能找不到问题所在。
大概率是用错了反向筛选的符号!另外还有个新手高频误区:筛选字符串、日期字段时,不知道要搭配专属方法,直接用==筛选,要么报错要么筛不出正确结果。
今天来聊聊Pandas中的,在应用筛选时:正向筛选用==,反向筛选加~(波浪号),别再用not;字符串筛选配str,日期筛选配dt!
not为啥不好使?str/dt为啥必须用?
先解决第一个高频疑问:not为啥在pandas里“水土不服”?
我们筛选DataFrame字段时,本质上是用“条件”生成一个布尔索引(要么True,要么False),pandas会根据这个索引,保留True对应的行、过滤False对应的行。而not是Python的通用逻辑运算符,它只适合判断单个布尔值(比如not True → False);但pandas筛选时生成的是布尔数组(一堆True/False),用not否定数组,只会触发逻辑冲突,要么报错,要么结果异常。
反观~(波浪号),它是pandas专门用来否定布尔数组的运算符,能精准对应每一个布尔值取反,效率高还不报错,这也是官方更推荐的用法。
再解决第二个关键问题:为啥字符串、日期筛选要额外加str/dt?
pandas里的字符串、日期字段,和普通数字字段不一样,它们属于“特殊数据类型”,直接用==筛选,只能处理简单的“完全匹配”,一旦涉及“包含、开头/结尾匹配”(字符串)、“某天、某月筛选”(日期),就会报错——这时候必须用pandas专属的str访问器(字符串专用)和dt访问器(日期专用),相当于给筛选“加个buff”,才能精准匹配。
实操演示:分类型教学,正向+反向一步到位
我们用一个更贴近实际工作的DataFrame,包含“姓名”(字符串)、“科目”(字符串)、“成绩”(数字)、“考试日期”(日期)四个字段,对比看下正确/错误写法。
先准备测试数据:
import pandas as pd# 测试数据(含字符串、日期字段)data = {"姓名": ["张三", "李四", "王五", "赵六", "孙七"],"科目": ["数学", "语文", "数学", "英语", "数学"],"成绩": [85, 92, 78, 88, 90],"考试日期": pd.to_datetime(["2024-06-10", "2024-06-10", "2024-06-11", "2024-06-11", "2024-06-12"])}df = pd.DataFrame(data)# 查看数据类型,确认字符串(object)、日期(datetime64)print("原始数据:")print(df)print("\n数据类型:")print(df.dtypes)
一、基础筛选:数字字段(正向==,反向~)
数字字段(比如成绩)筛选最简单,不用额外加访问器,直接按核心逻辑来就行,这里快速带过,巩固基础。
示例:筛选成绩等于90(正向)、成绩不等于90(反向)
# 正向筛选:成绩 == 90(正确写法)df_90 = df[df["成绩"] == 90]# 反向筛选:成绩 != 90(正确写法:正向条件加~)df_not_90 = df[~(df["成绩"] == 90)]print("成绩为90的行:")print(df_90)print("\n成绩不为90的行:")print(df_not_90)
二、重点1:字符串筛选(必须用str)
字符串字段(比如姓名、科目),核心逻辑不变(正向==、反向~),但必须搭配str访问器——简单说,就是在字段名后面加.str,再写筛选条件。
分两种高频场景,直接套用:
场景1:完全匹配(比如科目等于数学)
# 正向筛选:科目 == 数学(必须加.str,否则偶尔报错)df_math = df[df["科目"].str == "数学"]# 反向筛选:科目 != 数学(正向条件加~)df_not_math = df[~(df["科目"].str == "数学")]print("科目为数学的行:")print(df_math)print("\n科目不为数学的行:")print(df_not_math)
场景2:模糊匹配(比如姓名包含“张”,最常用)
# 正向筛选:姓名包含“张”(str.contains,包含匹配)df_zhang = df[df["姓名"].str.contains("张")]# 反向筛选:姓名不包含“张”(加~,核心逻辑不变)df_not_zhang = df[~df["姓名"].str.contains("张")]print("姓名包含张的行:")print(df_zhang)print("\n姓名不包含张的行:")print(df_not_zhang)
小提醒:字符串筛选的坑——① 忘记加.str,尤其是模糊匹配(str.contains),直接报错;② 大小写敏感(比如“张三”和“张三 ”(带空格)不一样),如果想忽略大小写,可加case=False(比如df["姓名"].str.contains("张", case=False))。
三、重点2:日期筛选(必须用dt访问器)
日期字段(比如考试日期),和字符串逻辑一致,核心还是“正向==、反向~”,但必须搭配dt访问器(字段名后加.dt),常用场景是“筛选某天、某月、某年”。
场景1:筛选具体某一天(比如2024-06-10)
# 正向筛选:考试日期 == 2024-06-10(必须加.dt.date,匹配日期本身)df_date10 = df[df["考试日期"].dt.date == pd.to_datetime("2024-06-10").date()]# 反向筛选:考试日期 != 2024-06-10(加~)df_not_date10 = df[~(df["考试日期"].dt.date == pd.to_datetime("2024-06-10").date())]print("考试日期为2024-06-10的行:")print(df_date10)print("\n考试日期不为2024-06-10的行:")print(df_not_date10)
场景2:筛选具体某个月(比如6月)
# 正向筛选:考试日期为6月份(dt.month,提取月份)df_june = df[df["考试日期"].dt.month == 6]# 反向筛选:考试日期不为6月份(加~)df_not_june = df[~(df["考试日期"].dt.month == 6)]print("6月份考试的行:")print(df_june)print("\n非6月份考试的行:")print(df_not_june)
小提醒:日期筛选的坑——① 忘记加.dt,直接用df["考试日期"] == "2024-06-10",会因格式不匹配筛不出结果;② 日期格式要统一,建议先用pd.to_datetime()将字段转为datetime类型(如测试数据所示)。
最容易踩的4个坑,避错
结合自己刚开始学的经历,还有平时帮同事排查的问题,总结了4个新手最容易踩的坑,尤其是反向筛选和str/dt使用时,一定要注意:
坑1:用not代替~ → 直接报错,记住:pandas筛选反向,只认~,不认not;
坑2:~的位置放错,不加括号 → 比如写成df[df["科目"].str ~== "数学"],语法错误,~必须放在整个条件前面;
坑3:字符串/日期筛选,忘记加str/dt → 字符串模糊匹配(str.contains)、日期提取(dt.month)直接报错,筛不出正确结果;
坑4:筛选字符串时,忘记加引号 → 比如df[df["科目"].str == 数学],报错,字符串条件一定要加引号。
总结一句
pandas的DataFrame字段筛选,核心3个关键点:
正向筛选:所有字段通用 == ,简单粗暴不出错;
反向筛选:所有字段通用 ~ ,别碰not,记得加括号;
特殊字段:字符串筛选加.str ,日期筛选加.dt ,缺一不可。
不管是简单的“等于/不等于”,还是复杂的“包含/某月筛选”,按这个逻辑来,筛选永远不会报错,多练习。
骐骥一跃,不能十步;驽马十驾,功在不舍。