Python数据科学:利用Pandas与NumPy进行高效数据清洗
数据清洗是数据科学工作流中至关重要的一环,通常占据整个项目70%以上的时间。低质量的数据会导致分析结果偏差、模型性能下降。Python生态中的Pandas与NumPy库,凭借其强大的数据处理能力和向量化操作,成为数据清洗的首选工具。本文将系统介绍如何利用这两个库进行高效数据清洗,并融入现代数据工具提升工作流效率。
1. 数据加载与初步探索
在开始清洗之前,我们需要先了解数据的基本情况。Pandas提供了多种数据读取函数,支持CSV、Excel、JSON、SQL数据库等多种数据源。
import pandas as pd
import numpy as np
# 从CSV文件加载数据
df = pd.read_csv('sales_data.csv')
# 查看数据基本信息
print(f"数据形状: {df.shape}")
print(f"\n列名: {df.columns.tolist()}")
print(f"\n数据类型:\n{df.dtypes}")
# 显示前5行数据
print("\n数据预览:")
print(df.head())
对于来自数据库的数据,传统方式需要编写SQL查询并导出为文件。现在,使用dblens SQL编辑器可以直接在浏览器中编写、执行SQL查询,并将结果一键导出为Pandas可读的格式,大大简化了数据获取流程。
2. 处理缺失值
缺失值是数据清洗中最常见的问题。Pandas提供了多种方法来识别和处理缺失值。
# 检查缺失值
missing_values = df.isnull().sum()
print("缺失值统计:")
print(missing_values[missing_values > 0])
# 处理缺失值的几种方法
# 1. 删除包含缺失值的行
df_dropped = df.dropna()
# 2. 用特定值填充
df_filled = df.fillna({
'price': 0,
'category': 'Unknown',
'quantity': df['quantity'].median() # 使用中位数填充
})
# 3. 使用前向或后向填充(时间序列数据常用)
df_ffill = df.fillna(method='ffill') # 前向填充
# 4. 使用插值法
df_interpolated = df.interpolate(method='linear')
NumPy的nan常量可以用于标记缺失值,配合Pandas进行高效处理。
3. 处理重复数据
重复数据会扭曲分析结果,需要及时识别和清理。
# 检查重复行
duplicate_count = df.duplicated().sum()
print(f"发现 {duplicate_count} 条重复记录")
# 删除完全重复的行
df_unique = df.drop_duplicates()
# 基于特定列删除重复(保留第一条)
df_unique_subset = df.drop_duplicates(subset=['customer_id', 'order_date'])
# 基于特定列删除重复(保留最后一条)
df_unique_last = df.drop_duplicates(subset=['customer_id', 'order_date'], keep='last')
4. 数据类型转换与标准化
正确的数据类型是进行计算和分析的基础。
# 查看当前数据类型
print("转换前数据类型:")
print(df.dtypes)
# 转换数据类型
df['order_date'] = pd.to_datetime(df['order_date'], errors='coerce')
df['price'] = pd.to_numeric(df['price'], errors='coerce')
df['category'] = df['category'].astype('category')
# 标准化文本数据(统一大小写、去除空格)
df['product_name'] = df['product_name'].str.strip().str.title()
df['customer_email'] = df['customer_email'].str.lower()
# 使用NumPy进行数值标准化
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
numeric_cols = ['price', 'quantity', 'discount']
df[numeric_cols] = scaler.fit_transform(df[numeric_cols])
5. 异常值检测与处理
异常值可能代表数据错误或有价值的极端情况,需要谨慎处理。
# 使用描述性统计识别异常值
print("数值列描述性统计:")
print(df[['price', 'quantity']].describe())
# 基于标准差识别异常值(Z-score方法)
from scipy import stats
z_scores = np.abs(stats.zscore(df[['price', 'quantity']].dropna()))
outliers = (z_scores > 3).any(axis=1)
print(f"发现 {outliers.sum()} 个异常值")
# 基于IQR(四分位距)识别异常值
Q1 = df['price'].quantile(0.25)
Q3 = df['price'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# 处理异常值的几种方法
# 1. 删除异常值
df_no_outliers = df[(df['price'] >= lower_bound) & (df['price'] <= upper_bound)]
# 2. 缩尾处理(Winsorization)
def winsorize_series(series, limits=[0.05, 0.05]):
return stats.mstats.winsorize(series, limits=limits)
df['price_winsorized'] = winsorize_series(df['price'].dropna())
# 3. 用中位数或分位数替换
df['price_capped'] = np.where(df['price'] > upper_bound, upper_bound,
np.where(df['price'] < lower_bound, lower_bound, df['price']))
6. 数据转换与特征工程
数据清洗后,通常需要进行转换以适合分析或建模。
# 创建新特征
df['total_amount'] = df['price'] * df['quantity']
df['discount_rate'] = df['discount'] / df['price']
# 日期特征提取
df['order_year'] = df['order_date'].dt.year
df['order_month'] = df['order_date'].dt.month
df['order_day'] = df['order_date'].dt.day
df['order_weekday'] = df['order_date'].dt.weekday
# 分箱(Binning)连续变量
df['price_bin'] = pd.cut(df['price'], bins=5, labels=['Very Low', 'Low', 'Medium', 'High', 'Very High'])
# 独热编码分类变量
df_encoded = pd.get_dummies(df, columns=['category', 'region'], prefix=['cat', 'reg'])
7. 高效数据清洗工作流
将上述步骤组合成可复用的清洗管道:
def data_cleaning_pipeline(df):
"""数据清洗管道"""
# 创建副本以避免修改原始数据
df_clean = df.copy()
# 1. 处理缺失值
df_clean = df_clean.fillna({
'price': df_clean['price'].median(),
'quantity': 1,
'category': 'Unknown'
})
# 2. 删除重复记录
df_clean = df_clean.drop_duplicates(subset=['order_id'])
# 3. 数据类型转换
df_clean['order_date'] = pd.to_datetime(df_clean['order_date'], errors='coerce')
df_clean['price'] = pd.to_numeric(df_clean['price'], errors='coerce')
# 4. 处理异常值(价格在合理范围内)
price_q1 = df_clean['price'].quantile(0.01)
price_q99 = df_clean['price'].quantile(0.99)
df_clean['price'] = df_clean['price'].clip(price_q1, price_q99)
# 5. 标准化文本
df_clean['customer_name'] = df_clean['customer_name'].str.strip().str.title()
return df_clean
# 应用清洗管道
cleaned_df = data_cleaning_pipeline(df)
print(f"清洗后数据形状: {cleaned_df.shape}")
在实际工作中,数据清洗过程往往需要多次迭代和验证。使用QueryNote可以记录每次清洗步骤的逻辑和结果,形成可追溯的数据清洗文档,这对于团队协作和数据治理至关重要。
8. 性能优化技巧
处理大规模数据时,性能成为关键考虑因素。
# 1. 使用适当的数据类型节省内存
def reduce_memory_usage(df):
"""减少DataFrame内存使用"""
start_mem = df.memory_usage().sum() / 1024**2
for col in df.columns:
col_type = df[col].dtype
if col_type != object:
c_min = df[col].min()
c_max = df[col].max()
if str(col_type)[:3] == 'int':
if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:
df[col] = df[col].astype(np.int8)
elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:
df[col] = df[col].astype(np.int16)
# ... 类似处理其他整数类型
else:
# 处理浮点数类型
pass
end_mem = df.memory_usage().sum() / 1024**2
print(f"内存使用从 {start_mem:.2f} MB 减少到 {end_mem:.2f} MB")
return df
# 2. 使用向量化操作替代循环
def calculate_discount_vectorized(df):
"""向量化计算折扣"""
# 高效:向量化操作
df['final_price'] = df['price'] * (1 - df['discount_rate'])
return df
# 3. 使用分块处理大数据集
chunk_size = 10000
chunks = []
for chunk in pd.read_csv('large_dataset.csv', chunksize=chunk_size):
# 对每个块应用清洗逻辑
cleaned_chunk = data_cleaning_pipeline(chunk)
chunks.append(cleaned_chunk)
# 合并所有块
final_df = pd.concat(chunks, ignore_index=True)
总结
高效的数据清洗是数据科学项目成功的基石。Pandas与NumPy提供了强大而灵活的工具集,能够处理从简单到复杂的数据清洗任务。关键要点包括:
- 系统化方法:按照加载→探索→清洗→验证的流程进行,确保每一步都有明确目的
- 合适工具选择:根据数据特点选择最合适的处理方法(如缺失值填充方法)
- 性能意识:处理大数据时注意内存使用和计算效率
- 可重复性:将清洗步骤封装为函数或管道,便于复用和分享
- 文档化:记录清洗决策和逻辑,便于后续追溯和团队协作
现代数据工具如dblens SQL编辑器和QueryNote进一步提升了数据清洗工作流的效率。dblens SQL编辑器简化了数据获取过程,而QueryNote则确保了清洗过程的可追溯性和团队协作效率。结合这些工具与Python的强大数据处理能力,数据科学家可以更专注于数据洞察而非数据整理,真正实现高效数据科学工作流。
记住,没有"一刀切"的数据清洗方案。最佳实践是根据具体业务场景、数据质量和分析目标,灵活选择和组合不同的清洗技术。持续学习和实践是掌握高效数据清洗的关键。
本文来自博客园,作者:DBLens数据库开发工具,转载请注明原文链接:https://www.cnblogs.com/dblens/p/19561453
浙公网安备 33010602011771号