← 返回首页

第10章 · 数据预处理及分析

数据预处理与分析

数据预处理是数据科学项目中至关重要的环节,直接影响分析结果的准确性和模型性能。本章将介绍数据预处理的核心技术,包括缺失值和异常值处理、特征缩放与编码,以及探索性数据分析方法。

学习目标:完成本章后,您将能够识别和处理数据中的缺失值与异常值,掌握特征缩放和编码技术,并能够进行探索性数据分析以发现数据中的模式和洞察。

本章内容概览

缺失/异常值处理

import pandas as pd
import numpy as np
from sklearn.impute import SimpleImputer

# 创建包含缺失值和异常值的示例数据
print("=== 缺失值与异常值处理 ===")
data = {
    '年龄': [25, 30, np.nan, 35, 28, 150, 32, np.nan],  # 150是异常值
    '收入': [50000, 60000, 55000, np.nan, 52000, 1000000, np.nan, 58000],  # 1000000是异常值
    '城市': ['北京', '上海', np.nan, '广州', '深圳', '北京', '上海', '杭州']
}

df = pd.DataFrame(data)
print("原始数据:")
print(df)

# 检查缺失值
print("\n缺失值统计:")
print(df.isnull().sum())

# 处理缺失值 - 数值列用均值填充
print("\n处理缺失值...")
df_cleaned = df.copy()
df_cleaned['年龄'].fillna(df_cleaned['年龄'].mean(), inplace=True)
df_cleaned['收入'].fillna(df_cleaned['收入'].mean(), inplace=True)
df_cleaned['城市'].fillna('未知', inplace=True)

# 检测和处理异常值
print("\n检测异常值...")
def detect_outliers(data):
    Q1 = data.quantile(0.25)
    Q3 = data.quantile(0.75)
    IQR = Q3 - Q1
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR
    outliers = data[(data < lower_bound) | (data > upper_bound)]
    return outliers

print("年龄异常值:", detect_outliers(df_cleaned['年龄']).tolist())
print("收入异常值:", detect_outliers(df_cleaned['收入']).tolist())

# 处理异常值 - 使用中位数替换
print("\n处理异常值...")
age_median = df_cleaned['年龄'].median()
income_median = df_cleaned['收入'].median()

df_cleaned.loc[df_cleaned['年龄'] > 100, '年龄'] = age_median
df_cleaned.loc[df_cleaned['收入'] > 200000, '收入'] = income_median

print("处理后的数据:")
print(df_cleaned)

特征缩放与编码

from sklearn.preprocessing import StandardScaler, MinMaxScaler, LabelEncoder, OneHotEncoder
from sklearn.compose import ColumnTransformer

print("=== 特征缩放与编码 ===")

# 创建示例数据
data = {
    '年龄': [25, 30, 35, 28, 32],
    '收入': [50000, 60000, 55000, 52000, 58000],
    '城市': ['北京', '上海', '广州', '深圳', '北京'],
    '学历': ['本科', '硕士', '博士', '本科', '硕士']
}

df = pd.DataFrame(data)
print("原始数据:")
print(df)

# 特征缩放 - 标准化
print("\n=== 特征标准化 ===")
scaler = StandardScaler()
scaled_data = scaler.fit_transform(df[['年龄', '收入']])
df_scaled = pd.DataFrame(scaled_data, columns=['年龄_标准化', '收入_标准化'])
print(df_scaled)

# 特征缩放 - 归一化
print("\n=== 特征归一化 ===")
minmax_scaler = MinMaxScaler()
normalized_data = minmax_scaler.fit_transform(df[['年龄', '收入']])
df_normalized = pd.DataFrame(normalized_data, columns=['年龄_归一化', '收入_归一化'])
print(df_normalized)

# 标签编码
print("\n=== 标签编码 ===")
label_encoder = LabelEncoder()
df['城市_编码'] = label_encoder.fit_transform(df['城市'])
df['学历_编码'] = label_encoder.fit_transform(df['学历'])
print("标签编码后的数据:")
print(df[['城市', '城市_编码', '学历', '学历_编码']])

# 独热编码
print("\n=== 独热编码 ===")
onehot_encoder = OneHotEncoder(sparse_output=False)
onehot_data = onehot_encoder.fit_transform(df[['城市']])
onehot_columns = onehot_encoder.get_feature_names_out(['城市'])
df_onehot = pd.DataFrame(onehot_data, columns=onehot_columns)
print("独热编码后的数据:")
print(df_onehot)

# 组合所有处理后的特征
print("\n=== 最终处理后的数据集 ===")
df_final = pd.concat([df_scaled, df_normalized, df[['城市_编码', '学历_编码']], df_onehot], axis=1)
print(df_final)

探索性数据分析

import matplotlib.pyplot as plt
import seaborn as sns

print("=== 探索性数据分析 ===")

# 创建示例数据集
np.random.seed(42)
n_samples = 200
data = {
    '年龄': np.random.normal(35, 10, n_samples),
    '收入': np.random.normal(50000, 15000, n_samples),
    '消费分数': np.random.normal(70, 15, n_samples),
    '城市': np.random.choice(['北京', '上海', '广州', '深圳'], n_samples),
    '性别': np.random.choice(['男', '女'], n_samples)
}

df = pd.DataFrame(data)
print("数据集基本信息:")
print(df.info())
print("\n数据集描述性统计:")
print(df.describe())

# 数据分布可视化
print("\n=== 数据分布分析 ===")
fig, axes = plt.subplots(2, 2, figsize=(12, 10))

# 年龄分布
axes[0, 0].hist(df['年龄'], bins=20, color='skyblue', alpha=0.7)
axes[0, 0].set_title('年龄分布')
axes[0, 0].set_xlabel('年龄')
axes[0, 0].set_ylabel('频数')

// 收入分布
axes[0, 1].hist(df['收入'], bins=20, color='lightcoral', alpha=0.7)
axes[0, 1].set_title('收入分布')
axes[0, 1].set_xlabel('收入')
axes[0, 1].set_ylabel('频数')

// 消费分数箱线图
axes[1, 0].boxplot(df['消费分数'])
axes[1, 0].set_title('消费分数箱线图')
axes[1, 0].set_ylabel('消费分数')

// 城市分布
city_counts = df['城市'].value_counts()
axes[1, 1].bar(city_counts.index, city_counts.values, color=['skyblue', 'lightcoral', 'lightgreen', 'gold'])
axes[1, 1].set_title('城市分布')
axes[1, 1].set_xlabel('城市')
axes[1, 1].set_ylabel('人数')

plt.tight_layout()
plt.show()

// 相关性分析
print("\n=== 相关性分析 ===")
numeric_cols = df.select_dtypes(include=[np.number]).columns
correlation_matrix = df[numeric_cols].corr()
print("数值变量相关性矩阵:")
print(correlation_matrix)

// 相关性热力图
plt.figure(figsize=(8, 6))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', center=0)
plt.title('变量相关性热力图')
plt.show()

// 分组分析
print("\n=== 分组分析 ===")
print("不同城市的平均收入:")
print(df.groupby('城市')['收入'].mean())

print("\n不同性别的平均消费分数:")
print(df.groupby('性别')['消费分数'].mean())

// 散点图分析关系
plt.figure(figsize=(10, 6))
sns.scatterplot(data=df, x='年龄', y='收入', hue='城市', style='性别', s=100)
plt.title('年龄与收入关系散点图')
plt.show()

子页面导航