课程目标

  1. 识别并处理数据中的缺失值、重复值和异常值。
  2. 掌握数据类型的转换与格式化。
  3. 学习数据分箱(离散化)与标准化。

核心Skills

  • 数据清洗:处理缺失值、重复值、异常值
  • 数据转换:类型转换、日期处理、字符串处理
  • 数据加工:分箱、标准化、特征衍生

课件内容与代码

步骤1:导入库与数据

python# 代码文件:day2_data_cleaning.ipynb
# 沿用第1天处理后的数据,或直接读取
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime

# 读取第1天保存的数据
df = pd.read_csv('processed_orders_with_profit.csv', encoding='utf-8')
print(f">>> 数据加载成功!形状: {df.shape}")
display(df.head())

步骤2:深入检查数据质量

pythonprint("="*60)
print("【步骤1:数据质量深度检查】")
print("="*60)
# 1. 缺失值检查
missing_summary = df.isnull().sum()
missing_percentage = (df.isnull().sum() / len(df)) * 100
missing_df = pd.DataFrame({'缺失数量': missing_summary, '缺失比例%': missing_percentage.round(2)})
print(">>> 各字段缺失值统计:")
display(missing_df[missing_df['缺失数量'] > 0])

# 2. 重复值检查
duplicate_rows = df.duplicated().sum()
print(f">>> 完全重复的行数: {duplicate_rows}")
if duplicate_rows > 0:
    display(df[df.duplicated()].head())

# 3. 关键字段唯一值检查
print("\n>>> 关键字段唯一值数量:")
print(f"  订单ID唯一数: {df['order_id'].nunique()} (应与总行数{len(df)}一致)")
print(f"  用户ID唯一数: {df['user_id'].nunique()}")
print(f"  商品ID唯一数: {df['product_id'].nunique()}")

步骤3:处理缺失值

pythonprint("="*60)
print("【步骤2:处理缺失值】")
print("="*60)
# 假设 `cost_price` 和 `brand` 可能存在缺失
# 策略1:删除缺失率过高的行(如果某列缺失太多)
# 策略2:填充 - 数值列用中位数/均值,分类列用众数或‘未知’

# 填充数值型缺失值(例如成本价)
if 'cost_price' in df.columns and df['cost_price'].isnull().sum() > 0:
    median_cost = df['cost_price'].median()
    df['cost_price'].fillna(median_cost, inplace=True)
    print(f">>> 已用中位数 {median_cost:.2f} 填充 cost_price 缺失值。")

# 填充分类型缺失值(例如品牌)
if 'brand' in df.columns and df['brand'].isnull().sum() > 0:
    mode_brand = df['brand'].mode()[0]  # 众数
    df['brand'].fillna(mode_brand, inplace=True)
    print(f">>> 已用众数 '{mode_brand}' 填充 brand 缺失值。")
    # 或者填充为‘未知’
    # df['brand'].fillna('未知', inplace=True)

print(">>> 填充后缺失值复查:")
print(df.isnull().sum())

步骤4:处理重复值

pythonprint("="*60)
print("【步骤3:处理重复值】")
print("="*60)
# 删除完全重复的行
initial_count = len(df)
df.drop_duplicates(inplace=True)
final_count = len(df)
print(f">>> 删除重复行:从 {initial_count} 行减少到 {final_count} 行,删除了 {initial_count - final_count} 行。")

# 业务逻辑去重:例如,同一订单ID不应有重复(除非是不同商品,但本例中order_id若唯一)
# 检查并处理可能的业务重复
order_id_dups = df['order_id'].duplicated().sum()
if order_id_dups > 0:
    print(f"警告:发现 {order_id_dups} 个重复的订单ID,需根据业务逻辑检查。")
    # 可能需要按其他列聚合或删除

步骤5:处理异常值

pythonprint("="*60)
print("【步骤4:检测与处理异常值】")
print("="*60)
# 1. 使用描述性统计和箱线图识别异常值
print(">>> 数值字段描述统计 (关注min, max, 与分位数的差距):")
display(df[['quantity', 'unit_price', 'total_amount', 'gross_profit']].describe())

# 绘制箱线图
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
numeric_cols = ['quantity', 'unit_price', 'total_amount', 'gross_profit']
for i, col in enumerate(numeric_cols):
    ax = axes[i//2, i%2]
    df.boxplot(column=col, ax=ax)
    ax.set_title(f'{col} 箱线图')
plt.tight_layout()
plt.show()

# 2. 基于业务规则和统计方法处理异常值
# 方法A:分位数法(IQR)
def cap_outliers_iqr(series):
    Q1 = series.quantile(0.25)
    Q3 = series.quantile(0.75)
    IQR = Q3 - Q1
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR
    # 将超出边界的值替换为边界值(盖帽法)
    return series.clip(lower_bound, upper_bound)

print(">>> 应用IQR盖帽法处理极端异常值...")
df['total_amount_capped'] = cap_outliers_iqr(df['total_amount'])
df['gross_profit_capped'] = cap_outliers_iqr(df['gross_profit'])

# 对比处理前后
print(">>> 处理前后 total_amount 范围对比:")
print(f"  原始范围: [{df['total_amount'].min():.2f}, {df['total_amount'].max():.2f}]")
print(f"  盖帽后范围: [{df['total_amount_capped'].min():.2f}, {df['total_amount_capped'].max():.2f}]")

步骤6:数据类型转换与日期处理

pythonprint("="*60)
print("【步骤5:数据类型转换与日期处理】")
print("="*60)
# 1. 日期字段转换(如果存在)
if 'order_date' in df.columns:
    df['order_date'] = pd.to_datetime(df['order_date'])
    print(">>> order_date 已转换为 datetime 类型。")
    # 衍生新的日期特征
    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  # 周一=0, 周日=6
    print(">>> 已衍生出年、月、日、星期几等特征。")
    display(df[['order_date', 'order_year', 'order_month', 'order_day', 'order_weekday']].head())

# 2. 分类字段转换为category类型以节省内存
cat_cols = ['city', 'category', 'brand', 'warehouse']
for col in cat_cols:
    if col in df.columns:
        df[col] = df[col].astype('category')
        print(f">>> {col} 已转换为 category 类型。")

步骤7:数据分箱(离散化)

pythonprint("="*60)
print("【步骤6:数据分箱(离散化)】")
print("="*60)
# 1. 等宽分箱:将用户消费金额分为高、中、低三档
if 'total_amount_capped' in df.columns:
    df['amount_level'] = pd.cut(df['total_amount_capped'],
                                 bins=[0, 500, 2000, df['total_amount_capped'].max()],
                                 labels=['低', '中', '高'],
                                 include_lowest=True)
    print(">>> 订单金额分箱(等宽)完成。")
    print(df['amount_level'].value_counts())

# 2. 等频分箱(按分位数):将用户按消费金额分为4组,每组数量大致相等
if 'total_amount_capped' in df.columns:
    df['amount_quantile'] = pd.qcut(df['total_amount_capped'], q=4, labels=['Q1', 'Q2', 'Q3', 'Q4'])
    print(">>> 订单金额等频分箱完成。")
    print(df['amount_quantile'].value_counts())

步骤8:数据标准化(归一化)

pythonprint("="*60)
print("【步骤7:数据标准化】")
print("="*60)
# 为后续建模准备,对数值特征进行标准化
from sklearn.preprocessing import MinMaxScaler, StandardScaler

# 假设我们需要标准化的列
scale_cols = ['unit_price', 'total_amount_capped', 'gross_profit_capped']
scaler = MinMaxScaler()  # 归一化到[0,1]
# scaler = StandardScaler() # Z-score标准化

df_scaled = df.copy()
df_scaled[scale_cols] = scaler.fit_transform(df[scale_cols])
print(">>> 已对数值列进行归一化处理。")
display(df_scaled[scale_cols].head())

步骤9:保存清洗后的数据

python# 保存清洗和预处理后的数据
df.to_csv('cleaned_orders.csv', index=False, encoding='utf-8')
df_scaled.to_csv('cleaned_and_scaled_orders.csv', index=False, encoding='utf-8')
print(">>> 清洗后的数据已保存为 'cleaned_orders.csv'")
print(">>> 清洗并标准化后的数据已保存为 'cleaned_and_scaled_orders.csv'")

本日要点总结

  1. 数据质量评估.isnull(), .duplicated(), .nunique()
  2. 缺失值处理.fillna() 用中位数、众数或固定值填充;.dropna() 删除。
  3. 异常值检测:箱线图、IQR(四分位距)法。
  4. 异常值处理:盖帽法(.clip())、删除或视为特殊值。
  5. 类型转换pd.to_datetime(), .astype('category')
  6. 特征衍生:从日期字段提取年、月、日等。
  7. 数据分箱pd.cut()(等宽)、pd.qcut()(等频)。
  8. 数据标准化MinMaxScaler(归一化)、StandardScaler(Z-score标准化)。