Python数据筛选案例如何过滤无效数据

wen python案例 28

本文目录导读:

Python数据筛选案例如何过滤无效数据

  1. 目录导读
  2. 什么是无效数据?常见的脏数据类型
  3. 准备环境与数据:用pandas创建模拟数据集
  4. 方法一:基于条件筛选(过滤空值、异常值)
  5. 方法二:正则表达式匹配(清洗文本脏数据)
  6. 方法三:IQR与Z-score法(剔除统计异常值)
  7. 问答环节:新手最常遇到的5个过滤陷阱
  8. 总结与最佳实践建议

Python数据筛选实战:3大高效方法精准过滤无效数据(附完整代码)

目录导读

  • 什么是无效数据?常见的脏数据类型
  • 准备环境与数据:用pandas创建模拟数据集
  • 方法一:基于条件筛选(过滤空值、异常值)
  • 方法二:正则表达式匹配(清洗文本脏数据)
  • 方法三:IQR与Z-score法(剔除统计异常值)
  • 问答环节:新手最常遇到的5个过滤陷阱
  • 总结与最佳实践建议

什么是无效数据?常见的脏数据类型

在Python数据分析中,无效数据通常指以下三类:

  • 缺失数据:如NoneNaN、空字符串。
  • 异常数据:明显超出业务范围的数值,例如年龄为200岁。
  • 格式错误数据:如混入字母的数字列、多余的符号、重复记录。

据IBM统计,数据清洗通常占数据分析工作量的60%以上,没有经过筛选的“脏数据”会导致统计偏差、模型失效,甚至业务误判。

准备环境与数据:用pandas创建模拟数据集

我们使用pandasnumpy创建一个包含各种“无效”字段的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)

总结与最佳实践建议

过滤核心原则

  1. 先备份,后清洗:用.copy()避免污染原始数据。
  2. 按类型处理:数值用范围+统计方法,文本用正则,缺失值用dropna/fillna
  3. 保持可追溯:每个过滤步骤记录过滤行数,用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提供工具,但判断力来自对业务的理解,建议每次过滤后输出一份“被删除的行”以供核验。

(文章结束,未包含字数统计)

抱歉,评论功能暂时关闭!