Pandas 1.5.3 实战:Excel 数据清洗与转换的 5 个高阶场景
1. 缺失值处理的智能策略
数据清洗中最常见的挑战莫过于缺失值处理。Pandas 1.5.3 提供了比以往更强大的工具链来处理这个难题。让我们从一个真实的销售数据集开始:
import pandas as pd sales_data = pd.read_excel('sales_records.xlsx', engine='openpyxl') print(sales_data.isna().sum())缺失值诊断矩阵可以帮助我们快速定位问题:
| 列名 | 缺失数量 | 缺失比例 | 数据类型 |
|---|---|---|---|
| customer_id | 12 | 2.3% | int64 |
| product_name | 0 | 0% | object |
| unit_price | 45 | 8.7% | float64 |
| quantity | 7 | 1.4% | int64 |
对于不同场景,我们有多种处理方案:
- 时间序列填充:使用前后时间点的平均值
sales_data['unit_price'] = sales_data['unit_price'].interpolate(method='time')- 分组填充:按产品类别填充中位数
sales_data['unit_price'] = sales_data.groupby('product_category')['unit_price']\ .transform(lambda x: x.fillna(x.median()))- 标记异常值:创建新列标识填充记录
sales_data['price_imputed'] = sales_data['unit_price'].isna().astype(int) sales_data['unit_price'] = sales_data['unit_price'].fillna(sales_data['unit_price'].median())提示:在金融领域,建议保留原始缺失状态并创建新列存储填充值,这对后续模型训练至关重要。
2. 类型转换与内存优化
Pandas 的数据类型系统远比表面看起来复杂。观察这个内存使用案例:
# 转换前内存使用 original_mem = sales_data.memory_usage(deep=True).sum() # 优化类型 sales_data['customer_id'] = pd.to_numeric(sales_data['customer_id'], downcast='integer') sales_data['unit_price'] = pd.to_numeric(sales_data['unit_price'], downcast='float') sales_data['order_date'] = pd.to_datetime(sales_data['order_date']) # 转换后内存使用 optimized_mem = sales_data.memory_usage(deep=True).sum() print(f"内存节省: {(original_mem - optimized_mem)/original_mem:.1%}")分类数据的黄金法则:
- 基数小于总行数10% → 使用
category类型 - 高频字符串字段 → 优先考虑
category - 参与数值计算的字段 → 保持数值类型
# 智能类型转换函数 def auto_convert(df, col): if df[col].dtype == 'object': if df[col].nunique() / len(df) < 0.1: return df[col].astype('category') return df[col] for col in sales_data.columns: sales_data[col] = auto_convert(sales_data, col)3. 多表合并的进阶技巧
真实业务中我们常需要合并多个数据源。假设我们有:
- 销售记录表
- 客户信息表
- 产品目录表
合并策略对比表:
| 合并类型 | 适用场景 | 代码示例 | 内存消耗 |
|---|---|---|---|
| 内连接 | 只保留匹配记录 | pd.merge(sales, clients) | 低 |
| 左连接 | 保留所有左表记录 | pd.merge(sales, clients, how='left') | 中 |
| 外连接 | 保留所有记录 | pd.merge(sales, clients, how='outer') | 高 |
| 索引连接 | 大数据集合并 | sales.join(clients.set_index('id'), on='client_id') | 最低 |
对于大型数据集,可以使用merge的优化参数:
merged_data = pd.merge( sales_data, client_info, on='client_id', validate='many_to_one', # 确保关系正确性 indicator=True, # 跟踪记录来源 suffixes=('_sales', '_client') )性能优化技巧:
- 合并前对连接键排序可提升30%速度
- 使用
pd.concat()代替merge处理相同结构的表 - 大数据集考虑
dask.dataframe
4. 条件筛选的工程实践
复杂条件筛选是业务分析的核心。Pandas 1.5.3 优化了布尔索引的性能:
# 基础筛选 q3_sales = sales_data[ (sales_data['order_date'].dt.quarter == 3) & (sales_data['region'].isin(['West', 'East'])) & (sales_data['quantity'] > 10) ] # 使用query方法(更易读) high_value = sales_data.query( "unit_price > 100 and loyalty_status == 'Gold' and product_category != 'Clearance'" )动态条件构建技巧:
conditions = [] if include_east_region: conditions.append(df['region'] == 'East') if min_price: conditions.append(df['unit_price'] >= min_price) final_condition = pd.concat(conditions, axis=1).all(axis=1) filtered_data = sales_data[final_condition]对于时间序列数据,可以使用between_time和at_time:
# 获取每天上午的交易 morning_sales = sales_data.set_index('order_date')\ .between_time('09:00', '12:00')\ .reset_index()5. 分组聚合的深度应用
分组聚合远不止groupby().sum()那么简单。看这个多维度分析案例:
# 多级分组与自定义聚合 results = (sales_data .groupby(['region', pd.Grouper(key='order_date', freq='M')]) .agg({ 'quantity': ['sum', 'mean'], 'unit_price': lambda x: (x * sales_data.loc[x.index, 'quantity']).sum(), 'client_id': pd.Series.nunique }) .rename(columns={ '<lambda>': 'revenue', 'client_id': 'unique_clients' }) )分组优化技巧:
- 使用
pd.Grouper进行时间分组 - 大数据集考虑
method='hash'参数 - 避免在分组后立即计算,使用
.pipe()延迟执行
透视表高级用法:
pivot = pd.pivot_table( sales_data, values='unit_price', index=['region', 'sales_rep'], columns=pd.cut(sales_data['order_date'].dt.month, bins=[0,3,6,9,12]), aggfunc=['mean', 'count'], margins=True, fill_value=0 )对于超大数据集,可以使用eval()进行链式操作优化:
sales_data = sales_data.eval(""" total = unit_price * quantity discount_amount = total * discount_rate / 100 net_value = total - discount_amount """)