使用Pandas进行高效数据筛选的实用指南与常见问题解析

引言
Pandas是Python中最流行的数据处理库,它提供了强大而灵活的数据结构和数据分析工具。在数据科学和数据分析的工作流程中,数据筛选是最基础也是最重要的操作之一。高效的数据筛选不仅能提高代码执行效率,还能让数据分析过程更加清晰和可靠。本文将深入探讨Pandas数据筛选的各种方法,从基础到高级,并解析常见问题和最佳实践。
1. 基础筛选方法
1.1 布尔索引(Boolean Indexing)
布尔索引是Pandas中最常用的数据筛选方法,它通过创建布尔条件来选择满足特定条件的行。
import pandas as pd
import numpy as np

# 创建示例数据
data = {
‘姓名’: [‘张三’, ‘李四’, ‘王五’, ‘赵六’, ‘钱七’],
‘年龄’: [25, 30, 35, 28, 32],
‘城市’: [‘北京’, ‘上海’, ‘广州’, ‘深圳’, ‘上海’],
‘薪资’: [8000, 12000, 15000, 9000, 13000],
‘部门’: [‘技术’, ‘销售’, ‘技术’, ‘人事’, ‘销售’]
}
df = pd.DataFrame(data)

# 基础布尔索引示例
# 筛选年龄大于30的员工
older_employees = df[df[‘年龄’] > 30]
print(“年龄大于30的员工:”)
print(older_employees)

# 筛选城市为上海的员工
shanghai_employees = df[df[‘城市’] == ‘上海’]
print(“\n上海的员工:”)
print(shanghai_employees)

# 多条件筛选(使用&、|、~)
# 筛选技术部门且薪资大于10000的员工
tech_high_salary = df[(df[‘部门’] == ‘技术’) & (df[‘薪资’] > 10000)]
print(“\n技术部门且薪资大于10000的员工:”)
print(tech_high_salary)

# 筛选城市为北京或上海的员工
beijing_shanghai = df[(df[‘城市’] == ‘北京’) | (df[‘城市’] == ‘上海’)]
print(“\n北京或上海的员工:”)
print(beijing_shanghai)
1.2 使用
query()
方法
query()
方法提供了一种更简洁、更易读的查询语法,特别适合复杂的条件筛选。
# 使用query()方法进行筛选
# 筛选年龄大于30的员工
result1 = df.query(‘年龄 > 30’)
print(“query()筛选年龄>30:”)
print(result1)

# 多条件查询
result2 = df.query(‘部门 == “技术” and 薪资 > 10000’)
print(“\nquery()筛选技术部门且薪资>10000:”)
print(result2)

# 使用变量
min_salary = 10000
result3 = df.query(‘薪资 > @min_salary’)
print(“\nquery()使用变量筛选薪资>10000:”)
print(result3)

# 复杂查询
result4 = df.query(‘城市 in [“北京”, “上海”] and 年龄 < 35') print("\nquery()复杂查询:") print(result4) 1.3 使用 loc[] 和 iloc[] loc[] 基于标签进行索引, iloc[] 基于位置进行索引,它们都可以用于数据筛选。 # 使用loc[]进行筛选 # 筛选年龄大于30的行,选择特定列 result_loc = df.loc[df['年龄'] > 30, [‘姓名’, ‘城市’, ‘薪资’]]
print(“loc[]筛选:”)
print(result_loc)

# 使用iloc[]进行位置筛选
# 筛选前3行,第1-3列(0-based索引)
result_iloc = df.iloc[0:3, 0:3]
print(“\niloc[]筛选:”)
print(result_iloc)

# 结合使用
# 筛选满足条件的行,选择所有列
condition = df[‘城市’] == ‘上海’
result_combined = df.loc[condition, :]
print(“\nloc[]结合条件筛选所有列:”)
print(result_combined)
2. 高级筛选技巧
2.1 使用
isin()
进行集合筛选
当需要筛选的值在一个列表或集合中时,
isin()
方法非常有用。
# 使用isin()筛选
target_cities = [‘北京’, ‘上海’]
city_filter = df[‘城市’].isin(target_cities)
result = df[city_filter]
print(“使用isin()筛选特定城市:”)
print(result)

# 反向筛选(使用~)
not_target_cities = df[~df[‘城市’].isin([‘广州’, ‘深圳’])]
print(“\n使用~和isin()排除特定城市:”)
print(not_target_cities)

# 结合其他条件
result_combined = df[df[‘城市’].isin([‘北京’, ‘上海’]) & (df[‘薪资’] > 10000)]
print(“\nisin()结合其他条件:”)
print(result_combined)
2.2 使用
str.contains()
进行文本匹配
对于字符串列,可以使用
str.contains()
进行模糊匹配。
# 创建包含更多文本数据的示例
text_data = {
‘项目名称’: [‘项目A-前端开发’, ‘项目B-后端开发’, ‘项目C-数据分析’, ‘项目D-前端测试’, ‘项目E-运维’],
‘负责人’: [‘张三’, ‘李四’, ‘王五’, ‘赵六’, ‘钱七’],
‘预算’: [100000, 150000, 80000, 90000, 120000]
}
df_text = pd.DataFrame(text_data)

# 筛选包含”前端”的项目
frontend_projects = df_text[df_text[‘项目名称’].str.contains(‘前端’)]
print(“包含’前端’的项目:”)
print(frontend_projects)

# 不区分大小写筛选
case_insensitive = df_text[df_text[‘项目名称’].str.contains(‘开发’, case=False)]
print(“\n不区分大小写筛选’开发’:”)
print(case_insensitive)

# 使用正则表达式
regex_projects = df_text[df_text[‘项目名称’].str.contains(r’项目[ABC]’)]
print(“\n使用正则表达式筛选项目A、B、C:”)
print(regex_projects)

# 反向筛选(不包含)
not_frontend = df_text[~df_text[‘项目名称’].str.contains(‘前端’)]
print(“\n不包含’前端’的项目:”)
print(not_frontend)
2.3 使用
between()
进行范围筛选
between()
方法用于筛选在某个范围内的值。
# 使用between()筛选年龄在25-30之间的员工
age_range = df[df[‘年龄’].between(25, 30)]
print(“年龄在25-30之间的员工:”)
print(age_range)

# 使用between()筛选薪资在10000-13000之间的员工
salary_range = df[df[‘薪资’].between(10000, 13000, inclusive=’both’)]
print(“\n薪资在10000-13000之间的员工:”)
print(salary_range)

# 使用between()结合其他条件
result = df[df[‘年龄’].between(25, 30) & (df[‘城市’] == ‘上海’)]
print(“\n年龄在25-30之间且城市为上海的员工:”)
print(result)
2.4 使用
filter()
方法
filter()
方法可以根据列名、索引或函数来筛选DataFrame。
# 创建包含多个列的DataFrame
df_multi = pd.DataFrame({
‘A’: [1, 2, 3, 4],
‘B’: [5, 6, 7, 8],
‘C’: [9, 10, 11, 12],
‘D’: [13, 14, 15, 16]
})

# 根据列名筛选(包含特定字符)
result = df_multi.filter(items=[‘A’, ‘C’])
print(“筛选特定列:”)
print(result)

# 使用like参数筛选列名包含’B’的列
result_like = df_multi.filter(like=’B’, axis=1)
print(“\n筛选列名包含’B’的列:”)
print(result_like)

# 使用正则表达式筛选列名
result_regex = df_multi.filter(regex=r'[A-C]’, axis=1)
print(“\n使用正则表达式筛选列名:”)
print(result_regex)
2.5 使用
where()
和
mask()
where()
和
mask()
方法用于条件替换,但也可以用于筛选。
# 使用where()进行条件筛选
# 保留满足条件的值,不满足的设为NaN
result_where = df.where(df[‘年龄’] > 30)
print(“where()筛选(保留>30,其他为NaN):”)
print(result_where)

# 使用mask()进行条件筛选(与where相反)
result_mask = df.mask(df[‘年龄’] <= 30) print("\nmask()筛选(保留>30,其他为NaN):”)
print(result_mask)

# 结合dropna()使用
result_clean = df.where(df[‘年龄’] > 30).dropna()
print(“\nwhere()结合dropna()筛选:”)
print(result_clean)
3. 性能优化技巧
3.1 避免链式索引(Chained Indexing)
链式索引会导致
SettingWithCopyWarning
,并且可能产生不可预测的结果。
# 错误的做法(链式索引)
# df[df[‘年龄’] > 30][‘薪资’] = 15000 # 这会引发警告

# 正确的做法1:使用loc
df.loc[df[‘年龄’] > 30, ‘薪资’] = 15000
print(“使用loc修改数据:”)
print(df)

# 正确的做法2:使用where()或mask()
df_original = pd.DataFrame(data) # 重新创建原始数据
df_original[‘薪资’] = df_original[‘薪资’].where(df_original[‘年龄’] <= 30, 15000) print("\n使用where()修改数据:") print(df_original) 3.2 使用向量化操作 向量化操作比循环更快,应尽量使用。 # 创建大型数据集测试性能 np.random.seed(42) large_df = pd.DataFrame({ 'id': range(100000), 'value': np.random.randint(1, 1000, 100000), 'category': np.random.choice(['A', 'B', 'C', 'D'], 100000) }) # 向量化筛选(快) vectorized_result = large_df[large_df['value'] > 500]
print(f”向量化筛选结果: {len(vectorized_result)} 行”)

# 使用apply()(相对较慢)
# apply_result = large_df[large_df[‘value’].apply(lambda x: x > 500)]
# print(f”apply()筛选结果: {len(apply_result)} 行”)

# 使用query()(通常很快)
query_result = large_df.query(‘value > 500’)
print(f”query()筛选结果: {100000 – len(query_result)} 行”)
3.3 使用
eval()
进行高效计算
eval()
可以用于高效的表达式计算,特别适合大型数据集。
# 使用eval()进行复杂筛选
# 创建示例数据
df_eval = pd.DataFrame({
‘a’: np.random.randint(1, 100, 1000),
‘b’: np.random.randint(1, 100, 1000),
‘c’: np.random.randint(1, 100, 1000)
})

# 使用eval()进行条件筛选
result_eval = df_eval.query(‘a + b > 100 and c < 50') print(f"使用eval()筛选结果: {len(result_eval)} 行") # 使用eval()创建新列 df_eval.eval('d = a * b + c', inplace=True) print("\n使用eval()创建新列d:") print(df_eval.head()) 3.4 使用 numba 加速(高级) 对于特别复杂的筛选逻辑,可以使用 numba 库来加速。 # 注意:需要安装numba库 # pip install numba try: from numba import jit @jit(nopython=True) def custom_filter(values): result = np.zeros(len(values), dtype=np.bool_) for i in range(len(values)): # 自定义筛选逻辑 if values[i] > 500 and values[i] < 800: result[i] = True return result # 应用自定义筛选函数 mask = custom_filter(large_df['value'].values) numba_result = large_df[mask] print(f"使用numba加速筛选结果: {len(numba_result)} 行") except ImportError: print("numba库未安装,跳过此示例") 4. 多索引(MultiIndex)筛选 4.1 创建多索引DataFrame # 创建多索引DataFrame arrays = [ ['销售', '销售', '技术', '技术', '人事'], ['张三', '李四', '王五', '赵六', '钱七'] ] index = pd.MultiIndex.from_arrays(arrays, names=['部门', '姓名']) multi_df = pd.DataFrame({ '年龄': [28, 32, 25, 30, 35], '薪资': [12000, 15000, 8000, 10000, 9000] }, index=index) print("多索引DataFrame:") print(multi_df) 4.2 多索引筛选方法 # 使用loc进行多索引筛选 # 筛选技术部门 tech_dept = multi_df.loc['技术'] print("技术部门:") print(tech_dept) # 筛选技术部门的王五 wang_wu = multi_df.loc[('技术', '王五')] print("\n技术部门王五:") print(wang_wu) # 使用slice进行范围筛选 # 筛选销售部门的所有员工 sales_dept = multi_df.loc['销售', :] print("\n销售部门所有员工:") print(sales_dept) # 使用query()进行多索引筛选 result = multi_df.query('部门 == "技术" and 薪资 > 8000′)
print(“\nquery()多索引筛选:”)
print(result)
5. 常见问题解析
5.1 SettingWithCopyWarning问题
这是Pandas中最常见的警告之一,通常发生在链式索引时。
# 问题示例
df_problem = pd.DataFrame(data)

# 可能引发警告的代码
# df_problem[df_problem[‘年龄’] > 30][‘薪资’] = 15000 # 警告!

# 解决方案1:使用loc
df_problem.loc[df_problem[‘年龄’] > 30, ‘薪资’] = 15000
print(“解决方案1 – 使用loc:”)
print(df_problem)

# 解决方案2:使用copy()
df_copy = df[df[‘年龄’] > 30].copy()
df_copy[‘薪资’] = 15000
print(“\n解决方案2 – 使用copy():”)
print(df_copy)

# 解决方案3:使用where()
df_original = pd.DataFrame(data)
df_original[‘薪资’] = df_original[‘薪资’].where(df_original[‘年龄’] <= 30, 15000) print("\n解决方案3 - 使用where():") print(df_original) 5.2 内存使用问题 处理大型数据集时,内存管理很重要。 # 优化内存使用的技巧 # 1. 使用适当的数据类型 df_memory = pd.DataFrame({ 'id': pd.Series(range(100000), dtype='int32'), # int32比int64节省内存 'value': pd.Series(np.random.randint(1, 1000, 100000), dtype='int16'), # int16更节省 'category': pd.Categorical(np.random.choice(['A', 'B', 'C'], 100000)) # 分类类型节省内存 }) print("优化数据类型后的内存使用:") print(df_memory.info()) # 2. 分块处理大型数据集 def process_large_file(file_path, chunk_size=10000): results = [] for chunk in pd.read_csv(file_path, chunksize=chunk_size): # 处理每个chunk filtered_chunk = chunk[chunk['value'] > 500]
results.append(filtered_chunk)
return pd.concat(results, ignore_index=True)

# 3. 使用query()减少内存占用
# query()通常比布尔索引更节省内存
large_df = pd.DataFrame({
‘a’: np.random.randint(1, 1000, 100000),
‘b’: np.random.randint(1, 1000, 100000),
‘c’: np.random.randint(1, 1000, 100000)
})

# 内存更高效的筛选
result_efficient = large_df.query(‘a > 500 and b < 800') print(f"\n高效筛选结果: {len(result_efficient)} 行") 5.3 处理缺失值(NaN)的筛选 # 创建包含缺失值的DataFrame df_nan = pd.DataFrame({ 'A': [1, 2, np.nan, 4, 5], 'B': [np.nan, 2, 3, np.nan, 5], 'C': [1, 2, 3, 4, 5] }) print("包含NaN的DataFrame:") print(df_nan) # 筛选包含NaN的行 rows_with_nan = df_nan[df_nan.isnull().any(axis=1)] print("\n包含NaN的行:") print(rows_with_nan) # 筛选不包含NaN的行 rows_without_nan = df_nan.dropna() print("\n不包含NaN的行:") print(rows_without_nan) # 筛选特定列不包含NaN的行 rows_A_not_nan = df_nan[df_nan['A'].notna()] print("\nA列不包含NaN的行:") print(rows_A_not_nan) # 使用fillna()填充后筛选 df_filled = df_nan.fillna(0) result = df_filled[df_filled['A'] > 2]
print(“\nfillna()后筛选:”)
print(result)
5.4 性能瓶颈诊断
import time

# 测试不同方法的性能
def benchmark_filtering_methods(df, condition):
methods = {
‘布尔索引’: lambda: df[df[‘value’] > condition],
‘query()’: lambda: df.query(f’value > {condition}’),
‘loc’: lambda: df.loc[df[‘value’] > condition],
‘eval()’: lambda: df.eval(f’value > {condition}’),
‘apply()’: lambda: df[df[‘value’].apply(lambda x: x > condition)]
}

results = {}
for name, method in methods.items():
start_time = time.time()
result = method()
end_time = time.time()
results[name] = end_time – start_time
print(f”{name}: {end_time – start_time:.6f} 秒,结果行数: {len(result)}”)

return results

# 使用之前创建的large_df进行测试
print(“\n性能测试(10万行数据):”)
benchmark_filtering_methods(large_df, 500)
5.5 筛选结果的索引处理
# 筛选后索引的处理问题
df_index = pd.DataFrame({
‘name’: [‘Alice’, ‘Bob’, ‘Charlie’, ‘David’],
‘score’: [85, 92, 78, 88]
}, index=[‘a’, ‘b’, ‘c’, ‘d’])

# 筛选后保留原始索引
filtered = df_index[df_index[‘score’] > 80]
print(“筛选结果(保留原始索引):”)
print(filtered)

# 重置索引
filtered_reset = filtered.reset_index(drop=True)
print(“\n重置索引后:”)
print(filtered_reset)

# 筛选后保留特定索引
filtered_with_index = df_index.loc[df_index[‘score’] > 80, :]
print(“\n使用loc保留索引:”)
print(filtered_with_index)
6. 实际应用案例
6.1 电商数据分析案例
# 创建电商销售数据
np.random.seed(42)
ecommerce_data = pd.DataFrame({
‘order_id’: range(1000, 1500),
‘customer_id’: np.random.randint(10000, 10050, 500),
‘product’: np.random.choice([‘手机’, ‘电脑’, ‘平板’, ‘耳机’], 500),
‘price’: np.random.randint(100, 1000, 500),
‘quantity’: np.random.randint(1, 5, 500),
‘category’: np.random.choice([‘电子’, ‘家电’, ‘数码’], 500),
‘order_date’: pd.date_range(‘2023-01-01′, periods=500, freq=’D’)
})

# 案例1:筛选高价值订单
high_value_orders = ecommerce_data[
(ecommerce_data[‘price’] * ecommerce_data[‘quantity’]) > 2000
]
print(“高价值订单(总价>2000):”)
print(high_value_orders.head())

# 案例2:筛选特定时间段的订单
target_period = ecommerce_data[
(ecommerce_data[‘order_date’] >= ‘2023-02-01’) &
(ecommerce_data[‘order_date’] <= '2023-02-28') ] print("\n2023年2月的订单:") print(target_period.head()) # 案例3:多条件复杂筛选 complex_filter = ecommerce_data[ (ecommerce_data['category'] == '电子') & (ecommerce_data['product'].isin(['手机', '电脑'])) & (ecommerce_data['price'] > 500) &
(ecommerce_data[‘quantity’] >= 2)
]
print(“\n复杂条件筛选(电子类手机/电脑,价格>500,数量>=2):”)
print(complex_filter.head())

# 案例4:使用query()的电商筛选
query_result = ecommerce_data.query(
‘price * quantity > 1500 and category in [“电子”, “数码”]’
)
print(“\nquery()电商筛选:”)
print(query_result.head())
6.2 日志分析案例
# 创建日志数据
log_data = pd.DataFrame({
‘timestamp’: pd.date_range(‘2023-01-01′, periods=100, freq=’H’),
‘level’: np.random.choice([‘INFO’, ‘WARNING’, ‘ERROR’, ‘DEBUG’], 100),
‘message’: np.random.choice([
‘User login successful’,
‘Database connection failed’,
‘File not found’,
‘Cache cleared’,
‘API request timeout’
], 100),
‘response_time’: np.random.randint(10, 1000, 100)
})

# 案例1:筛选错误日志
error_logs = log_data[log_data[‘level’] == ‘ERROR’]
print(“错误日志:”)
print(error_logs.head())

# 案例2:筛选慢响应
slow_responses = log_data[log_data[‘response_time’] > 500]
print(“\n慢响应日志(>500ms):”)
print(slow_responses.head())

# 案例3:文本匹配筛选
db_errors = log_data[log_data[‘message’].str.contains(‘Database’, case=False)]
print(“\n数据库相关错误:”)
print(db_errors.head())

# 案例4:时间范围筛选
hourly_errors = log_data[
(log_data[‘level’] == ‘ERROR’) &
(log_data[‘timestamp’].dt.hour >= 18) # 晚上6点后的错误
]
print(“\n晚上6点后的错误日志:”)
print(hourly_errors.head())
7. 最佳实践总结
7.1 代码可读性最佳实践
# 1. 使用有意义的变量名
# 不好的做法
# result = df[(df[‘a’] > 10) & (df[‘b’] < 20)] # 好的做法 high_value_products = df[(df['price'] > 1000) & (df[‘stock’] < 20)] print("有意义的变量名示例:") print(high_value_products.head()) # 2. 复杂条件使用query() complex_condition = ecommerce_data.query( 'price * quantity > 2000 and category in [“电子”, “数码”] and order_date >= “2023-02-01″‘
)
print(“\n复杂条件使用query():”)
print(complex_condition.head())

# 3. 使用管道操作(pipe)
def filter_high_value(df, threshold=2000):
return df[df[‘price’] * df[‘quantity’] > threshold]

def filter_by_category(df, categories):
return df[df[‘category’].isin(categories)]

# 使用管道
result = (ecommerce_data
.pipe(filter_high_value, threshold=1500)
.pipe(filter_by_category, [‘电子’, ‘数码’]))
print(“\n管道操作示例:”)
print(result.head())
7.2 性能最佳实践
# 1. 优先使用向量化操作
# 不好的做法(使用apply)
# df[‘is_high’] = df[‘price’].apply(lambda x: x > 500)

# 好的做法(向量化)
df[‘is_high’] = df[‘price’] > 500

# 2. 使用query()处理大型数据集
# 对于大型数据集,query()通常比布尔索引更快
large_dataset = pd.DataFrame({
‘id’: range(1000000),
‘value’: np.random.randint(1, 1000, 1000000)
})

# 使用query()而不是布尔索引
result = large_dataset.query(‘value > 500’)
print(f”query()处理100万行数据: {len(result)} 行”)

# 3. 使用适当的数据类型
df_optimized = pd.DataFrame({
‘id’: pd.Series(range(100000), dtype=’int32′),
‘category’: pd.Categorical(np.random.choice([‘A’, ‘B’, ‘C’], 100000)),
‘value’: pd.Series(np.random.randint(1, 1000, 100000), dtype=’int16′)
})

print(“\n优化数据类型后的内存使用:”)
print(df_optimized.info())
7.3 调试和错误处理
# 1. 检查筛选条件
def safe_filter(df, condition, debug=False):
“””安全筛选函数,带调试选项”””
try:
if debug:
print(f”原始数据形状: {df.shape}”)
print(f”条件: {condition}”)

result = df[condition]

if debug:
print(f”结果形状: {result.shape}”)
print(f”筛选比例: {len(result)/len(df):.2%}”)

return result
except Exception as e:
print(f”筛选出错: {e}”)
return pd.DataFrame()

# 使用示例
condition = (ecommerce_data[‘price’] > 500) & (ecommerce_data[‘quantity’] >= 2)
filtered = safe_filter(ecommerce_data, condition, debug=True)

# 2. 验证筛选结果
def validate_filter(df_original, df_filtered, expected_ratio=None):
“””验证筛选结果是否合理”””
if len(df_filtered) == 0:
print(“警告: 筛选结果为空”)
return False

if expected_ratio and abs(len(df_filtered)/len(df_original) – expected_ratio) > 0.1:
print(f”警告: 筛选比例异常,期望约{expected_ratio:.1%},实际{len(df_filtered)/len(df_original):.1%}”)
return False

return True

# 验证示例
is_valid = validate_filter(ecommerce_data, filtered, expected_ratio=0.1)
print(f”\n筛选验证结果: {‘通过’ if is_valid else ‘失败’}”)
8. 总结
本文详细介绍了Pandas数据筛选的各个方面,从基础到高级,从理论到实践。关键要点包括:
基础方法
:布尔索引、query()、loc/iloc是核心工具
高级技巧
:isin()、str.contains()、between()等方法应对复杂场景
性能优化
:避免链式索引、使用向量化操作、合理使用数据类型
常见问题
:SettingWithCopyWarning、内存管理、缺失值处理
最佳实践
:代码可读性、性能优化、错误处理
掌握这些技巧将显著提高数据处理效率,让数据分析工作更加高效和可靠。在实际应用中,应根据具体场景选择最合适的方法,并始终关注代码的可读性和性能。
# 使用Pandas进行高效数据筛选的实用指南与常见问题解析
引言
Pandas是Python中最流行的数据处理库,它提供了强大而灵活的数据结构和数据分析工具。在数据科学和数据分析的工作流程中,数据筛选是最基础也是最重要的操作之一。高效的数据筛选不仅能提高代码执行效率,还能让数据分析过程更加清晰和可靠。本文将深入探讨Pandas数据筛选的各种方法,从基础到高级,并解析常见问题和最佳实践。
1. 基础筛选方法
1.1 布尔索引(Boolean Indexing)
布尔索引是Pandas中最常用的数据筛选方法,它通过创建布尔条件来选择满足特定条件的行。
import pandas as pd
import numpy as np

# 创建示例数据
data = {
‘姓名’: [‘张三’, ‘李四’, ‘王五’, ‘赵六’, ‘钱七’],
‘年龄’: [25, 30, 35, 28, 32],
‘城市’: [‘北京’, ‘上海’, ‘广州’, ‘深圳’, ‘上海’],
‘薪资’: [8000, 12000, 15000, 9000, 13000],
‘部门’: [‘技术’, ‘销售’, ‘技术’, ‘人事’, ‘销售’]
}
df = pd.DataFrame(data)

# 基础布尔索引示例
# 筛选年龄大于30的员工
older_employees = df[df[‘年龄’] > 30]
print(“年龄大于30的员工:”)
print(older_employees)

# 筛选城市为上海的员工
shanghai_employees = df[df[‘城市’] == ‘上海’]
print(“\n上海的员工:”)
print(shanghai_employees)

# 多条件筛选(使用&、|、~)
# 筛选技术部门且薪资大于10000的员工
tech_high_salary = df[(df[‘部门’] == ‘技术’) & (df[‘薪资’] > 10000)]
print(“\n技术部门且薪资大于10000的员工:”)
print(tech_high_salary)

# 筛选城市为北京或上海的员工
beijing_shanghai = df[(df[‘城市’] == ‘北京’) | (df[‘城市’] == ‘上海’)]
print(“\n北京或上海的员工:”)
print(beijing_shanghai)
1.2 使用
query()
方法
query()
方法提供了一种更简洁、更易读的查询语法,特别适合复杂的条件筛选。
# 使用query()方法进行筛选
# 筛选年龄大于30的员工
result1 = df.query(‘年龄 > 30’)
print(“query()筛选年龄>30:”)
print(result1)

# 多条件查询
result2 = df.query(‘部门 == “技术” and 薪资 > 10000’)
print(“\nquery()筛选技术部门且薪资>10000:”)
print(result2)

# 使用变量
min_salary = 10000
result3 = df.query(‘薪资 > @min_salary’)
print(“\nquery()使用变量筛选薪资>10000:”)
print(result3)

# 复杂查询
result4 = df.query(‘城市 in [“北京”, “上海”] and 年龄 < 35') print("\nquery()复杂查询:") print(result4) 1.3 使用 loc[] 和 iloc[] loc[] 基于标签进行索引, iloc[] 基于位置进行索引,它们都可以用于数据筛选。 # 使用loc[]进行筛选 # 筛选年龄大于30的行,选择特定列 result_loc = df.loc[df['年龄'] > 30, [‘姓名’, ‘城市’, ‘薪资’]]
print(“loc[]筛选:”)
print(result_loc)

# 使用iloc[]进行位置筛选
# 筛选前3行,第1-3列(0-based索引)
result_iloc = df.iloc[0:3, 0:3]
print(“\niloc[]筛选:”)
print(result_iloc)

# 结合使用
# 筛选满足条件的行,选择所有列
condition = df[‘城市’] == ‘上海’
result_combined = df.loc[condition, :]
print(“\nloc[]结合条件筛选所有列:”)
print(result_combined)
2. 高级筛选技巧
2.1 使用
isin()
进行集合筛选
当需要筛选的值在一个列表或集合中时,
isin()
方法非常有用。
# 使用isin()筛选
target_cities = [‘北京’, ‘上海’]
city_filter = df[‘城市’].isin(target_cities)
result = df[city_filter]
print(“使用isin()筛选特定城市:”)
print(result)

# 反向筛选(使用~)
not_target_cities = df[~df[‘城市’].isin([‘广州’, ‘深圳’])]
print(“\n使用~和isin()排除特定城市:”)
print(not_target_cities)

# 结合其他条件
result_combined = df[df[‘城市’].isin([‘北京’, ‘上海’]) & (df[‘薪资’] > 10000)]
print(“\nisin()结合其他条件:”)
print(result_combined)
2.2 使用
str.contains()
进行文本匹配
对于字符串列,可以使用
str.contains()
进行模糊匹配。
# 创建包含更多文本数据的示例
text_data = {
‘项目名称’: [‘项目A-前端开发’, ‘项目B-后端开发’, ‘项目C-数据分析’, ‘项目D-前端测试’, ‘项目E-运维’],
‘负责人’: [‘张三’, ‘李四’, ‘王五’, ‘赵六’, ‘钱七’],
‘预算’: [100000, 150000, 80000, 90000, 120000]
}
df_text = pd.DataFrame(text_data)

# 筛选包含”前端”的项目
frontend_projects = df_text[df_text[‘项目名称’].str.contains(‘前端’)]
print(“包含’前端’的项目:”)
print(frontend_projects)

# 不区分大小写筛选
case_insensitive = df_text[df_text[‘项目名称’].str.contains(‘开发’, case=False)]
print(“\n不区分大小写筛选’开发’:”)
print(case_insensitive)

# 使用正则表达式
regex_projects = df_text[df_text[‘项目名称’].str.contains(r’项目[ABC]’)]
print(“\n使用正则表达式筛选项目A、B、C:”)
print(regex_projects)

# 反向筛选(不包含)
not_frontend = df_text[~df_text[‘项目名称’].str.contains(‘前端’)]
print(“\n不包含’前端’的项目:”)
print(not_frontend)
2.3 使用
between()
进行范围筛选
between()
方法用于筛选在某个范围内的值。
# 使用between()筛选年龄在25-30之间的员工
age_range = df[df[‘年龄’].between(25, 30)]
print(“年龄在25-30之间的员工:”)
print(age_range)

# 使用between()筛选薪资在10000-13000之间的员工
salary_range = df[df[‘薪资’].between(10000, 13000, inclusive=’both’)]
print(“\n薪资在10000-13000之间的员工:”)
print(salary_range)

# 使用between()结合其他条件
result = df[df[‘年龄’].between(25, 30) & (df[‘城市’] == ‘上海’)]
print(“\n年龄在25-30之间且城市为上海的员工:”)
print(result)
2.4 使用
filter()
方法
filter()
方法可以根据列名、索引或函数来筛选DataFrame。
# 创建包含多个列的DataFrame
df_multi = pd.DataFrame({
‘A’: [1, 2, 3, 4],
‘B’: [5, 6, 7, 8],
‘C’: [9, 10, 11, 12],
‘D’: [13, 14, 15, 16]
})

# 根据列名筛选(包含特定字符)
result = df_multi.filter(items=[‘A’, ‘C’])
print(“筛选特定列:”)
print(result)

# 使用like参数筛选列名包含’B’的列
result_like = df_multi.filter(like=’B’, axis=1)
print(“\n筛选列名包含’B’的列:”)
print(result_like)

# 使用正则表达式筛选列名
result_regex = df_multi.filter(regex=r'[A-C]’, axis=1)
print(“\n使用正则表达式筛选列名:”)
print(result_regex)
2.5 使用
where()
和
mask()
where()
和
mask()
方法用于条件替换,但也可以用于筛选。
# 使用where()进行条件筛选
# 保留满足条件的值,不满足的设为NaN
result_where = df.where(df[‘年龄’] > 30)
print(“where()筛选(保留>30,其他为NaN):”)
print(result_where)

# 使用mask()进行条件筛选(与where相反)
result_mask = df.mask(df[‘年龄’] <= 30) print("\nmask()筛选(保留>30,其他为NaN):”)
print(result_mask)

# 结合dropna()使用
result_clean = df.where(df[‘年龄’] > 30).dropna()
print(“\nwhere()结合dropna()筛选:”)
print(result_clean)
3. 性能优化技巧
3.1 避免链式索引(Chained Indexing)
链式索引会导致
SettingWithCopyWarning
,并且可能产生不可预测的结果。
# 错误的做法(链式索引)
# df[df[‘年龄’] > 30][‘薪资’] = 15000 # 这会引发警告

# 正确的做法1:使用loc
df.loc[df[‘年龄’] > 30, ‘薪资’] = 15000
print(“使用loc修改数据:”)
print(df)

# 正确的做法2:使用where()或mask()
df_original = pd.DataFrame(data) # 重新创建原始数据
df_original[‘薪资’] = df_original[‘薪资’].where(df_original[‘年龄’] <= 30, 15000) print("\n使用where()修改数据:") print(df_original) 3.2 使用向量化操作 向量化操作比循环更快,应尽量使用。 # 创建大型数据集测试性能 np.random.seed(42) large_df = pd.DataFrame({ 'id': range(100000), 'value': np.random.randint(1, 1000, 100000), 'category': np.random.choice(['A', 'B', 'C', 'D'], 100000) }) # 向量化筛选(快) vectorized_result = large_df[large_df['value'] > 500]
print(f”向量化筛选结果: {len(vectorized_result)} 行”)

# 使用apply()(相对较慢)
# apply_result = large_df[large_df[‘value’].apply(lambda x: x > 500)]
# print(f”apply()筛选结果: {len(apply_result)} 行”)

# 使用query()(通常很快)
query_result = large_df.query(‘value > 500’)
print(f”query()筛选结果: {100000 – len(query_result)} 行”)
3.3 使用
eval()
进行高效计算
eval()
可以用于高效的表达式计算,特别适合大型数据集。
# 使用eval()进行复杂筛选
# 创建示例数据
df_eval = pd.DataFrame({
‘a’: np.random.randint(1, 100, 1000),
‘b’: np.random.randint(1, 100, 1000),
‘c’: np.random.randint(1, 100, 1000)
})

# 使用eval()进行条件筛选
result_eval = df_eval.query(‘a + b > 100 and c < 50') print(f"使用eval()筛选结果: {len(result_eval)} 行") # 使用eval()创建新列 df_eval.eval('d = a * b + c', inplace=True) print("\n使用eval()创建新列d:") print(df_eval.head()) 3.4 使用 numba 加速(高级) 对于特别复杂的筛选逻辑,可以使用 numba 库来加速。 # 注意:需要安装numba库 # pip install numba try: from numba import jit @jit(nopython=True) def custom_filter(values): result = np.zeros(len(values), dtype=np.bool_) for i in range(len(values)): # 自定义筛选逻辑 if values[i] > 500 and values[i] < 800: result[i] = True return result # 应用自定义筛选函数 mask = custom_filter(large_df['value'].values) numba_result = large_df[mask] print(f"使用numba加速筛选结果: {len(numba_result)} 行") except ImportError: print("numba库未安装,跳过此示例") 4. 多索引(MultiIndex)筛选 4.1 创建多索引DataFrame # 创建多索引DataFrame arrays = [ ['销售', '销售', '技术', '技术', '人事'], ['张三', '李四', '王五', '赵六', '钱七'] ] index = pd.MultiIndex.from_arrays(arrays, names=['部门', '姓名']) multi_df = pd.DataFrame({ '年龄': [28, 32, 25, 30, 35], '薪资': [12000, 15000, 8000, 10000, 9000] }, index=index) print("多索引DataFrame:") print(multi_df) 4.2 多索引筛选方法 # 使用loc进行多索引筛选 # 筛选技术部门 tech_dept = multi_df.loc['技术'] print("技术部门:") print(tech_dept) # 筛选技术部门的王五 wang_wu = multi_df.loc[('技术', '王五')] print("\n技术部门王五:") print(wang_wu) # 使用slice进行范围筛选 # 筛选销售部门的所有员工 sales_dept = multi_df.loc['销售', :] print("\n销售部门所有员工:") print(sales_dept) # 使用query()进行多索引筛选 result = multi_df.query('部门 == "技术" and 薪资 > 8000′)
print(“\nquery()多索引筛选:”)
print(result)
5. 常见问题解析
5.1 SettingWithCopyWarning问题
这是Pandas中最常见的警告之一,通常发生在链式索引时。
# 问题示例
df_problem = pd.DataFrame(data)

# 可能引发警告的代码
# df_problem[df_problem[‘年龄’] > 30][‘薪资’] = 15000 # 警告!

# 解决方案1:使用loc
df_problem.loc[df_problem[‘年龄’] > 30, ‘薪资’] = 15000
print(“解决方案1 – 使用loc:”)
print(df_problem)

# 解决方案2:使用copy()
df_copy = df[df[‘年龄’] > 30].copy()
df_copy[‘薪资’] = 15000
print(“\n解决方案2 – 使用copy():”)
print(df_copy)

# 解决方案3:使用where()
df_original = pd.DataFrame(data)
df_original[‘薪资’] = df_original[‘薪资’].where(df_original[‘年龄’] <= 30, 15000) print("\n解决方案3 - 使用where():") print(df_original) 5.2 内存使用问题 处理大型数据集时,内存管理很重要。 # 优化内存使用的技巧 # 1. 使用适当的数据类型 df_memory = pd.DataFrame({ 'id': pd.Series(range(100000), dtype='int32'), # int32比int64节省内存 'value': pd.Series(np.random.randint(1, 1000, 100000), dtype='int16'), # int16更节省 'category': pd.Categorical(np.random.choice(['A', 'B', 'C'], 100000)) # 分类类型节省内存 }) print("优化数据类型后的内存使用:") print(df_memory.info()) # 2. 分块处理大型数据集 def process_large_file(file_path, chunk_size=10000): results = [] for chunk in pd.read_csv(file_path, chunksize=chunk_size): # 处理每个chunk filtered_chunk = chunk[chunk['value'] > 500]
results.append(filtered_chunk)
return pd.concat(results, ignore_index=True)

# 3. 使用query()减少内存占用
# query()通常比布尔索引更节省内存
large_df = pd.DataFrame({
‘a’: np.random.randint(1, 1000, 100000),
‘b’: np.random.randint(1, 1000, 100000),
‘c’: np.random.randint(1, 1000, 100000)
})

# 内存更高效的筛选
result_efficient = large_df.query(‘a > 500 and b < 800') print(f"\n高效筛选结果: {len(result_efficient)} 行") 5.3 处理缺失值(NaN)的筛选 # 创建包含缺失值的DataFrame df_nan = pd.DataFrame({ 'A': [1, 2, np.nan, 4, 5], 'B': [np.nan, 2, 3, np.nan, 5], 'C': [1, 2, 3, 4, 5] }) print("包含NaN的DataFrame:") print(df_nan) # 筛选包含NaN的行 rows_with_nan = df_nan[df_nan.isnull().any(axis=1)] print("\n包含NaN的行:") print(rows_with_nan) # 筛选不包含NaN的行 rows_without_nan = df_nan.dropna() print("\n不包含NaN的行:") print(rows_without_nan) # 筛选特定列不包含NaN的行 rows_A_not_nan = df_nan[df_nan['A'].notna()] print("\nA列不包含NaN的行:") print(rows_A_not_nan) # 使用fillna()填充后筛选 df_filled = df_nan.fillna(0) result = df_filled[df_filled['A'] > 2]
print(“\nfillna()后筛选:”)
print(result)
5.4 性能瓶颈诊断
import time

# 测试不同方法的性能
def benchmark_filtering_methods(df, condition):
methods = {
‘布尔索引’: lambda: df[df[‘value’] > condition],
‘query()’: lambda: df.query(f’value > {condition}’),
‘loc’: lambda: df.loc[df[‘value’] > condition],
‘eval()’: lambda: df.eval(f’value > {condition}’),
‘apply()’: lambda: df[df[‘value’].apply(lambda x: x > condition)]
}

results = {}
for name, method in methods.items():
start_time = time.time()
result = method()
end_time = time.time()
results[name] = end_time – start_time
print(f”{name}: {end_time – start_time:.6f} 秒,结果行数: {len(result)}”)

return results

# 使用之前创建的large_df进行测试
print(“\n性能测试(10万行数据):”)
benchmark_filtering_methods(large_df, 500)
5.5 筛选结果的索引处理
# 筛选后索引的处理问题
df_index = pd.DataFrame({
‘name’: [‘Alice’, ‘Bob’, ‘Charlie’, ‘David’],
‘score’: [85, 92, 78, 88]
}, index=[‘a’, ‘b’, ‘c’, ‘d’])

# 筛选后保留原始索引
filtered = df_index[df_index[‘score’] > 80]
print(“筛选结果(保留原始索引):”)
print(filtered)

# 重置索引
filtered_reset = filtered.reset_index(drop=True)
print(“\n重置索引后:”)
print(filtered_reset)

# 筛选后保留特定索引
filtered_with_index = df_index.loc[df_index[‘score’] > 80, :]
print(“\n使用loc保留索引:”)
print(filtered_with_index)
6. 实际应用案例
6.1 电商数据分析案例
# 创建电商销售数据
np.random.seed(42)
ecommerce_data = pd.DataFrame({
‘order_id’: range(1000, 1500),
‘customer_id’: np.random.randint(10000, 10050, 500),
‘product’: np.random.choice([‘手机’, ‘电脑’, ‘平板’, ‘耳机’], 500),
‘price’: np.random.randint(100, 1000, 500),
‘quantity’: np.random.randint(1, 5, 500),
‘category’: np.random.choice([‘电子’, ‘家电’, ‘数码’], 500),
‘order_date’: pd.date_range(‘2023-01-01′, periods=500, freq=’D’)
})

# 案例1:筛选高价值订单
high_value_orders = ecommerce_data[
(ecommerce_data[‘price’] * ecommerce_data[‘quantity’]) > 2000
]
print(“高价值订单(总价>2000):”)
print(high_value_orders.head())

# 案例2:筛选特定时间段的订单
target_period = ecommerce_data[
(ecommerce_data[‘order_date’] >= ‘2023-02-01’) &
(ecommerce_data[‘order_date’] <= '2023-02-28') ] print("\n2023年2月的订单:") print(target_period.head()) # 案例3:多条件复杂筛选 complex_filter = ecommerce_data[ (ecommerce_data['category'] == '电子') & (ecommerce_data['product'].isin(['手机', '电脑'])) & (ecommerce_data['price'] > 500) &
(ecommerce_data[‘quantity’] >= 2)
]
print(“\n复杂条件筛选(电子类手机/电脑,价格>500,数量>=2):”)
print(complex_filter.head())

# 案例4:使用query()的电商筛选
query_result = ecommerce_data.query(
‘price * quantity > 1500 and category in [“电子”, “数码”]’
)
print(“\nquery()电商筛选:”)
print(query_result.head())
6.2 日志分析案例
# 创建日志数据
log_data = pd.DataFrame({
‘timestamp’: pd.date_range(‘2023-01-01′, periods=100, freq=’H’),
‘level’: np.random.choice([‘INFO’, ‘WARNING’, ‘ERROR’, ‘DEBUG’], 100),
‘message’: np.random.choice([
‘User login successful’,
‘Database connection failed’,
‘File not found’,
‘Cache cleared’,
‘API request timeout’
], 100),
‘response_time’: np.random.randint(10, 1000, 100)
})

# 案例1:筛选错误日志
error_logs = log_data[log_data[‘level’] == ‘ERROR’]
print(“错误日志:”)
print(error_logs.head())

# 案例2:筛选慢响应
slow_responses = log_data[log_data[‘response_time’] > 500]
print(“\n慢响应日志(>500ms):”)
print(slow_responses.head())

# 案例3:文本匹配筛选
db_errors = log_data[log_data[‘message’].str.contains(‘Database’, case=False)]
print(“\n数据库相关错误:”)
print(db_errors.head())

# 案例4:时间范围筛选
hourly_errors = log_data[
(log_data[‘level’] == ‘ERROR’) &
(log_data[‘timestamp’].dt.hour >= 18) # 晚上6点后的错误
]
print(“\n晚上6点后的错误日志:”)
print(hourly_errors.head())
7. 最佳实践总结
7.1 代码可读性最佳实践
# 1. 使用有意义的变量名
# 不好的做法
# result = df[(df[‘a’] > 10) & (df[‘b’] < 20)] # 好的做法 high_value_products = df[(df['price'] > 1000) & (df[‘stock’] < 20)] print("有意义的变量名示例:") print(high_value_products.head()) # 2. 复杂条件使用query() complex_condition = ecommerce_data.query( 'price * quantity > 2000 and category in [“电子”, “数码”] and order_date >= “2023-02-01″‘
)
print(“\n复杂条件使用query():”)
print(complex_condition.head())

# 3. 使用管道操作(pipe)
def filter_high_value(df, threshold=2000):
return df[df[‘price’] * df[‘quantity’] > threshold]

def filter_by_category(df, categories):
return df[df[‘category’].isin(categories)]

# 使用管道
result = (ecommerce_data
.pipe(filter_high_value, threshold=1500)
.pipe(filter_by_category, [‘电子’, ‘数码’]))
print(“\n管道操作示例:”)
print(result.head())
7.2 性能最佳实践
# 1. 优先使用向量化操作
# 不好的做法(使用apply)
# df[‘is_high’] = df[‘price’].apply(lambda x: x > 500)

# 好的做法(向量化)
df[‘is_high’] = df[‘price’] > 500

# 2. 使用query()处理大型数据集
# 对于大型数据集,query()通常比布尔索引更快
large_dataset = pd.DataFrame({
‘id’: range(1000000),
‘value’: np.random.randint(1, 1000, 1000000)
})

# 使用query()而不是布尔索引
result = large_dataset.query(‘value > 500’)
print(f”query()处理100万行数据: {len(result)} 行”)

# 3. 使用适当的数据类型
df_optimized = pd.DataFrame({
‘id’: pd.Series(range(100000), dtype=’int32′),
‘category’: pd.Categorical(np.random.choice([‘A’, ‘B’, ‘C’], 100000)),
‘value’: pd.Series(np.random.randint(1, 1000, 100000), dtype=’int16′)
})

print(“\n优化数据类型后的内存使用:”)
print(df_optimized.info())
7.3 调试和错误处理
# 1. 检查筛选条件
def safe_filter(df, condition, debug=False):
“””安全筛选函数,带调试选项”””
try:
if debug:
print(f”原始数据形状: {df.shape}”)
print(f”条件: {condition}”)

result = df[condition]

if debug:
print(f”结果形状: {result.shape}”)
print(f”筛选比例: {len(result)/len(df):.2%}”)

return result
except Exception as e:
print(f”筛选出错: {e}”)
return pd.DataFrame()

# 使用示例
condition = (ecommerce_data[‘price’] > 500) & (ecommerce_data[‘quantity’] >= 2)
filtered = safe_filter(ecommerce_data, condition, debug=True)

# 2. 验证筛选结果
def validate_filter(df_original, df_filtered, expected_ratio=None):
“””验证筛选结果是否合理”””
if len(df_filtered) == 0:
print(“警告: 筛选结果为空”)
return False

if expected_ratio and abs(len(df_filtered)/len(df_original) – expected_ratio) > 0.1:
print(f”警告: 筛选比例异常,期望约{expected_ratio:.1%},实际{len(df_filtered)/len(df_original):.1%}”)
return False

return True

# 验证示例
is_valid = validate_filter(ecommerce_data, filtered, expected_ratio=0.1)
print(f”\n筛选验证结果: {‘通过’ if is_valid else ‘失败’}”)
8. 总结
本文详细介绍了Pandas数据筛选的各个方面,从基础到高级,从理论到实践。关键要点包括:
基础方法
:布尔索引、query()、loc/iloc是核心工具
高级技巧
:isin()、str.contains()、between()等方法应对复杂场景
性能优化
:避免链式索引、使用向量化操作、合理使用数据类型
常见问题
:SettingWithCopyWarning、内存管理、缺失值处理
最佳实践
:代码可读性、性能优化、错误处理
掌握这些技巧将显著提高数据处理效率,让数据分析工作更加高效和可靠。在实际应用中,应根据具体场景选择最合适的方法,并始终关注代码的可读性和性能。