最强总结!十大数据变换技术 !!

文摘   2024-10-14 11:26   北京  

哈喽,我是小白~

今天和大家聊聊数据变换的内容。

数据变换技术在机器学习实验中非常重要,它可以通过标准化、归一化或降维等方法改善模型性能,提高训练速度。合理的数据变换能帮助模型更快收敛,并减少对异常值的敏感性,从而提高模型的稳定性和准确性。

最终,这些技术可以更好地揭示数据中的潜在模式,使模型对新的数据更具泛化能力。

涉及到的一些技术有:

  • 标准化 (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的标准正态分布。

原理

通过对每个特征进行线性变换,使得特征数据中心化并进行缩放,以便特征值在相同尺度上进行比较,尤其适用于线性模型。

公式推导

  1. 均值:对于数据集中的一个特征向量 ,首先计算其均值 
  1. 标准差:计算该特征的标准差 
  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(5010100),  # 正态分布
    'feature_2': np.random.normal(205100),   # 正态分布
    'feature_3': np.random.uniform(3060100)  # 均匀分布
}

# 创建DataFrame
df = pd.DataFrame(data)

# 标准化
scaler = StandardScaler()
df_standardized = pd.DataFrame(scaler.fit_transform(df), columns=df.columns)

plt.figure(figsize=(146))

# 子图1: 标准化前的直方图
plt.subplot(221)
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(222)
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(223)
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([123], ['feature_1''feature_2''feature_3'])
plt.ylabel('Value')

# 子图4: 标准化后的盒须图
plt.subplot(224)
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([123], ['feature_1''feature_2''feature_3'])
plt.ylabel('Standardized Value')

# 调整布局
plt.tight_layout()
plt.show()
  1. 数据生成:创建一个虚拟数据集,包括三个特征,分别为正态分布和均匀分布。
  2. 标准化:使用StandardScaler对数据进行标准化处理。

  • 直方图:展示每个特征在标准化前后的分布。通过观察标准化后的直方图,能清晰看到数据均值归零且标准差为1的特性。
  • 盒须图:用来显示数据的四分位间距和异常值。标准化后可以看到数据的变化,特别是数据的集中趋势和扩展范围均已调整。

2. 归一化 (Normalization)

归一化用于将数据缩放到固定范围(通常是 [0, 1]),特别适合分布范围较大的数据,有助于神经网络的快速收敛。

原理

通过将特征的最小值调整为0,最大值调整为1,使得特征值在同一比例下比较,防止特征值大的数据主导模型训练。

公式推导

  1. 最小值与最大值:找到数据特征的最小值  和最大值 
  2. 归一化变换:使用以下公式对每个特征值进行缩放:

如果需要归一化到 [-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=(1610))

# 散点图(归一化前和归一化后)
plt.subplot(331)
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(332)
sns.boxplot(data=data)
plt.title('Box Plot Before Normalization')

plt.subplot(335)
sns.boxplot(data=data_normalized)
plt.title('Box Plot After Normalization')

# 直方图(归一化前和归一化后)
plt.subplot(333)
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(336)
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()

  1. 散点图(Scatter Plot):散点图可以有效地展示两个特征之间的关系。在归一化前,数据的分布可能会较为集中,显示出明显的聚集趋势。而归一化后,数据的范围被压缩到[0, 1]之间,整体的分布会显得更加均匀,方便观察数据之间的关系。

  2. 箱线图(Box Plot):箱线图用于展示数据的集中趋势和离散程度。归一化前的箱线图可能显示出异常值和数据的偏态分布。归一化后的箱线图能够清晰地展示每个特征的中位数、四分位数和异常值,有助于识别数据的分布情况和异常值。

  3. 直方图(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(22, figsize=(1210))

# 设置颜色
colors = ['#FF6347''#4682B4']

# 1. 原始数据的直方图
axes[00].hist(data, bins=30, color=colors[0], alpha=0.7)
axes[00].set_title('Original Data Histogram', fontsize=14)
axes[00].set_xlabel('Value', fontsize=12)
axes[00].set_ylabel('Frequency', fontsize=12)

# 2. 对数变换后数据的直方图
axes[01].hist(log_data, bins=30, color=colors[1], alpha=0.7)
axes[01].set_title('Log Transformed Data Histogram', fontsize=14)
axes[01].set_xlabel('Log(Value)', fontsize=12)
axes[01].set_ylabel('Frequency', fontsize=12)

# 3. 原始数据的QQ图
stats.probplot(data, dist="norm", plot=axes[10])
axes[10].get_lines()[1].set_color('black')  # 设置拟合线的颜色
axes[10].get_lines()[0].set_color(colors[0])  # 设置点的颜色
axes[10].set_title('QQ Plot of Original Data', fontsize=14)

# 4. 对数变换后数据的QQ图
stats.probplot(log_data, dist="norm", plot=axes[11])
axes[11].get_lines()[1].set_color('black')  # 设置拟合线的颜色
axes[11].get_lines()[0].set_color(colors[1])  # 设置点的颜色
axes[11].set_title('QQ Plot of Log Transformed Data', fontsize=14)

# 调整布局
plt.tight_layout()
plt.show()
  1. 原始数据的直方图:展示了伽马分布数据的强右偏分布,具有明显的长尾现象。
  2. 对数变换后的直方图:对数变换使得数据分布更加对称,尾部得到了压缩,使其分布更接近正态。

  • 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=(1210))

# 原始数据直方图
plt.subplot(221)
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(222)
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(223)
stats.probplot(df['Original'], dist="norm", plot=plt)
plt.title('Q-Q Plot of Original Data')

# 箱线图
plt.subplot(224)
sns.boxplot(data=df[['Original''Transformed']], palette='Set2')
plt.title('Boxplot of Original and Transformed Data')
plt.ylabel('Value')

# 调整布局
plt.tight_layout()
plt.show()
  1. 数据生成:使用 np.random.exponential 生成右偏态的虚拟数据集。
  2. 平方根变换:对原始数据应用平方根变换。

  • 使用 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(23, figsize=(1810))

# 1. 原始数据直方图
sns.histplot(data, kde=True, color='r', bins=30, edgecolor='black', ax=axs[00])
axs[00].set_title('Original Data Histogram')

# 2. Box-Cox 变换后的数据直方图
sns.histplot(data_transformed, kde=True, color='b', bins=30, edgecolor='black', ax=axs[01])
axs[01].set_title('Box-Cox Transformed Data Histogram')

# 3. 原始数据的 Q-Q 图
stats.probplot(data, dist="norm", plot=axs[02])
axs[02].set_title('Original Data Q-Q Plot')

# 4. Box-Cox 变换后的 Q-Q 图
stats.probplot(data_transformed, dist="norm", plot=axs[10])
axs[10].set_title('Box-Cox Transformed Data Q-Q Plot')

# 5. 原始和变换后的数据箱线图对比
sns.boxplot(data=[data, data_transformed], palette="Set2", ax=axs[11])
axs[11].set_xticks([01])
axs[11].set_xticklabels(['Original Data''Box-Cox Transformed Data'])
axs[11].set_title('Original vs Box-Cox Transformed Data Boxplot')

# 隐藏最后一个子图的坐标轴
axs[12].axis('off')

# 调整布局
plt.tight_layout()
plt.show()

  1. 原始数据直方图 vs Box-Cox 变换后的直方图:这两个图帮助我们理解原始数据的分布特性与经过 Box-Cox 变换后分布形态的变化。直方图用于展示数据集中于某些区间的情况,便于观察数据偏态(如右偏或左偏)。
  2. 原始数据 Q-Q 图 vs Box-Cox 变换后的 Q-Q 图:这两个 Q-Q 图将帮助判断数据的正态性。原始数据如果不服从正态分布,在 Q-Q 图上会出现明显偏差,而经过 Box-Cox 变换后,点会更接近正态分布的对角线。
  3. 原始数据与 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(1003) * 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=(1610))

# 散点图
plt.subplot(221)
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(222)
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(223)
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(224)
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()
  1. 生成虚拟数据集:我们使用 numpy 生成100行、3列的随机数据,以模拟特征的分布。数据乘以100使得数据范围更广泛。

  2. 最大绝对值缩放:使用 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(-50005000, data_size)
feature2 = np.random.uniform(100020000, 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(23, figsize=(1810))

# 直方图 - 原始数据
axs[00].hist(data['feature1'], bins=20, color='blue', alpha=0.7, label='Feature1 (Original)')
axs[00].set_title('Histogram of Feature1 (Original)', fontsize=12)
axs[00].set_xlabel('Feature1')
axs[00].set_ylabel('Frequency')

axs[01].hist(data['feature2'], bins=20, color='green', alpha=0.7, label='Feature2 (Original)')
axs[01].set_title('Histogram of Feature2 (Original)', fontsize=12)
axs[01].set_xlabel('Feature2')
axs[01].set_ylabel('Frequency')

# 直方图 - 缩放后数据
axs[10].hist(data_scaled['feature1'], bins=20, color='orange', alpha=0.7, label='Feature1 (Scaled)')
axs[10].set_title('Histogram of Feature1 (Scaled)', fontsize=12)
axs[10].set_xlabel('Feature1')
axs[10].set_ylabel('Frequency')

axs[11].hist(data_scaled['feature2'], bins=20, color='red', alpha=0.7, label='Feature2 (Scaled)')
axs[11].set_title('Histogram of Feature2 (Scaled)', fontsize=12)
axs[11].set_xlabel('Feature2')
axs[11].set_ylabel('Frequency')

# 散点图 - 原始 vs 缩放后
axs[02].scatter(data['feature1'], data['feature2'], color='purple', label='Original Data', alpha=0.6)
axs[02].set_title('Scatter Plot (Original Data)', fontsize=12)
axs[02].set_xlabel('Feature1')
axs[02].set_ylabel('Feature2')

axs[12].scatter(data_scaled['feature1'], data_scaled['feature2'], color='cyan', label='Scaled Data', alpha=0.6)
axs[12].set_title('Scatter Plot (Scaled Data)', fontsize=12)
axs[12].set_xlabel('Feature1')
axs[12].set_ylabel('Feature2')

# 调整布局
plt.tight_layout()
plt.show()
  1. 数据生成:使用 np.random.uniform 生成范围较大的随机数据,模拟真实世界中的特征分布情况。
  2. 小数缩放:通过计算最大绝对值并除以 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=(1610))

# 绘制变换前后的直方图
plt.subplot(221)
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(222)
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(223)
sns.boxplot(data=df[['Normal''Exponential']], palette='pastel')
plt.title('Boxplot of Original Distributions')

plt.subplot(224)
sns.boxplot(data=df_transformed[['Normal_Transformed''Exponential_Transformed']], palette='pastel')
plt.title('Boxplot of Transformed Distributions')

# 调整图形布局
plt.tight_layout()
plt.show()
  1. 数据生成:使用np.random.normal生成正态分布数据。使用np.random.exponential生成指数分布数据。将两个数据集整合到一个DataFrame中。
  2. 分位数变换:创建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(33), 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=(168))

# 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=(84))
plt.bar(range(13), 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([12])

# 显示图形
plt.tight_layout()
plt.show()
  1. 生成虚拟数据集:我们使用随机数生成了一个三维数据集,并通过矩阵乘法使其特征具有一定的线性相关性,符合PCA的应用场景。
  2. PCA降维:使用sklearn.decomposition.PCA对数据进行降维处理,并提取前两个主成分。

  • 3D散点图:显示原始数据的三维分布情况,直观展示数据的复杂性。
  • 2D PCA散点图:通过颜色展示原始第三维信息在降维后的保留情况,显示降维效果。

  • Scree Plot:展示各主成分解释的方差比例,用于评估降维的效果和主成分的贡献。

10. 独立成分分析 (Independent Component Analysis, ICA)

ICA 是一种将多变量数据分解为统计独立成分的技术。通常用于信号分离问题(如音频混合问题),其核心在于最大化信号的统计独立性。

原理

ICA 假设观察到的数据  是一些独立成分  的线性混合。通过学习一个解混矩阵 ,可以得到近似的独立成分 。与主成分分析 (PCA) 不同,ICA 注重于找到统计上独立的分量,而不仅仅是无关的正交分量。

公式推导

假设有  个观测信号 ,可以表示为独立信号  的线性组合,即:

其中, 是  的混合矩阵。ICA 的目标是找到一个分离矩阵 ,使得:

推导过程

  1. 中心化:将观测数据  中心化,以确保每个特征的均值为0。设中心化后的数据为 
  2. 白化:将数据白化,使得白化后的数据  的协方差矩阵为单位矩阵。这一步可以通过主成分分析实现。
  3. 非高斯化:ICA 利用非高斯性作为独立性度量,通过最大化非高斯性来获取统计独立的成分。常用的方法包括极大熵法和负熵法。
  4. 迭代求解:通过迭代优化算法(如 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(08, 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([[11], [0.52]])  # 混合矩阵
X = np.dot(S, A.T)  # 混合信号

# 应用 ICA 来分离信号
ica = FastICA(n_components=2)
S_ = ica.fit_transform(X)  # 重构的源信号
A_ = ica.mixing_  # 得到的混合矩阵

# 图形显示
plt.figure(figsize=(128))

# 绘制原始的独立信号
plt.subplot(311)
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(312)
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(313)
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()

  1. 原始独立信号(源信号):第一个子图展示了源信号,其中一个是正弦波,另一个是方波。这是我们用来生成混合信号的基础数据,表示我们想要从混合信号中恢复的内容。
  2. 混合信号:第二个子图展示了将源信号通过一个随机混合矩阵混合后的信号。这些信号模拟了真实世界中的观测数据,如语音信号被多种声音混合。
  3. 分离信号:第三个子图展示了 ICA 分离后的信号。可以看到,ICA 能够很好地将混合信号分离回接近原始独立信号的形式。这验证了 ICA 对于盲源分离问题的有效性。

最后

以上就是今天所有的内容了。
如果对你来说比较有用,记得点赞、收藏,慢慢学习~
下期会有更多干货等着你!~


Python和机器学习初学者
Python和机器学习分享,只写干货,一起学习~
 最新文章