哈喽,我是小白~
今儿和大家介绍的这个工具,非常重要,即:数据预处理工具!~
数据预处理 能够提高数据质量,确保算法的有效性和准确性。通过清理、转换和标准化数据,预处理工具可以减少噪声和冗余,提升模型的训练效率。最后,良好的数据预处理还可以帮助发掘数据中的潜在模式,从而增强模型的泛化能力。
涉及到的有:
缺失值处理 特征标准化(Standardization) 特征归一化(Normalization) 类别编码(Categorical Encoding) 特征二值化(Binarization) 特征选择(Feature Selection) 主成分分析(PCA)与降维 数据变换(Data Transformation) 平衡处理(Imbalanced Data Handling) 时间序列数据处理
咱们从核心原理以及完整的案例和大家聊聊~
1. 缺失值处理
缺失值处理是数据预处理中必须的步骤,处理不当可能影响模型的准确性和稳定性。原因有很多,如数据采集不全、记录丢失等。
常用方法:
1. 删除法(Deletion Method):
行删除(Listwise Deletion):将包含缺失值的整行数据删除,适用于缺失值较少时。 列删除:如果某一特征的缺失值比例极高,可以删除该特征。
优点:简单直接。
缺点:丢失信息,不适用于缺失值多的数据集。
2. 插补法(Imputation Method):
均值/中位数/众数填补(Mean/Median/Mode Imputation):
用现有样本的均值、中位数或众数填补缺失值。对数值型数据,使用均值或中位数;对分类数据,使用众数。
回归插补(Regression Imputation): 使用回归模型预测缺失值。设缺失值特征为 ,其余特征为 ,用 建立一个回归模型预测 :
多重插补(Multiple Imputation by Chained Equations, MICE):通过一系列回归模型循环预测多个缺失值。
3. 插值法(Interpolation Method):
对于时间序列数据,可以使用线性插值、样条插值等方法估计缺失值。
线性插值(Linear Interpolation):
4. KNN 插补(K-Nearest Neighbors Imputation):
基于相似数据点(K近邻)的平均值填补缺失值:
核心公式
假设我们使用均值插补法,给定一个数据集 ,其中部分数据缺失。缺失值可以用现有样本的均值替代。对于缺失的数据点 ,其插补公式为:
案例代码
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# 设置随机种子
np.random.seed(42)
# 生成虚拟数据集
data = pd.DataFrame({
'A': np.random.randn(1000),
'B': np.random.randn(1000),
'C': np.random.randint(1, 100, 1000),
'D': np.random.randn(1000)
})
# 随机生成缺失值
missing_mask = np.random.rand(*data.shape) < 0.1 # 10%缺失率
data[missing_mask] = np.nan
# 创建子图
fig, axs = plt.subplots(2, 2, figsize=(14, 10))
# 图1:原始数据缺失值分布图
sns.heatmap(data.isnull(), cbar=False, cmap='viridis', ax=axs[0, 0])
axs[0, 0].set_title('Missing Value Distribution in Original Dataset')
# 图2:特征缺失值比例
missing_percent = data.isnull().mean() * 100
missing_percent.plot(kind='bar', color=['red', 'blue', 'green', 'orange'], ax=axs[0, 1])
axs[0, 1].set_title('Percentage of Missing Values per Feature')
axs[0, 1].set_ylabel('Percentage (%)')
# 缺失值处理 - 填充缺失值
data_filled_mean = data.fillna(data.mean())
# 图3:均值填充后特征的分布情况(只展示A、B两列的变化)
sns.kdeplot(data['A'], color='blue', label='Original A Column', ax=axs[1, 0])
sns.kdeplot(data_filled_mean['A'], color='red', label='Mean Filled A Column', ax=axs[1, 0])
sns.kdeplot(data['B'], color='green', label='Original B Column', ax=axs[1, 0])
sns.kdeplot(data_filled_mean['B'], color='orange', label='Mean Filled B Column', ax=axs[1, 0])
axs[1, 0].set_title('Distribution of A and B Features Before and After Mean Filling')
# 缺失值处理 - 删除含缺失值的行
data_dropped = data.dropna()
# 图4:删除缺失值后的数据集分布(展示 C 列的分布变化)
sns.kdeplot(data['C'].dropna(), color='purple', label='Original C Column', ax=axs[1, 1])
sns.kdeplot(data_dropped['C'], color='yellow', label='Dropped Missing Values C Column', ax=axs[1, 1])
axs[1, 1].set_title('Distribution of C Feature Before and After Dropping Missing Values')
# 调整子图布局
plt.tight_layout()
# 显示图形
plt.show()
数据生成:
data
是一个包含1000条记录和4个特征的数据集(A
,B
,C
,D
)。使用 np.random.randn
生成正态分布的数据,np.random.randint
生成C
列中的整数值。10%的数据被随机设置为 NaN
,模拟缺失值。
图1:使用 seaborn
的heatmap
展示数据集中缺失值的分布情况。图2:绘制各特征缺失值的比例(以条形图形式呈现)。 图3:使用均值填充后的数据与原始数据对比,展示特征 A
和B
的分布变化(使用KDE
核密度估计)。图4:展示删除含缺失值的行后,特征 C
的分布变化。
2. 特征标准化
标准化是将特征转换为零均值、单位方差的过程。标准化后的数据具有相同的量纲,有助于使梯度下降等算法收敛更快,且对距离敏感的模型如KNN、SVM等有重要影响。
原理
标准化将特征值按下列公式进行变换:
其中:
是标准化后的数据, 是原始特征值, 是该特征的均值, 是该特征的标准差。
核心公式
1. 均值的计算:
给定数据集 ,其均值为:
2. 标准差的计算:
标准差 表示数据点偏离均值的平均程度:
标准化的目标是让所有特征的均值为0,标准差为1,从而消除不同量纲的影响。将数据点 进行标准化,公式为:
案例代码
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
import seaborn as sns
# 设置随机种子,确保每次结果相同
np.random.seed(42)
# 创建虚拟数据集:每个特征的均值和方差不同
X = np.array([
np.random.normal(loc=100, scale=20, size=500), # 第一个特征:均值100,标准差20
np.random.normal(loc=50, scale=5, size=500), # 第二个特征:均值50,标准差5
np.random.normal(loc=-30, scale=10, size=500) # 第三个特征:均值-30,标准差10
]).T
# 使用StandardScaler进行标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 绘制图形
fig, axs = plt.subplots(2, 2, figsize=(12, 10))
fig.suptitle('Effect of Standardization on Data Distribution and Scatter Plot', fontsize=16)
# 直方图:原始数据
colors = ['#FF6347', '#4682B4', '#32CD32']
for i in range(3):
sns.histplot(X[:, i], bins=30, color=colors[i], ax=axs[0, 0], label=f'Feature {i+1}', kde=True)
axs[0, 0].set_title('Original Data - Histogram')
axs[0, 0].legend()
# 直方图:标准化后的数据
for i in range(3):
sns.histplot(X_scaled[:, i], bins=30, color=colors[i], ax=axs[0, 1], label=f'Feature {i+1}', kde=True)
axs[0, 1].set_title('Standardized Data - Histogram')
axs[0, 1].legend()
# 散点图:原始数据(使用前两个特征)
axs[1, 0].scatter(X[:, 0], X[:, 1], color='#FF6347', alpha=0.7, label='Original Data')
axs[1, 0].set_title('Original Data - Scatter Plot (Feature 1 vs Feature 2)')
axs[1, 0].set_xlabel('Feature 1')
axs[1, 0].set_ylabel('Feature 2')
axs[1, 0].legend()
# 散点图:标准化后的数据(使用前两个特征)
axs[1, 1].scatter(X_scaled[:, 0], X_scaled[:, 1], color='#4682B4', alpha=0.7, label='Standardized Data')
axs[1, 1].set_title('Standardized Data - Scatter Plot (Feature 1 vs Feature 2)')
axs[1, 1].set_xlabel('Feature 1 (Standardized)')
axs[1, 1].set_ylabel('Feature 2 (Standardized)')
axs[1, 1].legend()
# 调整布局
plt.tight_layout(rect=[0, 0.03, 1, 0.95])
plt.show()
数据集生成:创建了三个特征,均值和标准差各不相同。第一个特征的均值是100,标准差是20;第二个特征的均值是50,标准差是5;第三个特征的均值是-30,标准差是10。
特征标准化:使用
StandardScaler
对数据集进行标准化,使每个特征的均值为0,标准差为1。
直方图部分:原始数据的特征分布差异很大,经过标准化后,所有特征的数据分布变得接近,均值为0,标准差为1。 散点图部分:标准化前,特征的范围相差较大;标准化后,特征都缩放到了相同的范围。
3. 特征归一化
归一化是一种将数据缩放到特定区间(通常是 [0,1])的过程。它常用于模型对数值范围敏感的情况下,尤其是神经网络或其他依赖于距离计算的模型。
原理
最常用的归一化方法是 Min-Max Scaling,其公式为:
核心公式
对于数据集 ,其最小值为 ,最大值为 。每个数据点 经过归一化后,映射到 [0, 1] 范围,计算公式为:
这一操作确保数据的范围统一,有助于模型处理不同尺度的特征。
案例代码
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import MinMaxScaler
# 生成虚拟数据集
np.random.seed(42)
data = {
'Feature 1': np.random.normal(50, 15, 100), # 平均值50,标准差15
'Feature 2': np.random.uniform(0, 100, 100) # 均匀分布在0-100之间
}
df = pd.DataFrame(data)
# 初始化 Min-Max 归一化器
scaler = MinMaxScaler()
# 对数据进行归一化处理
df_normalized = pd.DataFrame(scaler.fit_transform(df), columns=['Feature 1', 'Feature 2'])
# 创建一个2x2的子图布局
fig, axs = plt.subplots(2, 2, figsize=(12, 10))
# 第一张图:特征1归一化前后的分布对比
sns.histplot(df['Feature 1'], bins=15, color='blue', kde=True, ax=axs[0, 0], label='Before Normalization')
sns.histplot(df_normalized['Feature 1'], bins=15, color='red', kde=True, ax=axs[0, 0], label='After Normalization')
axs[0, 0].set_title('Feature 1: Before vs After Normalization')
axs[0, 0].legend()
# 第二张图:特征2归一化前后的分布对比
sns.histplot(df['Feature 2'], bins=15, color='green', kde=True, ax=axs[0, 1], label='Before Normalization')
sns.histplot(df_normalized['Feature 2'], bins=15, color='purple', kde=True, ax=axs[0, 1], label='After Normalization')
axs[0, 1].set_title('Feature 2: Before vs After Normalization')
axs[0, 1].legend()
# 第三张图:归一化前的特征1和特征2的散点图
axs[1, 0].scatter(df['Feature 1'], df['Feature 2'], color='orange', label='Before Normalization', alpha=0.7)
axs[1, 0].set_title('Scatter Plot Before Normalization')
axs[1, 0].set_xlabel('Feature 1')
axs[1, 0].set_ylabel('Feature 2')
axs[1, 0].legend()
# 第四张图:归一化后的特征1和特征2的散点图
axs[1, 1].scatter(df_normalized['Feature 1'], df_normalized['Feature 2'], color='cyan', label='After Normalization', alpha=0.7)
axs[1, 1].set_title('Scatter Plot After Normalization')
axs[1, 1].set_xlabel('Feature 1')
axs[1, 1].set_ylabel('Feature 2')
axs[1, 1].legend()
# 调整图形布局
plt.tight_layout()
# 显示图形
plt.show()
数据生成:
Feature 1
是一个服从正态分布的特征,平均值为 50,标准差为 15。Feature 2
是一个在 0 和 100 之间的均匀分布。Min-Max 归一化:我们使用
MinMaxScaler
将每个特征缩放到 [0,1] 的范围内。
Feature 1
归一化前后的分布(蓝色和红色对比)。Feature 2
归一化前后的分布(绿色和紫色对比)。原始数据的散点图(橙色)。 归一化后的数据散点图(青色)。
4. 类别编码
类别编码是将分类变量转换为数值特征的过程,方便模型使用。分类变量可以是名义型(无序)或序数型(有序),针对不同类型的分类变量,编码方式不同。
常用方法:
1. Label Encoding:
将每个类别转换为唯一的整数标签。例如,对于类别集合 ,可以转换为 。
优点:简单直接。 缺点:可能引入类别顺序的信息(适合有序数据)。
2. One-Hot Encoding:
为每个类别创建一个二进制特征。假设类别为 ,对应的One-Hot编码为:
3. Target Encoding:
用类别的目标变量(如标签)的均值来替代类别值,适合树模型。
核心公式
对于One-Hot Encoding,给定类别集合 ,原始类别特征 被变换为 个二进制变量 :
One-Hot编码是将类别转换为0或1的二进制矩阵表示。
案例代码
# 导入所需库
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.preprocessing import LabelEncoder, OneHotEncoder
# 设置图形风格
sns.set(style="whitegrid")
plt.figure(figsize=(16, 10)) # 设置总图尺寸
# 生成虚拟数据集
np.random.seed(42)
data = {
'City': np.random.choice(['New York', 'Los Angeles', 'Chicago', 'Houston'], size=100),
'Weather': np.random.choice(['Sunny', 'Rainy', 'Cloudy'], size=100),
'Sales': np.random.randint(100, 1000, size=100)
}
df = pd.DataFrame(data)
# 一、类别编码 - Label Encoding
label_encoder = LabelEncoder()
df['City_Label'] = label_encoder.fit_transform(df['City'])
df['Weather_Label'] = label_encoder.fit_transform(df['Weather'])
# 二、类别编码 - One-Hot Encoding
df_one_hot = pd.get_dummies(df, columns=['City', 'Weather'])
# 三、数据分析与可视化
# 创建子图 - 2行2列
fig, axs = plt.subplots(2, 2, figsize=(16, 12))
# 1. 柱状图:展示每个城市的销售情况 (原始类别变量)
sns.barplot(x='City', y='Sales', data=df, ax=axs[0, 0], palette='Set2')
axs[0, 0].set_title('Sales by City (Original Categorical Variable)', fontsize=14)
axs[0, 0].set_xlabel('City')
axs[0, 0].set_ylabel('Sales')
# 2. 计数图:展示天气的分布 (原始类别变量)
sns.countplot(x='Weather', data=df, ax=axs[0, 1], palette='Set3')
axs[0, 1].set_title('Distribution of Weather (Original Categorical Variable)', fontsize=14)
axs[0, 1].set_xlabel('Weather')
axs[0, 1].set_ylabel('Count')
# 3. 热力图:展示One-Hot编码后,城市和销售额之间的相关性
sns.heatmap(df_one_hot[['Sales', 'City_New York', 'City_Los Angeles', 'City_Chicago', 'City_Houston']].corr(), annot=True, cmap='coolwarm', ax=axs[1, 0])
axs[1, 0].set_title('Correlation Heatmap (One-Hot Encoding)', fontsize=14)
# 4. 散点图:展示Label Encoding后,天气编码和销售额的关系
sns.scatterplot(x='Weather_Label', y='Sales', data=df, ax=axs[1, 1], hue='Weather', palette='Set1')
axs[1, 1].set_title('Sales vs Weather_Label (Label Encoding)', fontsize=14)
axs[1, 1].set_xlabel('Weather Label')
axs[1, 1].set_ylabel('Sales')
# 调整布局并展示
plt.tight_layout()
plt.show()
数据生成:创建了一个虚拟数据集 df
,包含3列:City
(城市)、Weather
(天气)、Sales
(销售额)。类别编码:使用 Label Encoding
将City
和Weather
类别变量转换为数值型变量。使用One-Hot Encoding
对City
和Weather
进行独热编码,生成了新的列表示每个类别的布尔值。
柱状图:显示每个城市的销售额分布。 计数图:展示了天气类别的分布情况。 热力图:显示了 One-Hot Encoding
后的城市与销售额的相关性。散点图:展示 Label Encoding
后,天气编码和销售额之间的关系。
5. 特征二值化
二值化是将数值特征转换为二进制特征(0或1)的过程。这通常用于将连续特征变为离散特征。通过设定一个阈值,数值大于该阈值的变为1,小于等于的变为0。
原理
对于给定阈值 ,二值化特征 的计算公式为:
核心公式
设定某一特征的值域为 ,并选择某个阈值 。对于每个样
本点 ,二值化后的值 为:
这种变换使得特征转化为二进制特征,常用于逻辑回归或决策树模型。
案例代码
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import Binarizer
from sklearn.datasets import make_classification
# 创建虚拟数据集
np.random.seed(42)
X, _ = make_classification(n_samples=10000, n_features=3, n_informative=2, n_redundant=1, n_classes=2, random_state=0)
# 设置二值化阈值
threshold = 0.5
binarizer = Binarizer(threshold=threshold)
# 对特征数据进行二值化
X_binarized = binarizer.fit_transform(X)
# 创建图像
fig, axs = plt.subplots(2, 2, figsize=(14, 10))
# 原始数据的散点图
axs[0, 0].scatter(X[:, 0], X[:, 1], c=X[:, 2], cmap='rainbow', edgecolor='k', s=100)
axs[0, 0].set_title("Scatter Plot of Original Data (Feature 1 vs Feature 2)", fontsize=14)
axs[0, 0].set_xlabel("Feature 1")
axs[0, 0].set_ylabel("Feature 2")
# 原始数据的柱状图
axs[0, 1].hist(X, bins=15, color=['#FF6F61', '#6B5B95', '#88B04B'], label=['Feature 1', 'Feature 2', 'Feature 3'], alpha=0.75)
axs[0, 1].set_title("Histogram of Original Data", fontsize=14)
axs[0, 1].legend()
# 二值化后的数据散点图
axs[1, 0].scatter(X_binarized[:, 0], X_binarized[:, 1], c=X_binarized[:, 2], cmap='rainbow', edgecolor='k', s=100)
axs[1, 0].set_title("Scatter Plot of Binarized Data (Feature 1 vs Feature 2)", fontsize=14)
axs[1, 0].set_xlabel("Feature 1")
axs[1, 0].set_ylabel("Feature 2")
# 二值化后的数据柱状图
axs[1, 1].hist(X_binarized, bins=2, color=['#FF6F61', '#6B5B95', '#88B04B'], label=['Feature 1', 'Feature 2', 'Feature 3'], alpha=0.75)
axs[1, 1].set_title("Histogram of Binarized Data", fontsize=14)
axs[1, 1].legend()
# 调整布局
plt.tight_layout()
plt.show()
代码说明:
make_classification:用于生成一个包含三个特征的分类数据集。 Binarizer:将数据集中超过阈值的数据置为1,小于或等于阈值的数据置为0。
原始数据的散点图,显示特征1与特征2的关系,并使用颜色表示特征3的变化。 原始数据的柱状图,显示三个特征的分布情况。 二值化后的散点图,显示特征1和特征2经过二值化后的变化。 二值化后的柱状图,显示二值化后的数据分布。
这些图像有助于展示二值化前后数据的变化以及特征的二值化如何影响数据的分布和结构。
6. 特征选择
特征选择是通过删除无关或冗余特征来提升模型性能的一种技术。它可以减少维度,降低模型复杂度,防止过拟合,并提高模型的训练速度。
常用方法:
1. 过滤法(Filter Method):
根据统计指标(如方差、相关性、卡方检验等)选择特征,与模型无关。
方差选择:去除方差较小的特征。 相关性选择:通过计算特征与目标变量的相关系数来筛选。
2. 包裹法(Wrapper Method):
基于模型性能选择特征子集。递归特征消除(Recursive Feature Elimination, RFE)是一种常用的包裹法。
RFE:通过反复训练模型,递归地消除权重较小的特征。
3. 嵌入法(Embedded Method):
通过模型自带的特征选择机制,例如正则化(L1)或决策树中的特征重要性。
核心公式:
以 Lasso 回归 为例,Lasso 在损失函数中加入 L1 正则化项:
L1正则化通过对权重加惩罚,将部分特征的系数压缩为0,从而实现特征选择。
案例代码
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.feature_selection import SelectFromModel
# 1. 生成虚拟分类数据集
X, y = make_classification(n_samples=10000, n_features=10, n_informative=5, n_redundant=2, n_classes=2, random_state=42)
# 2. 拆分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 3. 使用随机森林训练模型并计算特征重要性
rf = RandomForestClassifier(n_estimators=100, random_state=42)
rf.fit(X_train, y_train)
# 4. 获取特征重要性
importances = rf.feature_importances_
# 5. 选择重要特征 (阈值为平均重要性以上的特征)
selector = SelectFromModel(rf, threshold='mean', prefit=True)
X_train_selected = selector.transform(X_train)
X_test_selected = selector.transform(X_test)
# 6. 在原始特征和选择后特征上训练模型,并比较性能
rf_full = RandomForestClassifier(n_estimators=100, random_state=42)
rf_full.fit(X_train, y_train)
y_pred_full = rf_full.predict(X_test)
accuracy_full = accuracy_score(y_test, y_pred_full)
rf_selected = RandomForestClassifier(n_estimators=100, random_state=42)
rf_selected.fit(X_train_selected, y_train)
y_pred_selected = rf_selected.predict(X_test_selected)
accuracy_selected = accuracy_score(y_test, y_pred_selected)
# 7. 可视化
fig, axs = plt.subplots(1, 2, figsize=(14, 6))
# 图1: 特征重要性图
axs[0].barh(range(X.shape[1]), importances, color='c', edgecolor='k')
axs[0].set_yticks(range(X.shape[1]))
axs[0].set_yticklabels([f'Feature {i}' for i in range(X.shape[1])])
axs[0].set_title('Feature Importance (Random Forest)', fontsize=16)
axs[0].set_xlabel('Importance', fontsize=12)
axs[0].set_ylabel('Feature', fontsize=12)
axs[0].invert_yaxis()
# 图2: 准确率比较
axs[1].bar(['Full Features', 'Selected Features'], [accuracy_full, accuracy_selected], color=['blue', 'orange'], edgecolor='k')
axs[1].set_title('Accuracy Comparison', fontsize=16)
axs[1].set_ylabel('Accuracy', fontsize=12)
axs[1].set_ylim(0, 1)
for i, v in enumerate([accuracy_full, accuracy_selected]):
axs[1].text(i, v + 0.02, f'{v:.2f}', ha='center', fontsize=12)
plt.tight_layout()
plt.show()
生成虚拟数据集: make_classification
生成一个包含 10000 个样本、10 个特征的二分类数据集,其中 5 个是有效特征,2 个是冗余特征。特征重要性分析:通过随机森林 ( RandomForestClassifier
) 计算各个特征的重要性。特征选择:使用 SelectFromModel
选择重要性高于平均值的特征。比较模型性能:训练全特征和筛选后的特征两种模型,比较两者在测试集上的分类准确率。
左侧图展示了每个特征的重要性。 右侧图展示了全特征和筛选后特征的分类准确率。
7. 主成分分析与降维
PCA 是一种无监督降维方法,目的是通过将数据投影到方差最大的方向上来减少维度。PCA能够将高维数据映射到低维空间,尽可能保留数据的主要信息。
原理
PCA 的目标是找到数据协方差矩阵的特征向量,特征值越大的特征向量代表的方向包含的数据方差信息越多。通过投影到前 个主成分,可以实现数据的降维。
核心公式:
1. 协方差矩阵:
给定数据矩阵 ,其协方差矩阵 为:
2. 特征值分解:
协方差矩阵的特征值分解:
其中, 是特征值, 是特征向量。
3. 主成分选择:
按照特征值的大小选择前 个主成分。对数据矩阵 进行投影:
其中 是由前 个特征向量组成的矩阵。
公式推导:
设 是 的数据矩阵,首先计算数据矩阵的协方差矩阵 ,再进行特征值分解得到特征向量矩阵 。最后,将原始数据 投影到新空间:
案例代码
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
# 创建虚拟数据集
X, y = make_blobs(n_samples=3000, n_features=5, centers=4, cluster_std=1.5, random_state=42)
# 设置图像大小
plt.figure(figsize=(12, 6))
# ------------------------
# 案例 1:PCA 主成分分析
# ------------------------
# 进行PCA降维到2D
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X)
# 创建子图1: PCA的结果
plt.subplot(1, 2, 1)
plt.title('PCA (Principal Component Analysis)', fontsize=14)
plt.scatter(X_pca[:, 0], X_pca[:, 1], c=y, cmap='rainbow', edgecolor='k', s=60)
plt.xlabel('PCA Component 1')
plt.ylabel('PCA Component 2')
plt.grid(True)
# ------------------------
# 案例 2:t-SNE 非线性降维
# ------------------------
# 进行t-SNE降维到2D
tsne = TSNE(n_components=2, random_state=42)
X_tsne = tsne.fit_transform(X)
# 创建子图2: t-SNE的结果
plt.subplot(1, 2, 2)
plt.title('t-SNE (t-Distributed Stochastic Neighbor Embedding)', fontsize=14)
plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=y, cmap='rainbow', edgecolor='k', s=60)
plt.xlabel('t-SNE Component 1')
plt.ylabel('t-SNE Component 2')
plt.grid(True)
# 调整布局
plt.tight_layout()
plt.show()
代码解释:
数据生成:使用 make_blobs
生成具有4个簇的虚拟数据集,每个样本有5个特征。PCA:对数据进行PCA降维到二维空间,并在子图1中可视化结果。 t-SNE:对数据进行t-SNE降维到二维空间,并在子图2中可视化结果。
左图展示PCA降维的结果,主成分是线性变换的结果,簇的形状比较规则。 右图展示t-SNE降维的结果,能够更好地捕捉到簇之间的复杂、非线性结构。
通过对比PCA和t-SNE的图像,可以清楚地看到PCA适合线性结构,而t-SNE可以更好地处理非线性数据的分布。
8. 数据变换
数据变换是对数据进行数学运算以改变其分布或范围,常用于正态化数据或减少偏度。常用方法包括对数变换、平方根变换、Box-Cox变换等。
常用方法:
1. 对数变换(Log Transformation):
对于呈现幂次或指数分布的数据,对数变换可以使其更符合正态分布。
2. 平方根变换(Square Root Transformation):
适用于较小范围数据,能减少数据的偏度。
3. Box-Cox 变换:
通过调节参数 进行非线性变换,以使数据更接近正态分布。
核心公式
对于 Box-Cox 变换,假设 是非负的连续数据,我们通过调整 的值来选择变换形式:
当 时,Box-Cox 变换为对数变换。 当 时,变换为幂变换。
Box-Cox 的目标是找到最优的 ,使得变换后的数据更符合正态分布。
案例代码
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# 设置随机种子,保证结果可重复
np.random.seed(42)
# 生成虚拟数据集
n_samples = 1000
X1 = np.random.normal(loc=50, scale=10, size=n_samples) # 正态分布数据
X2 = np.random.exponential(scale=2, size=n_samples) # 指数分布数据
X3 = np.random.uniform(low=20, high=70, size=n_samples) # 均匀分布数据
# 组合为数据集
df = pd.DataFrame({'Feature1': X1, 'Feature2': X2, 'Feature3': X3})
# 数据标准化
scaler = StandardScaler()
df_scaled = pd.DataFrame(scaler.fit_transform(df), columns=df.columns)
# 数据归一化
min_max_scaler = MinMaxScaler()
df_normalized = pd.DataFrame(min_max_scaler.fit_transform(df), columns=df.columns)
# 对数变换(加上一个小值以避免log(0)问题)
df_log_transformed = df.copy()
df_log_transformed['Feature2'] = np.log(df_log_transformed['Feature2'] + 1)
# 画图设置
plt.figure(figsize=(16, 12))
# 原始数据的散点图
plt.subplot(2, 2, 1)
sns.scatterplot(x='Feature1', y='Feature2', data=df, color='blue', label='Original Data', alpha=0.6)
plt.title('Original Data (Scatter Plot)', fontsize=14)
plt.xlabel('Feature1', fontsize=12)
plt.ylabel('Feature2', fontsize=12)
plt.grid(True)
# 标准化后的数据的散点图
plt.subplot(2, 2, 2)
sns.scatterplot(x='Feature1', y='Feature2', data=df_scaled, color='green', label='Standardized Data', alpha=0.6)
plt.title('Standardized Data (Scatter Plot)', fontsize=14)
plt.xlabel('Feature1 (Standardized)', fontsize=12)
plt.ylabel('Feature2 (Standardized)', fontsize=12)
plt.grid(True)
# 原始数据的直方图
plt.subplot(2, 2, 3)
df.plot(kind='hist', alpha=0.5, bins=30, ax=plt.gca(), color=['blue', 'orange', 'green'])
plt.title('Original Data (Histogram)', fontsize=14)
plt.xlabel('Value', fontsize=12)
plt.ylabel('Frequency', fontsize=12)
plt.grid(True)
# 对数变换后的数据的直方图
plt.subplot(2, 2, 4)
df_log_transformed.plot(kind='hist', alpha=0.5, bins=30, ax=plt.gca(), color=['blue', 'orange', 'green'])
plt.title('Log Transformed Data (Histogram)', fontsize=14)
plt.xlabel('Value (Log Transformed)', fontsize=12)
plt.ylabel('Frequency', fontsize=12)
plt.grid(True)
# 显示图像
plt.tight_layout()
plt.show()
虚拟数据集的生成:使用了三种不同的分布——正态分布、指数分布、均匀分布来生成特征数据。 数据变换:
标准化:使用 StandardScaler
将数据转换为均值为0,标准差为1的标准正态分布。归一化:使用 MinMaxScaler
将数据缩放到[0, 1]
之间。对数变换:对某些数据(如指数分布)进行对数变换,使得数据的分布更接近正态分布。
左上角图:原始数据的散点图。 右上角图:标准化后的数据散点图。 左下角图:原始数据的直方图。 右下角图:对数变换后的数据直方图。
其中:
散点图:展示了数据分布和相关性。标准化后,数据的范围被压缩到类似正态分布的形状。 直方图:展示了数据的分布形状。对数变换使数据更接近对称,去除了某些特征的偏态。
9. 平衡处理
类别不平衡指的是数据集中某些类别的样本数量远多于其他类别,导致模型偏向于多数类样本。常用处理方法包括欠采样、过采样和合成少数类样本。
常用方法:
1. 欠采样(Under-sampling):
从多数类样本中随机移除部分样本,使其数量与少数类平衡。缺点是可能丢失有用信息。
2. 过采样(Over-sampling):
通过重复少数类样本,使其数量增加。缺点是可能导致过拟合。
3. SMOTE(Synthetic Minority Over-sampling Technique):
基于少数类样本生成新的合成样本。通过在少数类样本之间插值,生成合成样本:
核心公式
SMOTE算法:给定少数类样本 和其邻近样本 ,生成新的合成样本 的公式为:
其中 是随机生成的在 [0, 1] 范围内的权重系数。
案例代码
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from imblearn.under_sampling import RandomUnderSampler
from imblearn.over_sampling import RandomOverSampler
# 生成虚拟不平衡数据集
X, y = make_classification(n_samples=1000, n_features=2, n_informative=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.9, 0.1], flip_y=0, random_state=42)
# 图形颜色和样式定义
colors = ['#FF6347', '#4682B4']
# 原始数据可视化
plt.figure(figsize=(15, 7))
plt.subplot(1, 3, 1)
plt.scatter(X[y == 0][:, 0], X[y == 0][:, 1], label='Class 0', color=colors[0], alpha=0.6)
plt.scatter(X[y == 1][:, 0], X[y == 1][:, 1], label='Class 1', color=colors[1], alpha=0.6)
plt.title("Original Imbalanced Data")
plt.legend()
plt.xlabel("Feature 1")
plt.ylabel("Feature 2")
# 欠采样处理
rus = RandomUnderSampler(random_state=42)
X_res, y_res = rus.fit_resample(X, y)
plt.subplot(1, 3, 2)
plt.scatter(X_res[y_res == 0][:, 0], X_res[y_res == 0][:, 1], label='Class 0', color=colors[0], alpha=0.6)
plt.scatter(X_res[y_res == 1][:, 0], X_res[y_res == 1][:, 1], label='Class 1', color=colors[1], alpha=0.6)
plt.title("After Undersampling")
plt.legend()
plt.xlabel("Feature 1")
plt.ylabel("Feature 2")
# 过采样处理
ros = RandomOverSampler(random_state=42)
X_res, y_res = ros.fit_resample(X, y)
plt.subplot(1, 3, 3)
plt.scatter(X_res[y_res == 0][:, 0], X_res[y_res == 0][:, 1], label='Class 0', color=colors[0], alpha=0.6)
plt.scatter(X_res[y_res == 1][:, 0], X_res[y_res == 1][:, 1], label='Class 1', color=colors[1], alpha=0.6)
plt.title("After Oversampling")
plt.legend()
plt.xlabel("Feature 1")
plt.ylabel("Feature 2")
# 展示所有图形
plt.tight_layout()
plt.show()
生成虚拟数据:我们使用
make_classification
函数生成一个不平衡的二分类数据集。类别0占总样本的90%,类别1仅占10%,形成了明显的类别不平衡。可视化原始数据:使用散点图展示原始数据,两个特征用作X和Y轴,不同类别用不同颜色表示。
欠采样处理:使用
RandomUnderSampler
方法对数据进行欠采样,减少多数类样本的数量,使其与少数类样本平衡。处理后再进行可视化。过采样处理:使用
RandomOverSampler
方法对数据进行过采样,增加少数类样本的数量,使其与多数类样本平衡。处理后同样可视化。
原始数据分布:展示了不平衡的类别0和类别1的数据分布,类别0(多数类)样本显著多于类别1(少数类)。 欠采样处理后:多数类样本被减少,使其数量与少数类接近。 过采样处理后:少数类样本增加,使其数量与多数类平衡。
每个图中的不同颜色代表不同的类(类0为红色,类1为蓝色),使得分类更加直观。
10. 时间序列数据处理
时间序列数据处理涉及到数据随时间变化的特性。时间序列数据预处理的重点包括平稳性检测、差分处理、趋势和季节性消除等。
常用方法:
1. 差分(Differencing):
差分是通过计算当前数据与前一个时间点数据的差值来消除趋势性。一次差分公式为:
2. 滚动窗口(Rolling Window):
在固定长度的窗口内计算统计量,如移动平均值:
3. 自回归模型(AR, Autoregressive Model):
自回归模型根据前 个时间点的值预测当前值:
核心公式
1. 差分公式:
差分通过当前时间点与前一时间点的差值消除趋势性:
2. 移动平均公式:
在固定长度的时间窗口内计算均值:
3. 自回归模型:
预测当前时间点 依赖于之前 个时间点的值:
其中 为误差项, 为自回归系数。
案例代码
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
# 生成虚拟时间序列数据
np.random.seed(42)
date_range_hourly = pd.date_range(start='2024-01-01', periods=200, freq='H')
date_range_daily = pd.date_range(start='2024-01-01', periods=200, freq='D')
# 生成两个不同的时间序列数据集
hourly_data = np.cumsum(np.random.randn(len(date_range_hourly))) + 50
daily_data = np.cumsum(np.random.randn(len(date_range_daily))) + 100
# 引入一些缺失值
hourly_data[30:40] = np.nan
daily_data[50:60] = np.nan
# 将时间序列数据放入DataFrame中
df_hourly = pd.DataFrame({'date': date_range_hourly, 'value': hourly_data})
df_daily = pd.DataFrame({'date': date_range_daily, 'value': daily_data})
# 填充缺失值,使用前向填充
df_hourly['value_filled'] = df_hourly['value'].fillna(method='ffill')
df_daily['value_filled'] = df_daily['value'].fillna(method='ffill')
# 计算滑动平均(窗口为24小时和7天)
df_hourly['rolling_mean'] = df_hourly['value_filled'].rolling(window=24).mean()
df_daily['rolling_mean'] = df_daily['value_filled'].rolling(window=7).mean()
# 计算差分(消除趋势)
df_hourly['differenced'] = df_hourly['value_filled'].diff()
df_daily['differenced'] = df_daily['value_filled'].diff()
# 设置图形的尺寸和风格
plt.figure(figsize=(14, 10))
# 颜色设置
colors = ['#FF6347', '#4682B4', '#32CD32', '#FFD700', '#9370DB', '#FF1493']
# 子图 1:小时数据的原始序列、滑动平均和差分
plt.subplot(2, 1, 1)
plt.plot(df_hourly['date'], df_hourly['value_filled'], label='Hourly Data', color=colors[0], linewidth=2)
plt.plot(df_hourly['date'], df_hourly['rolling_mean'], label='Hourly Rolling Mean', color=colors[1], linewidth=2)
plt.plot(df_hourly['date'], df_hourly['differenced'], label='Hourly Differenced', color=colors[2], linewidth=1.5)
plt.title('Hourly Time Series Data: Original, Rolling Mean, and Differenced', fontsize=14)
plt.ylabel('Hourly Value')
plt.legend(loc='upper left')
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d %H:%M'))
plt.xticks(rotation=45)
# 子图 2:日数据的原始序列、滑动平均和差分
plt.subplot(2, 1, 2)
plt.plot(df_daily['date'], df_daily['value_filled'], label='Daily Data', color=colors[3], linewidth=2)
plt.plot(df_daily['date'], df_daily['rolling_mean'], label='Daily Rolling Mean', color=colors[4], linewidth=2)
plt.plot(df_daily['date'], df_daily['differenced'], label='Daily Differenced', color=colors[5], linewidth=1.5)
plt.title('Daily Time Series Data: Original, Rolling Mean, and Differenced', fontsize=14)
plt.ylabel('Daily Value')
plt.legend(loc='upper left')
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
plt.xticks(rotation=45)
# 调整子图之间的间距
plt.tight_layout()
# 显示图形
plt.show()
数据生成:我们使用
pandas
生成两个时间序列,一个是按小时的(逐小时),另一个是按天的(逐日)。每个时间序列的数值是基于随机数的累积和(模拟一些趋势性)。缺失值处理:通过
fillna(method='ffill')
,我们使用前向填充法处理缺失值。滑动平均:对逐小时数据,使用24小时的窗口;对逐日数据,使用7天的窗口来计算滑动平均。
差分:使用
diff()
对数据进行差分,消除原始数据中的趋势,突出变化率。
第一个子图显示逐小时数据的原始数据、滑动平均和差分。 第二个子图显示逐日数据的原始数据、滑动平均和差分。
最后