哈喽,我是小白~
今天和大家聊聊数据变换的内容。
数据变换技术在机器学习实验中非常重要,它可以通过标准化、归一化或降维等方法改善模型性能,提高训练速度。合理的数据变换能帮助模型更快收敛,并减少对异常值的敏感性,从而提高模型的稳定性和准确性。
最终,这些技术可以更好地揭示数据中的潜在模式,使模型对新的数据更具泛化能力。
涉及到的一些技术有:
标准化 (Standardization) 归一化 (Normalization) 对数变换 (Log Transformation) 平方根变换 (Square Root Transformation) Box-Cox变换 (Box-Cox Transformation) 最大绝对值缩放 (MaxAbs Scaling) 小数缩放 (Decimal Scaling) 分位数变换 (Quantile Transformation) 主成分分析 (Principal Component Analysis, PCA) 独立成分分析 (Independent Component Analysis, ICA)
一起来把其中的原理和方法都讲清楚~
1. 标准化 (Standardization)
标准化主要用于消除特征量纲差异,将特征分布调整为均值为0、标准差为1的标准正态分布。
原理
通过对每个特征进行线性变换,使得特征数据中心化并进行缩放,以便特征值在相同尺度上进行比较,尤其适用于线性模型。
公式推导
均值:对于数据集中的一个特征向量 ,首先计算其均值 :
标准差:计算该特征的标准差 :
标准化:每个特征值 按以下公式转换为标准分布值 :
该操作使得数据的分布具有均值0和标准差1。
Python案例
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
# 生成虚拟数据集
np.random.seed(0)
data = {
'feature_1': np.random.normal(50, 10, 100), # 正态分布
'feature_2': np.random.normal(20, 5, 100), # 正态分布
'feature_3': np.random.uniform(30, 60, 100) # 均匀分布
}
# 创建DataFrame
df = pd.DataFrame(data)
# 标准化
scaler = StandardScaler()
df_standardized = pd.DataFrame(scaler.fit_transform(df), columns=df.columns)
plt.figure(figsize=(14, 6))
# 子图1: 标准化前的直方图
plt.subplot(2, 2, 1)
plt.hist(df['feature_1'], bins=15, alpha=0.7, color='dodgerblue', edgecolor='black')
plt.hist(df['feature_2'], bins=15, alpha=0.7, color='salmon', edgecolor='black')
plt.hist(df['feature_3'], bins=15, alpha=0.7, color='limegreen', edgecolor='black')
plt.title('Histogram before Standardization')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.legend(['feature_1', 'feature_2', 'feature_3'])
# 子图2: 标准化后的直方图
plt.subplot(2, 2, 2)
plt.hist(df_standardized['feature_1'], bins=15, alpha=0.7, color='dodgerblue', edgecolor='black')
plt.hist(df_standardized['feature_2'], bins=15, alpha=0.7, color='salmon', edgecolor='black')
plt.hist(df_standardized['feature_3'], bins=15, alpha=0.7, color='limegreen', edgecolor='black')
plt.title('Histogram after Standardization')
plt.xlabel('Standardized Value')
plt.ylabel('Frequency')
plt.legend(['feature_1', 'feature_2', 'feature_3'])
# 子图3: 标准化前的盒须图
plt.subplot(2, 2, 3)
plt.boxplot([df['feature_1'], df['feature_2'], df['feature_3']], patch_artist=True,
boxprops=dict(facecolor='skyblue'), medianprops=dict(color='red'))
plt.title('Box Plot before Standardization')
plt.xticks([1, 2, 3], ['feature_1', 'feature_2', 'feature_3'])
plt.ylabel('Value')
# 子图4: 标准化后的盒须图
plt.subplot(2, 2, 4)
plt.boxplot([df_standardized['feature_1'], df_standardized['feature_2'], df_standardized['feature_3']], patch_artist=True,
boxprops=dict(facecolor='skyblue'), medianprops=dict(color='red'))
plt.title('Box Plot after Standardization')
plt.xticks([1, 2, 3], ['feature_1', 'feature_2', 'feature_3'])
plt.ylabel('Standardized Value')
# 调整布局
plt.tight_layout()
plt.show()
数据生成:创建一个虚拟数据集,包括三个特征,分别为正态分布和均匀分布。 标准化:使用 StandardScaler
对数据进行标准化处理。
直方图:展示每个特征在标准化前后的分布。通过观察标准化后的直方图,能清晰看到数据均值归零且标准差为1的特性。 盒须图:用来显示数据的四分位间距和异常值。标准化后可以看到数据的变化,特别是数据的集中趋势和扩展范围均已调整。
2. 归一化 (Normalization)
归一化用于将数据缩放到固定范围(通常是 [0, 1]),特别适合分布范围较大的数据,有助于神经网络的快速收敛。
原理
通过将特征的最小值调整为0,最大值调整为1,使得特征值在同一比例下比较,防止特征值大的数据主导模型训练。
公式推导
最小值与最大值:找到数据特征的最小值 和最大值 。 归一化变换:使用以下公式对每个特征值进行缩放:
如果需要归一化到 [-1, 1] 范围,则用公式:
Python案例
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)
# 生成虚拟数据集
num_samples = 100
feature1 = np.random.normal(loc=100, scale=20, size=num_samples)
feature2 = np.random.normal(loc=50, scale=10, size=num_samples)
feature3 = np.random.normal(loc=30, scale=5, size=num_samples)
# 创建DataFrame
data = pd.DataFrame({
'Feature 1': feature1,
'Feature 2': feature2,
'Feature 3': feature3
})
# 归一化数据
scaler = MinMaxScaler()
data_normalized = pd.DataFrame(scaler.fit_transform(data), columns=data.columns)
# 设置绘图样式
sns.set(style="whitegrid")
# 创建图形
plt.figure(figsize=(16, 10))
# 散点图(归一化前和归一化后)
plt.subplot(3, 3, 1)
plt.scatter(data['Feature 1'], data['Feature 2'], color='royalblue', alpha=0.6, label='Before Normalization')
plt.scatter(data_normalized['Feature 1'], data_normalized['Feature 2'], color='coral', alpha=0.6, label='After Normalization')
plt.title('Scatter Plot Before and After Normalization')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.legend()
# 箱线图(归一化前和归一化后)
plt.subplot(3, 3, 2)
sns.boxplot(data=data)
plt.title('Box Plot Before Normalization')
plt.subplot(3, 3, 5)
sns.boxplot(data=data_normalized)
plt.title('Box Plot After Normalization')
# 直方图(归一化前和归一化后)
plt.subplot(3, 3, 3)
plt.hist(data['Feature 1'], bins=15, color='cyan', alpha=0.7, label='Feature 1')
plt.hist(data['Feature 2'], bins=15, color='magenta', alpha=0.7, label='Feature 2')
plt.hist(data['Feature 3'], bins=15, color='yellow', alpha=0.7, label='Feature 3')
plt.title('Histogram Before Normalization')
plt.legend()
plt.subplot(3, 3, 6)
plt.hist(data_normalized['Feature 1'], bins=15, color='cyan', alpha=0.7, label='Feature 1')
plt.hist(data_normalized['Feature 2'], bins=15, color='magenta', alpha=0.7, label='Feature 2')
plt.hist(data_normalized['Feature 3'], bins=15, color='yellow', alpha=0.7, label='Feature 3')
plt.title('Histogram After Normalization')
plt.legend()
# 调整布局
plt.tight_layout()
plt.show()
散点图(Scatter Plot):散点图可以有效地展示两个特征之间的关系。在归一化前,数据的分布可能会较为集中,显示出明显的聚集趋势。而归一化后,数据的范围被压缩到[0, 1]之间,整体的分布会显得更加均匀,方便观察数据之间的关系。
箱线图(Box Plot):箱线图用于展示数据的集中趋势和离散程度。归一化前的箱线图可能显示出异常值和数据的偏态分布。归一化后的箱线图能够清晰地展示每个特征的中位数、四分位数和异常值,有助于识别数据的分布情况和异常值。
直方图(Histogram):直方图展示数据的频率分布。在归一化前,特征的分布可能呈现出偏态或不同的范围,直方图的形状会有所不同。而归一化后的直方图能够清晰地比较特征之间的分布情况,使得分析数据的分布更加直观。
3. 对数变换 (Log Transformation)
对数变换用于减少数据的偏态,尤其是处理长尾分布数据时,将数据从指数分布拉平至近似线性分布。
原理
通过对数缩放,增大小值之间的差距,减小大值之间的差距,从而使得数据分布更均匀。
公式推导
其中 是特征值,通常在取对数时加1以避免对数的零值问题。若数据本身无负值,也可以采用 。
Python案例
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as stats
# 设置随机种子以确保结果可重复
np.random.seed(42)
# 生成右偏的伽马分布数据
data = np.random.gamma(shape=2, scale=2, size=1000)
# 对数变换数据
log_data = np.log(data)
# 创建子图
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# 设置颜色
colors = ['#FF6347', '#4682B4']
# 1. 原始数据的直方图
axes[0, 0].hist(data, bins=30, color=colors[0], alpha=0.7)
axes[0, 0].set_title('Original Data Histogram', fontsize=14)
axes[0, 0].set_xlabel('Value', fontsize=12)
axes[0, 0].set_ylabel('Frequency', fontsize=12)
# 2. 对数变换后数据的直方图
axes[0, 1].hist(log_data, bins=30, color=colors[1], alpha=0.7)
axes[0, 1].set_title('Log Transformed Data Histogram', fontsize=14)
axes[0, 1].set_xlabel('Log(Value)', fontsize=12)
axes[0, 1].set_ylabel('Frequency', fontsize=12)
# 3. 原始数据的QQ图
stats.probplot(data, dist="norm", plot=axes[1, 0])
axes[1, 0].get_lines()[1].set_color('black') # 设置拟合线的颜色
axes[1, 0].get_lines()[0].set_color(colors[0]) # 设置点的颜色
axes[1, 0].set_title('QQ Plot of Original Data', fontsize=14)
# 4. 对数变换后数据的QQ图
stats.probplot(log_data, dist="norm", plot=axes[1, 1])
axes[1, 1].get_lines()[1].set_color('black') # 设置拟合线的颜色
axes[1, 1].get_lines()[0].set_color(colors[1]) # 设置点的颜色
axes[1, 1].set_title('QQ Plot of Log Transformed Data', fontsize=14)
# 调整布局
plt.tight_layout()
plt.show()
原始数据的直方图:展示了伽马分布数据的强右偏分布,具有明显的长尾现象。 对数变换后的直方图:对数变换使得数据分布更加对称,尾部得到了压缩,使其分布更接近正态。
QQ图(原始数据):原始数据的QQ图显示了数据分布明显偏离正态分布,尤其在尾部有较大偏差。直观展示数据的分布情况,尤其是偏态分布和对数变换后的效果对比。
QQ图(对数变换数据):对数变换后的QQ图显示数据更加接近正态分布,大部分点接近拟合线。用于判断数据是否符合正态分布,是对数变换效果的良好指标。
4. 平方根变换 (Square Root Transformation)
平方根变换适用于减少数据偏态,常用于包含零值的数据,是对数变换的替代方法。
原理
通过取平方根操作,较大的值缩小得较多,从而减少大值与小值之间的差距,拉平数据分布。
公式推导
这里,平方根变换可以将极端值压缩,同时维持数据的整体趋势。
Python案例
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import scipy.stats as stats
# 设置随机种子
np.random.seed(42)
# 生成一个右偏态分布的数据集
data_size = 1000
data = np.random.exponential(scale=2.0, size=data_size)
# 应用平方根变换
transformed_data = np.sqrt(data)
# 创建数据框
df = pd.DataFrame({
'Original': data,
'Transformed': transformed_data
})
# 创建绘图区域
plt.figure(figsize=(12, 10))
# 原始数据直方图
plt.subplot(2, 2, 1)
sns.histplot(df['Original'], bins=30, kde=True, color='cyan', stat='density')
plt.title('Original Data Histogram')
plt.xlabel('Value')
plt.ylabel('Density')
# 变换后数据直方图
plt.subplot(2, 2, 2)
sns.histplot(df['Transformed'], bins=30, kde=True, color='magenta', stat='density')
plt.title('Square Root Transformed Data Histogram')
plt.xlabel('Value')
plt.ylabel('Density')
# Q-Q图
plt.subplot(2, 2, 3)
stats.probplot(df['Original'], dist="norm", plot=plt)
plt.title('Q-Q Plot of Original Data')
# 箱线图
plt.subplot(2, 2, 4)
sns.boxplot(data=df[['Original', 'Transformed']], palette='Set2')
plt.title('Boxplot of Original and Transformed Data')
plt.ylabel('Value')
# 调整布局
plt.tight_layout()
plt.show()
数据生成:使用 np.random.exponential
生成右偏态的虚拟数据集。平方根变换:对原始数据应用平方根变换。
使用 seaborn
和matplotlib
绘制直方图和箱线图。使用 scipy.stats
绘制 Q-Q 图。
5. Box-Cox变换 (Box-Cox Transformation)
Box-Cox变换是一种数据正态化技术,通过参数 的不同取值,使非正态分布数据近似正态分布。
原理
根据 的不同取值选择相应的变换公式,从而找到最适合的数据分布。
公式推导
其中, 必须为正值,因此在进行 Box-Cox 之前通常需要进行数据偏移。
Python案例
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats
# 生成一个虚拟数据集(右偏分布)
np.random.seed(42)
data = np.random.exponential(scale=2, size=1000)
# Box-Cox 变换
data_transformed, _ = stats.boxcox(data)
# 创建图形和子图
fig, axs = plt.subplots(2, 3, figsize=(18, 10))
# 1. 原始数据直方图
sns.histplot(data, kde=True, color='r', bins=30, edgecolor='black', ax=axs[0, 0])
axs[0, 0].set_title('Original Data Histogram')
# 2. Box-Cox 变换后的数据直方图
sns.histplot(data_transformed, kde=True, color='b', bins=30, edgecolor='black', ax=axs[0, 1])
axs[0, 1].set_title('Box-Cox Transformed Data Histogram')
# 3. 原始数据的 Q-Q 图
stats.probplot(data, dist="norm", plot=axs[0, 2])
axs[0, 2].set_title('Original Data Q-Q Plot')
# 4. Box-Cox 变换后的 Q-Q 图
stats.probplot(data_transformed, dist="norm", plot=axs[1, 0])
axs[1, 0].set_title('Box-Cox Transformed Data Q-Q Plot')
# 5. 原始和变换后的数据箱线图对比
sns.boxplot(data=[data, data_transformed], palette="Set2", ax=axs[1, 1])
axs[1, 1].set_xticks([0, 1])
axs[1, 1].set_xticklabels(['Original Data', 'Box-Cox Transformed Data'])
axs[1, 1].set_title('Original vs Box-Cox Transformed Data Boxplot')
# 隐藏最后一个子图的坐标轴
axs[1, 2].axis('off')
# 调整布局
plt.tight_layout()
plt.show()
原始数据直方图 vs Box-Cox 变换后的直方图:这两个图帮助我们理解原始数据的分布特性与经过 Box-Cox 变换后分布形态的变化。直方图用于展示数据集中于某些区间的情况,便于观察数据偏态(如右偏或左偏)。 原始数据 Q-Q 图 vs Box-Cox 变换后的 Q-Q 图:这两个 Q-Q 图将帮助判断数据的正态性。原始数据如果不服从正态分布,在 Q-Q 图上会出现明显偏差,而经过 Box-Cox 变换后,点会更接近正态分布的对角线。 原始数据与 Box-Cox 变换后的箱线图:通过箱线图,可以直接对比变换前后数据的集中趋势、四分位距、最大最小值和离群点情况。Box-Cox 变换能减少偏差和极端值的影响。
6. 最大绝对值缩放 (MaxAbs Scaling)
最大绝对值缩放用于将特征值缩放至 [-1, 1] 范围,适用于特征值已经中心化但需要进一步缩放的场景。
原理
通过特征值的最大绝对值进行缩放,不改变特征值的相对关系,使得数据保持稀疏性。
公式推导
其中, 是数据集中特征值的最大绝对值。
Python案例
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.preprocessing import MaxAbsScaler
# 生成虚拟数据集
np.random.seed(0)
data = np.random.randn(100, 3) * 100 # 生成100行3列的随机数据
columns = ['Feature1', 'Feature2', 'Feature3']
df = pd.DataFrame(data, columns=columns)
# 使用最大绝对值缩放
scaler = MaxAbsScaler()
scaled_data = scaler.fit_transform(df)
scaled_df = pd.DataFrame(scaled_data, columns=columns)
# 绘制图形
plt.figure(figsize=(16, 10))
# 散点图
plt.subplot(2, 2, 1)
plt.scatter(scaled_df['Feature1'], scaled_df['Feature2'], c='cyan', alpha=0.6)
plt.title('Scatter Plot: Feature1 vs Feature2')
plt.xlabel('Feature1 (Scaled)')
plt.ylabel('Feature2 (Scaled)')
plt.grid(True)
# 箱形图
plt.subplot(2, 2, 2)
plt.boxplot(scaled_df.values, labels=scaled_df.columns, patch_artist=True,
boxprops=dict(facecolor='orange', color='blue'),
medianprops=dict(color='red'))
plt.title('Box Plot of Scaled Features')
plt.ylabel('Values (Scaled)')
# 直方图
plt.subplot(2, 2, 3)
plt.hist(scaled_df['Feature1'], bins=15, color='magenta', alpha=0.7, label='Feature1', edgecolor='black')
plt.hist(scaled_df['Feature2'], bins=15, color='yellow', alpha=0.5, label='Feature2', edgecolor='black')
plt.hist(scaled_df['Feature3'], bins=15, color='green', alpha=0.3, label='Feature3', edgecolor='black')
plt.title('Histogram of Scaled Features')
plt.xlabel('Value (Scaled)')
plt.ylabel('Frequency')
plt.legend()
# 密度图
plt.subplot(2, 2, 4)
for col in scaled_df.columns:
scaled_df[col].plot(kind='kde', label=col, linewidth=2)
plt.title('Density Plot of Scaled Features')
plt.xlabel('Value (Scaled)')
plt.ylabel('Density')
plt.legend()
plt.tight_layout()
plt.show()
生成虚拟数据集:我们使用
numpy
生成100行、3列的随机数据,以模拟特征的分布。数据乘以100使得数据范围更广泛。最大绝对值缩放:使用
MaxAbsScaler
对数据进行缩放。这种方法会将每个特征的最大绝对值调整为1,同时保持特征之间的比例关系。
散点图:用于可视化 Feature1
和Feature2
之间的关系,能帮助我们识别潜在的相关性或聚类。箱形图:用于显示每个特征的分布,包括中位数、四分位数和异常值,这有助于判断特征的离散程度和偏态。 直方图:用于分析每个特征的频率分布,能够揭示数据的整体分布情况,包括对称性和峰态。 密度图:进一步展示每个特征的分布情况,与直方图互补,可以直观地看到数据的集中趋势和分散程度。
7. 小数缩放 (Decimal Scaling)
小数缩放用于通过调整数据到特定位数上,减少特征值的量纲差异。
原理
通过除以一个合适的十次方数,使得所有数据都缩放到一个较小的数值范围。
公式推导
其中 是数据中最大绝对值的整数位数,满足 。
Python案例
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
# 创建虚拟数据集
np.random.seed(42)
data_size = 100
feature1 = np.random.uniform(-5000, 5000, data_size)
feature2 = np.random.uniform(1000, 20000, data_size)
# 生成DataFrame
data = pd.DataFrame({
'feature1': feature1,
'feature2': feature2
})
# 定义小数缩放函数
def decimal_scaling(column):
max_abs_value = np.max(np.abs(column))
scaling_factor = np.ceil(np.log10(max_abs_value))
return column / (10**scaling_factor)
# 应用小数缩放
data_scaled = data.copy()
data_scaled['feature1'] = decimal_scaling(data['feature1'])
data_scaled['feature2'] = decimal_scaling(data['feature2'])
# 创建子图
fig, axs = plt.subplots(2, 3, figsize=(18, 10))
# 直方图 - 原始数据
axs[0, 0].hist(data['feature1'], bins=20, color='blue', alpha=0.7, label='Feature1 (Original)')
axs[0, 0].set_title('Histogram of Feature1 (Original)', fontsize=12)
axs[0, 0].set_xlabel('Feature1')
axs[0, 0].set_ylabel('Frequency')
axs[0, 1].hist(data['feature2'], bins=20, color='green', alpha=0.7, label='Feature2 (Original)')
axs[0, 1].set_title('Histogram of Feature2 (Original)', fontsize=12)
axs[0, 1].set_xlabel('Feature2')
axs[0, 1].set_ylabel('Frequency')
# 直方图 - 缩放后数据
axs[1, 0].hist(data_scaled['feature1'], bins=20, color='orange', alpha=0.7, label='Feature1 (Scaled)')
axs[1, 0].set_title('Histogram of Feature1 (Scaled)', fontsize=12)
axs[1, 0].set_xlabel('Feature1')
axs[1, 0].set_ylabel('Frequency')
axs[1, 1].hist(data_scaled['feature2'], bins=20, color='red', alpha=0.7, label='Feature2 (Scaled)')
axs[1, 1].set_title('Histogram of Feature2 (Scaled)', fontsize=12)
axs[1, 1].set_xlabel('Feature2')
axs[1, 1].set_ylabel('Frequency')
# 散点图 - 原始 vs 缩放后
axs[0, 2].scatter(data['feature1'], data['feature2'], color='purple', label='Original Data', alpha=0.6)
axs[0, 2].set_title('Scatter Plot (Original Data)', fontsize=12)
axs[0, 2].set_xlabel('Feature1')
axs[0, 2].set_ylabel('Feature2')
axs[1, 2].scatter(data_scaled['feature1'], data_scaled['feature2'], color='cyan', label='Scaled Data', alpha=0.6)
axs[1, 2].set_title('Scatter Plot (Scaled Data)', fontsize=12)
axs[1, 2].set_xlabel('Feature1')
axs[1, 2].set_ylabel('Feature2')
# 调整布局
plt.tight_layout()
plt.show()
数据生成:使用 np.random.uniform
生成范围较大的随机数据,模拟真实世界中的特征分布情况。小数缩放:通过计算最大绝对值并除以 10 的幂,缩放特征数据。
直方图:展示原始数据和经过小数缩放后数据的分布频率。 散点图:分别展示原始数据和缩放后的特征之间的关系。
8. 分位数变换 (Quantile Transformation)
分位数变换将数据重新映射到目标分布(如均匀或正态分布)上,常用于保持特征秩不变的场景。
原理
根据数据点的分位数重新分布数据,特别适合于减少极端值的影响并且使数据近似正态分布。
公式推导
首先计算数据点的分位数(百分位数) :
然后通过逆累积分布函数 将分位数映射到目标分布上。
Python案例
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import QuantileTransformer
# 设置随机种子以便结果可复现
np.random.seed(42)
# 生成虚拟数据集
n_samples = 1000
# 正态分布数据
data_normal = np.random.normal(loc=0, scale=1, size=n_samples)
# 指数分布数据
data_exponential = np.random.exponential(scale=1, size=n_samples)
# 将数据整合到 DataFrame
df = pd.DataFrame({
'Normal': data_normal,
'Exponential': data_exponential
})
# 创建 QuantileTransformer 实例
quantile_transformer = QuantileTransformer(output_distribution='normal')
# 对数据进行分位数变换
df_transformed = quantile_transformer.fit_transform(df)
# 将转化后的数据放入新的 DataFrame
df_transformed = pd.DataFrame(df_transformed, columns=['Normal_Transformed', 'Exponential_Transformed'])
# 绘制图形
plt.figure(figsize=(16, 10))
# 绘制变换前后的直方图
plt.subplot(2, 2, 1)
sns.histplot(df['Normal'], color='skyblue', bins=30, kde=True, label='Normal Distribution', stat='density')
sns.histplot(df_transformed['Normal_Transformed'], color='orange', bins=30, kde=True, label='Transformed Normal', stat='density')
plt.title('Histogram of Normal Distribution vs Transformed Normal')
plt.legend()
plt.subplot(2, 2, 2)
sns.histplot(df['Exponential'], color='lightgreen', bins=30, kde=True, label='Exponential Distribution', stat='density')
sns.histplot(df_transformed['Exponential_Transformed'], color='red', bins=30, kde=True, label='Transformed Exponential', stat='density')
plt.title('Histogram of Exponential Distribution vs Transformed Exponential')
plt.legend()
# 绘制变换前后的箱线图
plt.subplot(2, 2, 3)
sns.boxplot(data=df[['Normal', 'Exponential']], palette='pastel')
plt.title('Boxplot of Original Distributions')
plt.subplot(2, 2, 4)
sns.boxplot(data=df_transformed[['Normal_Transformed', 'Exponential_Transformed']], palette='pastel')
plt.title('Boxplot of Transformed Distributions')
# 调整图形布局
plt.tight_layout()
plt.show()
数据生成:使用 np.random.normal
生成正态分布数据。使用np.random.exponential
生成指数分布数据。将两个数据集整合到一个DataFrame
中。分位数变换:创建 QuantileTransformer
实例,并设置输出分布为正态分布。使用fit_transform
方法对数据进行变换,并将结果存入新的DataFrame
中。
直方图:直方图能够清晰展示数据的分布情况。通过比较变换前后的直方图,可以直观地看到分位数变换如何影响数据的形状,尤其是如何使非正态分布的数据趋向正态分布。
第一行左图展示原始正态分布数据与变换后的数据分布对比。 第一行右图展示原始指数分布数据与变换后的数据分布对比。
箱线图:箱线图用于显示数据的集中趋势和离散程度,包括中位数、四分位数及异常值。通过比较变换前后的箱线图,可以观察到数据的变化情况,例如是否有异常值的减少,以及中位数和四分位数的变化。
第二行左图展示原始数据的箱线图,体现数据的中位数、四分位数及异常值。 第二行右图展示变换后的数据的箱线图,展示数据在变换后趋向正态分布的特征。
9. 主成分分析 (PCA)
PCA用于降维,将高维数据投影到低维空间以减少数据维度和冗余,提取主要特征。
原理
通过计算协方差矩阵的特征值和特征向量,选择较大特征值对应的特征向量构成新的特征空间。
公式推导
计算数据的协方差矩阵 :
求解特征值与特征向量:
将原始数据投影到 个主成分上以形成低维数据:
Python案例
import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from mpl_toolkits.mplot3d import Axes3D
# 生成一个虚拟的三维数据集
np.random.seed(42)
n_samples = 500
# 三个特征分布不同
X = np.dot(np.random.rand(3, 3), np.random.randn(3, n_samples)).T
# 应用PCA将3维数据降维到2维
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X)
# 创建图形窗口
fig = plt.figure(figsize=(16, 8))
# 1. 原始数据的三维散点图
ax1 = fig.add_subplot(121, projection='3d')
ax1.scatter(X[:, 0], X[:, 1], X[:, 2], c=X[:, 2], cmap='viridis', s=50, alpha=0.7)
ax1.set_title('Original 3D Data', fontsize=14)
ax1.set_xlabel('X1')
ax1.set_ylabel('X2')
ax1.set_zlabel('X3')
# 2. PCA降维后的二维散点图
ax2 = fig.add_subplot(122)
scatter = ax2.scatter(X_pca[:, 0], X_pca[:, 1], c=X[:, 2], cmap='plasma', s=50, alpha=0.7)
ax2.set_title('2D Data After PCA', fontsize=14)
ax2.set_xlabel('Principal Component 1')
ax2.set_ylabel('Principal Component 2')
plt.colorbar(scatter, ax=ax2, label='Original X3 Value')
# 3. PCA解释方差比例图(Scree Plot)
plt.figure(figsize=(8, 4))
plt.bar(range(1, 3), pca.explained_variance_ratio_, color='red', alpha=0.7)
plt.title('Scree Plot: Explained Variance Ratio by Component', fontsize=14)
plt.xlabel('Principal Component')
plt.ylabel('Explained Variance Ratio')
plt.xticks([1, 2])
# 显示图形
plt.tight_layout()
plt.show()
生成虚拟数据集:我们使用随机数生成了一个三维数据集,并通过矩阵乘法使其特征具有一定的线性相关性,符合PCA的应用场景。 PCA降维:使用 sklearn.decomposition.PCA
对数据进行降维处理,并提取前两个主成分。
3D散点图:显示原始数据的三维分布情况,直观展示数据的复杂性。 2D PCA散点图:通过颜色展示原始第三维信息在降维后的保留情况,显示降维效果。
Scree Plot:展示各主成分解释的方差比例,用于评估降维的效果和主成分的贡献。
10. 独立成分分析 (Independent Component Analysis, ICA)
ICA 是一种将多变量数据分解为统计独立成分的技术。通常用于信号分离问题(如音频混合问题),其核心在于最大化信号的统计独立性。
原理
ICA 假设观察到的数据 是一些独立成分 的线性混合。通过学习一个解混矩阵 ,可以得到近似的独立成分 。与主成分分析 (PCA) 不同,ICA 注重于找到统计上独立的分量,而不仅仅是无关的正交分量。
公式推导
假设有 个观测信号 ,可以表示为独立信号 的线性组合,即:
其中, 是 的混合矩阵。ICA 的目标是找到一个分离矩阵 ,使得:
推导过程:
中心化:将观测数据 中心化,以确保每个特征的均值为0。设中心化后的数据为 。 白化:将数据白化,使得白化后的数据 的协方差矩阵为单位矩阵。这一步可以通过主成分分析实现。 非高斯化:ICA 利用非高斯性作为独立性度量,通过最大化非高斯性来获取统计独立的成分。常用的方法包括极大熵法和负熵法。 迭代求解:通过迭代优化算法(如 FastICA)来不断调整解混矩阵 ,直至非高斯性最大化。 **负熵 (Negentropy)**:
非高斯性可以通过负熵来度量,负熵 的定义如下:
其中 是信号 的熵, 是与 具有相同协方差矩阵的高斯信号。通过最大化负熵,可以最大化信号的非高斯性,进而找到统计独立的成分。
信号分离:ICA 被广泛用于语音信号的分离,如将混合的音频信号分解为独立音源。
图像处理:ICA 用于分离混合图像信号,并在生物医学信号处理(如脑电波信号分离)中有重要应用。
金融分析:ICA 可用于分析金融数据中的隐含因子,提取潜在的独立市场驱动因素。
通过 ICA,可以在存在高度相关性的复杂数据中发现真正独立的隐含成分,使得在特征提取、降维和信号分离中具有强大的应用价值。
Python案例
import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import FastICA
# 设置随机种子以确保结果可重复
np.random.seed(42)
# 生成两个独立的信号源
n_samples = 2000
time = np.linspace(0, 8, n_samples)
# 源信号1: 正弦波
s1 = np.sin(2 * time)
# 源信号2: 方波
s2 = np.sign(np.sin(3 * time))
# 将源信号放在矩阵 S 中
S = np.c_[s1, s2]
# 将源信号加上噪声
S += 0.2 * np.random.normal(size=S.shape)
# 标准化信号:减去均值,除以标准差
S /= S.std(axis=0)
# 创建一个随机的混合矩阵
A = np.array([[1, 1], [0.5, 2]]) # 混合矩阵
X = np.dot(S, A.T) # 混合信号
# 应用 ICA 来分离信号
ica = FastICA(n_components=2)
S_ = ica.fit_transform(X) # 重构的源信号
A_ = ica.mixing_ # 得到的混合矩阵
# 图形显示
plt.figure(figsize=(12, 8))
# 绘制原始的独立信号
plt.subplot(3, 1, 1)
plt.title("Original Independent Signals (Sources)")
colors = ['red', 'blue']
for i, sig in enumerate(S.T):
plt.plot(time, sig, color=colors[i], label=f"Source {i+1}")
plt.legend()
# 绘制混合信号
plt.subplot(3, 1, 2)
plt.title("Mixed Signals")
for i, sig in enumerate(X.T):
plt.plot(time, sig, color=colors[i], label=f"Mixed {i+1}")
plt.legend()
# 绘制分离后的信号
plt.subplot(3, 1, 3)
plt.title("Signals after ICA (Separated Signals)")
for i, sig in enumerate(S_.T):
plt.plot(time, sig, color=colors[i], label=f"ICA Separated {i+1}")
plt.legend()
plt.tight_layout()
plt.show()
原始独立信号(源信号):第一个子图展示了源信号,其中一个是正弦波,另一个是方波。这是我们用来生成混合信号的基础数据,表示我们想要从混合信号中恢复的内容。 混合信号:第二个子图展示了将源信号通过一个随机混合矩阵混合后的信号。这些信号模拟了真实世界中的观测数据,如语音信号被多种声音混合。 分离信号:第三个子图展示了 ICA 分离后的信号。可以看到,ICA 能够很好地将混合信号分离回接近原始独立信号的形式。这验证了 ICA 对于盲源分离问题的有效性。
最后