本文目录导读:

- 目录导读
- 什么是无效数据?常见的脏数据类型
- 准备环境与数据:用pandas创建模拟数据集
- 方法一:基于条件筛选(过滤空值、异常值)
- 方法二:正则表达式匹配(清洗文本脏数据)
- 方法三:IQR与Z-score法(剔除统计异常值)
- 问答环节:新手最常遇到的5个过滤陷阱
- 总结与最佳实践建议
Python数据筛选实战:3大高效方法精准过滤无效数据(附完整代码)
目录导读
- 什么是无效数据?常见的脏数据类型
- 准备环境与数据:用pandas创建模拟数据集
- 方法一:基于条件筛选(过滤空值、异常值)
- 方法二:正则表达式匹配(清洗文本脏数据)
- 方法三:IQR与Z-score法(剔除统计异常值)
- 问答环节:新手最常遇到的5个过滤陷阱
- 总结与最佳实践建议
什么是无效数据?常见的脏数据类型
在Python数据分析中,无效数据通常指以下三类:
- 缺失数据:如
None、NaN、空字符串。 - 异常数据:明显超出业务范围的数值,例如年龄为200岁。
- 格式错误数据:如混入字母的数字列、多余的符号、重复记录。
据IBM统计,数据清洗通常占数据分析工作量的60%以上,没有经过筛选的“脏数据”会导致统计偏差、模型失效,甚至业务误判。
准备环境与数据:用pandas创建模拟数据集
我们使用pandas和numpy创建一个包含各种“无效”字段的DataFrame:
import pandas as pd
import numpy as np
# 模拟100条订单数据,故意混入无效值
np.random.seed(42)
df = pd.DataFrame({
'order_id': range(1001, 1101),
'customer_name': ['张三', '李四', '', '王五', None, '赵六', '张三'] * 14 + ['李四'],
'age': np.random.randint(18, 65, 100).astype(float),
'amount': np.random.uniform(50, 500, 100).round(2),
'phone': ['138-1234-5678', 'abc', '159-1111-2233', '', None] * 20,
'status': ['成功', '失败', '成功', '失败', '超时', '重复提交', '成功'] * 14 + ['失败']
})
# 手动注入问题数据
df.loc[10, 'age'] = 200 # 异常年龄
df.loc[20, 'age'] = -5 # 负年龄
df.loc[30, 'amount'] = np.nan # 缺失金额
df.loc[40, 'phone'] = 'not a number'
print("原始数据形状:", df.shape)
输出: 存在NaN、空字符串、异常数值、格式错误等脏数据。
方法一:基于条件筛选(过滤空值、异常值)
1 过滤空值
pandas提供dropna()直接删除包含NaN的行,但要注意:
# 删除status列中任意空值所在行 df_clean = df.dropna(subset=['status', 'customer_name'])
对于空字符串,不能直接用dropna,需结合replace:
df_clean = df.replace('', np.nan).dropna(subset=['customer_name'])
2 过滤数值范围无效值
# 年龄应在0-120之间,金额大于0 valid_age = (df['age'] >= 0) & (df['age'] <= 120) valid_amount = df['amount'] > 0 df_filtered = df[valid_age & valid_amount]
3 组合过滤——直接得到干净子集
# 一行代码完成多重过滤
df_clean = df[
(df['age'].notna()) &
(df['amount'].notna()) &
(df['age'] > 0) &
(df['age'] < 120) &
(df['amount'] > 0)
].copy()
print(f"清洗后行数: {len(df_clean)}")
注意:务必使用.copy(),避免后续操作修改原数据。
方法二:正则表达式匹配(清洗文本脏数据)
电话号码是最常见的脏数据来源,我们用str.match()结合正则精准过滤:
# 定义电话格式:3-8位数字格式,支持连字符
phone_pattern = r'^1[3-9]\d{1}-\d{4}-\d{4}$' # 简化版示例
# 更通用的:允许数字、连字符、至少10位
pattern_general = r'^[\d\-]{11,13}$'
# 筛选有效的电话(排除非字符串、空值、格式错误)
valid_phone = df['phone'].astype(str).str.match(pattern_general)
df_clean = df[valid_phone & df['phone'].notna()]
注意:
astype(str)可以防止NaN或数字类型报错。
实用技巧:批量清理文本列
如果status列中有多个状态变种,可以用str.contains:
# 只保留'成功'或'失败'(不含'超时'等) valid_status = df['status'].str.contains(r'^(成功|失败)$', na=False) df_clean = df[valid_status]
na=False参数让NaN自动被排除,非常实用。
方法三:IQR与Z-score法(剔除统计异常值)
对于连续数值列(如金额、年龄),业务规则有时不够,需要用统计方法。
1 基于IQR(四分位距)剔除离群点
Q1 = df['amount'].quantile(0.25)
Q3 = df['amount'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
df_no_outlier = df[(df['amount'] >= lower_bound) & (df['amount'] <= upper_bound)]
print(f"IQR方法过滤前{len(df)}行,过滤后{len(df_no_outlier)}行")
2 基于Z-score(标准分数)
from scipy import stats z_scores = np.abs(stats.zscore(df['amount'].dropna())) threshold = 3 # Z-score >3 视为异常 df_no_outlier_z = df[z_scores < threshold]
适用场景:数据近似正态分布时使用Z-score;偏态分布用IQR更稳健。
问答环节:新手最常遇到的5个过滤陷阱
Q1:过滤后行数减少,但原始DataFrame为什么没变?
答:过滤操作返回的是视图,需赋值保存,如:
df = df[df['age'] > 0] # ✅ 正确赋值 # 或使用 df.drop(df[条件].index, inplace=True)
Q2:为什么dropna()没删掉空字符串?
答:NaN与空字符串不同,必须先替换:
df.replace('', np.nan, inplace=True)
Q3:过滤重复数据用哪个函数?
答:df.drop_duplicates(subset=['order_id']),注意keep='first'或'last'。
Q4:正则匹配电话时,为什么空行也通过了?
答:空字符串转为str后仍是,可能匹配到某些宽松模式,建议先过滤NaN和空串:
mask = df['phone'].notna() & (df['phone'] != '') & df['phone'].str.match(r'...')
Q5:过滤后索引混乱了怎么办?
答:添加reset_index(drop=True):
df_clean = df_clean.reset_index(drop=True)
总结与最佳实践建议
过滤核心原则
- 先备份,后清洗:用
.copy()避免污染原始数据。 - 按类型处理:数值用范围+统计方法,文本用正则,缺失值用
dropna/fillna。 - 保持可追溯:每个过滤步骤记录过滤行数,用
shape对比。
推荐过滤流水线代码结构(可直接套用)
def clean_dataframe(df):
# 1. 缺失值处理
df = df.replace('', np.nan).dropna(subset=['关键列'])
# 2. 数值范围过滤
df = df[(df['age'] > 0) & (df['age'] < 120)]
# 3. 文本格式过滤
df = df[df['phone'].astype(str).str.match(r'^[\d\-]{11,13}$')]
# 4. 统计异常过滤
Q1, Q3 = df['amount'].quantile([0.25, 0.75])
iqr = Q3 - Q1
df = df[(df['amount'] > Q1-1.5*iqr) & (df['amount'] < Q3+1.5*iqr)]
return df.reset_index(drop=True)
最后提醒:在真正的业务场景中,无效数据的定义需要与业务方反复确认——有时年龄200可能是测试数据,有时“失败”状态可能需要保留,Python提供工具,但判断力来自对业务的理解,建议每次过滤后输出一份“被删除的行”以供核验。
(文章结束,未包含字数统计)