github star gitee star atomgit star PyPI Downloads AI 编程 AI 交流群

大家好,我是正在实战各种AI项目的程序员晚枫。

今天学习Pandas数据筛选与查询,这是数据分析最频繁的操作。

掌握这5种方法,无论数据量多大,你都能快速找到想要的数据。这些技巧在项目1:销售数据分析报表自动化中会大量使用。


准备数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import pandas as pd
import numpy as np

# 创建电商销售数据示例(与项目1数据格式一致)
np.random.seed(42)

# 生成30天的销售数据
dates = pd.date_range(start='2024-01-01', periods=30, freq='D')

data = []
for date in dates:
n_orders = np.random.randint(5, 15)
for _ in range(n_orders):
data.append({
'订单ID': f"ORD{np.random.randint(100000, 999999)}",
'日期': date,
'产品类别': np.random.choice(['电子产品', '服装', '食品', '家居']),
'产品名称': np.random.choice(['iPhone', 'T恤', '零食', '杯子', '耳机', '鞋子']),
'单价': np.random.randint(50, 5000),
'数量': np.random.randint(1, 5),
'地区': np.random.choice(['华北', '华东', '华南', '西南']),
'销售员': np.random.choice(['张三', '李四', '王五', '赵六', '钱七'])
})

df = pd.DataFrame(data)
df['金额'] = df['单价'] * df['数量']

print(f"数据集大小: {len(df)} 条销售记录")
print("\n前5行数据:")
print(df.head())

方法1:布尔索引(最常用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 单条件筛选:金额大于1000的订单
high_value = df[df['金额'] > 1000]
print(f"高价值订单: {len(high_value)} 条")

# 多条件:与 (&) - 电子产品且金额大于2000
tech_high = df[(df['产品类别'] == '电子产品') & (df['金额'] > 2000)]

# 多条件:或 (|) - 华北或华东地区的订单
north_east = df[(df['地区'] == '华北') | (df['地区'] == '华东')]

# 非 (~) - 排除食品类订单
not_food = df[~(df['产品类别'] == '食品')]

print("高价值订单示例:")
print(high_value[['订单ID', '产品名称', '金额']].head())

方法2:isin - 多值匹配

1
2
3
4
5
6
7
8
9
10
11
# 匹配多个产品类别(比多个或条件简洁)
selected_categories = df[df['产品类别'].isin(['电子产品', '家居'])]

# 匹配多个地区
hot_regions = df[df['地区'].isin(['华北', '华东', '华南'])]

# 结合取反 - 排除某些地区
not_southwest = df[~df['地区'].isin(['西南'])]

print(f"选中类别订单数: {len(selected_categories)}")
print(f"热门地区订单数: {len(hot_regions)}")

方法3:query - 类SQL语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 简单查询:金额大于1000
result = df.query('金额 > 1000')

# 多条件:电子产品且金额大于2000
result = df.query('产品类别 == "电子产品" and 金额 > 2000')

# 使用变量
min_amount = 1500
result = df.query('金额 > @min_amount') # @表示外部变量

# 字符串匹配:订单ID包含特定数字
result = df.query('订单ID.str.contains("123")', engine='python')

print(f"Query筛选结果: {len(result)} 条")

优点:代码可读性更好,特别是复杂条件时。


方法4:loc - 标签索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 设置订单ID为索引(方便演示)
df_indexed = df.set_index('订单ID')

# 选择单行
print("单个订单详情:")
print(df_indexed.iloc[0]) # 第一行

# 选择多行
print("\n前3个订单:")
print(df_indexed.iloc[0:3])

# 行列同时选择 - 只看产品名称和金额两列
print("\n指定列:")
print(df_indexed.iloc[0:5][['产品名称', '金额']])

# 条件筛选 + 列选择
print("\n高价值订单的产品和地区:")
print(df_indexed.loc[df_indexed['金额'] > 2000, ['产品类别', '地区', '金额']].head())

方法5:iloc - 位置索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 第0行
print("第1条记录:")
print(df.iloc[0])

# 前5行
print("\n前5条记录:")
print(df.iloc[0:5])

# 特定行
print("\n第1、3、5条记录:")
print(df.iloc[[0, 2, 4]])

# 行列切片 - 前10行的前4列
print("\n前10行前4列:")
print(df.iloc[0:10, 0:4])

# 倒数几行
print("\n最后3条记录:")
print(df.iloc[-3:])

字符串匹配筛选

1
2
3
4
5
6
7
8
9
10
11
# 产品名称包含"iPhone"
iphone_orders = df[df['产品名称'].str.contains('iPhone', na=False)]

# 以特定字符开头
ord_orders = df[df['订单ID'].str.startswith('ORD1', na=False)]

# 正则表达式 - 订单ID中间是234
pattern_orders = df[df['订单ID'].str.match(r'ORD234.*', na=False)]

print(f"iPhone订单数: {len(iphone_orders)}")
print(f"ORD1开头订单数: {len(ord_orders)}")

日期筛选

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 筛选某天的数据
day_orders = df[df['日期'] == '2024-01-15']

# 筛选某月的数据
df['月份'] = df['日期'].dt.month
jan_orders = df[df['月份'] == 1]

# 日期范围
range_orders = df[(df['日期'] >= '2024-01-10') & (df['日期'] <= '2024-01-20')]

# 最近N天
from datetime import datetime, timedelta
recent = df[df['日期'] >= df['日期'].max() - timedelta(days=7)]

print(f"1月订单数: {len(jan_orders)}")
print(f"日期范围内订单数: {len(range_orders)}")
print(f"最近7天订单数: {len(recent)}")

高效技巧

1. 链式操作

1
2
3
4
5
6
7
8
9
10
11
12
# 不推荐:多次赋值
filtered = df[df['产品类别'] == '电子产品']
result = filtered[filtered['金额'] > 2000]
result = result.sort_values('金额', ascending=False)

# 推荐:链式写法(一行搞定)
result = (df[df['产品类别'] == '电子产品']
.query('金额 > 2000')
.sort_values('金额', ascending=False))

print("链式操作结果:")
print(result[['产品名称', '金额', '地区']].head())

2. 使用copy避免警告

1
2
3
4
5
6
7
# 会出现SettingWithCopyWarning
subset = df[df['产品类别'] == '电子产品']
subset['折扣'] = 0.9 # 警告!

# 正确做法
subset = df[df['产品类别'] == '电子产品'].copy()
subset['折扣'] = 0.9 # 没问题

3. 重置索引

1
2
3
4
5
6
7
# 筛选后索引不连续
filtered = df[df['金额'] > 2000]
print(f"原索引: {list(filtered.index[:5])}") # 可能是不连续的

# 重置索引
filtered = filtered.reset_index(drop=True)
print(f"新索引: {list(filtered.index[:5])}") # 0, 1, 2, 3, 4

实战:销售数据复杂筛选场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 场景:找出华东区、电子产品、金额大于3000、由张三或李四销售的高价值订单
# 用于:计算高价值订单占比、给优秀销售员发奖金

# 复杂筛选
high_value_orders = (
df[
(df['地区'] == '华东') &
(df['产品类别'] == '电子产品') &
(df['金额'] > 3000) &
(df['销售员'].isin(['张三', '李四']))
]
.sort_values('金额', ascending=False)
.reset_index(drop=True)
)

print(f"符合条件的高价值订单: {len(high_value_orders)} 条")
print("\n订单详情:")
print(high_value_orders[['订单ID', '产品名称', '金额', '销售员', '日期']])

# 统计这些订单的总金额
total_amount = high_value_orders['金额'].sum()
print(f"\n这些订单总金额: ¥{total_amount:,.0f}")

# 按销售员分组统计
sales_by_person = high_value_orders.groupby('销售员').agg({
'订单ID': 'count',
'金额': 'sum'
}).rename(columns={'订单ID': '订单数'})
print("\n按销售员统计:")
print(sales_by_person)

性能对比:不同筛选方式的速度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import pandas as pd
import numpy as np

df = pd.DataFrame({
'id': range(100000),
'city': np.random.choice(['北京', '上海', '广州', '深圳'], 100000),
'age': np.random.randint(18, 65, 100000),
'salary': np.random.randint(5000, 30000, 100000),
'dept': np.random.choice(['技术', '市场', '财务', '运营'], 100000)
})

# 方式1:布尔索引
%timeit df[(df['age'] > 30) & (df['salary'] > 15000)]

# 方式2:query方法
%timeit df.query('age > 30 & salary > 15000')

# 方式3:loc
%timeit df.loc[(df['age'] > 30) & (df['salary'] > 15000)]

# 典型结果:三者速度接近,query可读性最好

进阶用法

query高级用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 引用外部变量
min_salary = 15000
target_cities = ['北京', '上海']
result = df.query('salary > @min_salary and city in @target_cities')

# 字符串方法
result = df.query('city.str.startswith("北")')

# 索引查询
df_idx = df.set_index('city')
result = df_idx.query('index == "北京"')

# 多条件组合
result = df.query('(age > 30 & salary > 15000) | (dept == "技术" & age < 25)')

where和mask

1
2
3
4
5
6
7
8
9
# where:不满足条件的替换为NaN
df['salary'].where(df['salary'] > 10000, other='低薪')

# mask:满足条件的替换(与where相反)
df['salary'].mask(df['salary'] < 10000, other='低薪')

# 实战:给薪资分等级
df['salary_level'] = df['salary'].where(df['salary'] >= 20000, '一般')
df['salary_level'] = df['salary_level'].where(df['salary'] >= 20000, '低薪')

isin多值匹配

1
2
3
4
5
6
7
8
9
10
# 选取多个城市的员工
target_cities = ['北京', '上海', '深圳']
result = df[df['city'].isin(target_cities)]

# 反选:不在这些城市的
result = df[~df['city'].isin(target_cities)]

# 搭配字典做映射
city_region = {'北京': '华北', '上海': '华东', '广州': '华南', '深圳': '华南'}
df['region'] = df['city'].map(city_region)

避坑指南

❌ 坑1:链式布尔条件忘记括号

1
2
3
4
5
# 错误写法!
result = df[df['age'] > 30 & df['salary'] > 15000] # 报错!

# 正确写法:每个条件加括号
result = df[(df['age'] > 30) & (df['salary'] > 15000)]

❌ 坑2:str方法遇到NaN

1
2
3
4
5
# 如果列中有NaN,str方法会报错
df['city'].str.contains('北') # 有NaN时会返回NaN

# 解决方案
df['city'].str.contains('北', na=False) # NaN视为False

实战案例:HR部门筛选候选人

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import pandas as pd
import numpy as np

# 模拟候选人数据
np.random.seed(42)
n = 5000
df = pd.DataFrame({
'name': [f'候选人{i:04d}' for i in range(n)],
'age': np.random.randint(22, 50, n),
'education': np.random.choice(['本科', '硕士', '博士'], n, p=[0.6, 0.3, 0.1]),
'experience': np.random.randint(0, 20, n),
'expected_salary': np.random.randint(8000, 35000, n),
'skill_score': np.random.uniform(40, 100, n).round(1),
'city': np.random.choice(['北京', '上海', '广州', '深圳', '成都', '重庆', '杭州'], n)
})

# 需求:筛选高潜候选人
# 条件:技能分>75 + 期望薪资<25k + 经验3-10年 + 一线城市
candidates = df.query(
'skill_score > 75 and expected_salary < 25000 '
'and 3 <= experience <= 10 '
'and city in ["北京", "上海", "深圳", "杭州"]'
)

print(f"符合条件的候选人: {len(candidates)} 人")
print(f"\n候选人概览:")
print(candidates[['name', 'age', 'education', 'experience', 'skill_score', 'expected_salary', 'city']].head(10))

# 按技能分排序
top10 = candidates.nlargest(10, 'skill_score')
print(f"\nTop 10候选人:")
print(top10[['name', 'skill_score', 'expected_salary']])

多表关联筛选

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 场景:从订单表筛选VIP用户的订单
orders = pd.DataFrame({
'order_id': [1, 2, 3, 4, 5],
'user_id': [101, 102, 103, 104, 105],
'amount': [500, 300, 800, 200, 1000]
})

vip_users = pd.DataFrame({
'user_id': [101, 103, 105],
'vip_level': [3, 2, 3]
})

# 方法1:isin
vip_orders = orders[orders['user_id'].isin(vip_users['user_id'])]

# 方法2:merge
vip_orders = orders.merge(vip_users, on='user_id')

# 方法3:join(需设索引)
vip_orders = orders.set_index('user_id').join(vip_users.set_index('user_id'), how='inner')

nlargest和nsmallest

1
2
3
4
5
6
# 取Top N(比sort_values + head更快)
df.nlargest(10, 'salary') # 薪资最高的10人
df.nsmallest(5, 'age') # 年龄最小的5人

# 多列排序取Top N
df.nlargest(10, ['salary', 'age']) # 先按salary,再按age

按数据类型筛选列

1
2
3
4
5
6
7
8
9
10
11
# 只选数值列
numeric_cols = df.select_dtypes(include=['int64', 'float64'])

# 只选字符串列
str_cols = df.select_dtypes(include=['object'])

# 排除某类型
exclude_date = df.select_dtypes(exclude=['datetime64'])

# 选分类列
cat_cols = df.select_dtypes(include=['category'])

多条件筛选速查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# AND条件
df[(df['age'] > 30) & (df['city'] == '北京')]

# OR条件
df[(df['city'] == '北京') | (df['city'] == '上海')]

# IN条件
df[df['city'].isin(['北京', '上海', '深圳'])]

# NOT条件
df[~df['city'].isin(['北京', '上海'])]

# BETWEEN条件
df[df['age'].between(25, 35)]

# 字符串包含
df[df['name'].str.contains('张')]

# 正则匹配
df[df['email'].str.match(r'^[\w.]+@company\.com$')]

# 空值筛选
df[df['email'].isna()] # 选空值
df[df['email'].notna()] # 选非空

# query版本
df.query('age > 30 and city in ["北京", "上海"]')
df.query('25 <= age <= 35')

筛选后操作的常见模式

1
2
3
4
5
6
7
8
9
10
11
# 模式1:筛选+修改
df.loc[df['age'] > 60, 'status'] = '退休'

# 模式2:筛选+统计
df[df['city'] == '北京']['salary'].describe()

# 模式3:筛选+排序+取TopN
df[df['year'] == 2025].nlargest(10, 'salary')

# 模式4:筛选+分组
df[df['status'] == 'active'].groupby('city').size()

下节预告

下一课我们将学习数据清洗-处理缺失值,这是数据分析中最重要的环节。干净的输入才有可靠的输出。

👉 继续阅读:Pandas数据清洗-处理缺失值


💬 加入学习交流群

扫码加入Python学习交流群,和数千名同学一起进步:

👉 点击加入交流群

群里不定期分享:

  • 数据分析实战案例
  • Python学习资料
  • 求职面试经验
  • 行业最新动态

推荐:AI Python数据分析实战营

🎁 限时福利:送《利用Python进行数据分析》实体书

👉 点击了解详情


课程导航

上一篇: Pandas数据读取与保存

下一篇: Pandas数据清洗-处理缺失值


PS:数据筛选是日常工作中最高频的操作。熟练掌握这5种方法,效率提升10倍。这些技巧在项目1的销售报表自动化中会大量用到。



📚 推荐教材

主教材《Excel+Python 飞速搞定数据分析与处理(图灵出品)》

💬 联系我

平台账号/链接
微信扫码加好友
微博@程序员晚枫
知乎@程序员晚枫
抖音@程序员晚枫
小红书@程序员晚枫
B 站Python 自动化办公社区

主营业务:AI 编程培训、企业内训、技术咨询

🎓 AI 编程实战课程

想系统学习 AI 编程?程序员晚枫的 AI 编程实战课 帮你从零上手!