数学建模——数据预处理

一、为什么要进行数据预处理前的分析

在获得数据以后,需要将数据进行分类,以便进行后续数据处理以及建模步骤,譬如,当数据中出现0/1表示否/是,成本行指标,效益型指标等等类型时,需要将其进行分割,不同数据类型使用不同模型或归一化至同一类型。其次,数据可视化分析能够更直观看出异常值,缺失值,数据走势,甚至有时可以看出数据是否为噪声数据进而进行去除噪声,看出是否为时间序列数据进而使用正确的模型。总而言之,数据预处理前的分析步骤能够大大提高后续的效率。

二、为什么要数据预处理

  1. 缺失值处理:实际数据往往会存在缺失值,如果不处理这些缺失值,可能导致分析结果不准确或模型训练失败。因此,需要采取适当的方法来处理缺失值,比如删除缺失值、插补缺失值等。

  2. 异常值处理:异常值可能会对数据分析和建模产生负面影响,因为它们可能会干扰统计模型的准确性和稳定性。因此,需要识别并处理异常值,以确保分析结果的可靠性。

  3. 数据标准化/归一化:如果数据的不同变量具有不同的尺度或单位,可能会导致分析结果偏向于某些变量,从而影响分析的准确性,标准化/归一化即是去量纲化的过程。通过数据标准化或归一化,可以消除这些尺度效应,确保各个变量对分析结果的影响相对均衡。

  4. 去除重复数据:数据集中可能存在重复的观测值,这些重复数据可能会导致分析结果出现偏差。因此,需要去除重复数据,以避免对分析结果的影响。

  5. 数据转换:在某些情况下,需要对数据进行转换,以满足分析或建模的要求。例如,对数转换、幂次转换等可以使数据更符合正态分布假设,从而提高分析的效果。

  6. 数据降维:通过保留数据集的关键信息,将高维数据映射到低维空间的过程。在实际应用中,数据通常具有很高的维度,但其中的信息可能存在冗余或噪声。

  7. 提高模型性能:经过数据预处理后的数据更加干净、规范,可以帮助建立更准确、稳健的模型,提高模型的性能和预测能力。

三、数据分析

1、散点图判断数据走势

以2024年美赛C题为例:

通过对数据进行散点图的绘画,很容易发现其中存在异常值,并且异常值出现的范围的大概位置,也能通过观察该数据分布的密集程度,判断出其中可能存在缺失值(需要进一步分析),并且该数据峰值集中在2022-02,不具有时间性。

2、箱型图观察离群数据,info判断空值情况

可以观察到存在不少离群点需要处理,而info结果为不存在空值

四、数据预处理

1、异常值处理

通常情况下会选择将异常值先变为空值,后使用填补空值的方法进行填补

1)箱线图

优点:

  1. 直观性: 箱型图提供了直观的数据分布可视化,可以快速识别出数据的中位数、四分位数、异常值等重要统计量,使得异常值的检测更加直观。

  2. 有效性: 箱型图基于数据的分位数来识别异常值,而不受整体数据分布的影响,因此在一定程度上能够有效地检测出异常值。

  3. 鲁棒性: 箱型图对数据的分布情况不敏感,不受极端值的影响,具有一定的鲁棒性。这意味着即使数据存在一些极端值,箱型图仍能够反映出数据的整体分布情况。

缺点:

  1. 参数选择: 箱型图中关于异常值的定义是基于箱体长度的一个固定比例(通常为1.5倍的四分位距),这个参数的选择可能对结果产生影响,而且这个选择是相对任意的。

  2. 信息丢失: 箱型图虽然提供了数据的重要统计量,但它也丢失了一些数据分布的细节信息,比如分布的形状、峰度等,因此可能无法完全捕捉到数据的复杂性。

  3. 不适用于非对称分布: 箱型图假设数据服从对称分布,对于非对称分布的数据可能无法准确反映异常值的情况。

  4. 局限性: 箱型图只能用于单变量数据的异常值检测,对于多变量数据的异常值检测并不适用。

s = df.describe()

# 计算分位差
q1 = s.loc['25%', 'value']
q3 = s.loc['75%',"value"]
iqr = q3 - q1
mi = q1 - 1.5*iqr
ma = q3 + 1.5*iqr
print('分位差为:%.3f,下限为:%.3f,上限为:%.3f' % (iqr,mi,ma))

# 筛选出异常值error、剔除异常值之后的数据data_c
ls1 = []
ax2=fig.add_subplot(2,1,2)
error = df["value"][(df["value"] < mi) | (df["value"] > ma)]
ls1.append(error)
df['value'] = df['value'].apply(lambda x: None if np.isin(x, ls) else x)
num_missing_2 = df['value'].isnull().sum()
print('异常值共%i条' % len(error))
print(df.info())
print(num_missing_2)
2)3σ原则

如果数据服从正态分布,异常值被定义为一组测定值中与平均值的偏差超过3倍的值 → p(|x - μ| > 3σ) ≤ 0.003

优点:

  1. 基于统计学原理:该方法基于数据的标准差,有一定的理论基础。
  2. 广泛适用:适用于符合正态分布或近似正态分布的数据。

缺点:

  1. 对非正态分布的数据效果较差:如果数据不符合正态分布,使用3σ原则可能会导致误判。
  2. 忽略了数据的分布特征:3σ原则仅基于数据的均值和标准差,忽略了数据的分布特征,可能无法发现一些真实存在的异常情况。
  3. 对极端值不敏感:3σ原则仅考虑了数据中大部分值的分布情况,对于极端值不够敏感,容易将一些真实的异常值排除在外。
u = df["value"].mean()  # 计算均值
std = df["value"].std()  # 计算标准差
print('均值为:%.3f,标准差为:%.3f' % (u,std))

#正态性检验
fig=plt.figure(figsize=(10,6))
ax1=fig.add_subplot(2,1,1)

# 绘制数据密度曲线
df["value"].plot(kind = 'kde',grid = True,style = '-k',title = '密度曲线')

# 筛选出异常值error、剔除异常值之后的数据data_c
ls = []
ax2=fig.add_subplot(2,1,2)
error=df["value"][np.abs(data-u)>3*std]
ls.append(error)
# data_c=data[np.abs(data-u)<=3*std]

df['value'] = df['value'].apply(lambda x: None if np.isin(x, ls) else x)
num_missing = df['value'].isnull().sum()

print("异常值共%d条"%(len(error)))
print(df.info())
print(num_missing)

# 图表表达
plt.scatter(df["value"].index,df["value"],color = 'k',marker='.',alpha = 0.3)
plt.scatter(error.index,error,color = 'r',marker='.',alpha = 0.5)
plt.xlim([-10,10010])
plt.grid()
plt.show()
3)LOF(局部离群因子)

LOF(局部离群因子)是一种用于检测数据集中异常值的算法。它通过比较每个数据点与其邻近数据点之间的密度来识别异常值。其基本思想是,对于每个数据点,首先计算其与其邻近点的密度,然后将该点的密度与其邻近点的密度进行比较,得出一个局部离群因子(LOF)值。LOF值越大,则表示该点相比于其邻近点来说更有可能是异常点。

优点:

  1. 能够发现各种形状和大小的异常值,不依赖于数据分布的假设。
  2. 能够捕捉到局部异常,而不仅仅是全局异常。
  3. 相对于基于距离的方法,对于高维数据的处理能力更强。

缺点:

  1. 对于大数据集和高维数据的计算开销较大。
  2. 对于噪声数据和密度差异较大的数据集,可能会产生不准确的结果。
  3. 需要手动选择邻近点的数量和其他参数,对参数的选择敏感。
import pandas as pd
from sklearn.neighbors import LocalOutlierFactor

X = df[['feature1', 'feature2']].values

# 创建一个LocalOutlierFactor对象,并指定参数
lof = LocalOutlierFactor(n_neighbors=10, contamination=0.1)

# 训练模型并预测异常值
y_pred = lof.fit_predict(X)

# 将预测结果添加到原始DataFrame中
df['LOF_pred'] = y_pred

# 将预测为异常值的数据点设置为缺失值
df.loc[y_pred == -1, ['feature1', 'feature2']] = np.nan

LocalOutlierFactor中的超参数设置:

  1. n_neighbors: 指定用于计算局部密度的邻近点的数量。较大的邻近点数量可以提高模型的稳定性,但也可能导致对于真实异常点的误分类增加。通常建议设置为10或更大的值。

  2. contamination: 指定要从数据中识别为异常点的比例。这是一个重要的超参数,因为它决定了 LOF 算法如何界定何为异常值。较高的污染率意味着更多的点将被标记为异常值,而较低的污染率则意味着更少的点将被标记为异常值。通常情况下,需要根据数据的特性进行调整。

  3. metric: 指定用于计算距离的度量方法。常用的距离度量方法包括 'euclidean'(欧氏距离), 'manhattan'(曼哈顿距离), 'minkowski'(闵可夫斯基距离)等。

2、缺失值处理

1)缺失值的查看

#查看缺失值
print(s.isnull())
print(df.isnull().sum()) #对每个键中的空值进行计数

# 缺失值可视化
missingno.matrix(df)
missingno.bar(df,color="blue") #以柱状图形式生成
plt.show()

# 密度图查看缺失值情况
data_c = data.fillna(data.median())  #  中位数填充缺失值
fig,axes = plt.subplots(1,4,figsize = (20,5))
data.plot.box(ax = axes[0],grid = True,title = '数据分布')
data.plot(kind = 'kde',style = '--r',ax = axes[1],grid = True,title = '删除缺失值',xlim = [-50,150])
data_c.plot(kind = 'kde',style = '--b',ax = axes[2],grid = True,title = '缺失值填充中位数',xlim = [-50,150])
plt.show()

2)删除缺失值

即删除缺失值所在的一整行数据

好处:

  1. 数据清洗:删除缺失值可以使数据更加干净,减少对数据分析和建模的影响。缺失值可能导致对数据的误解或错误的结论,因此清除缺失值有助于提高数据的质量和准确性。

  2. 简化分析:删除缺失值可以简化数据分析过程,减少处理数据的复杂性。在一些情况下,如果缺失值的数量相对较少,删除它们可能是最简单、最有效的方法,而不会对结果产生太大的影响。

  3. 提高模型性能:在建立机器学习模型时,删除缺失值可以提高模型的性能和准确性。因为缺失值会影响模型的训练过程,可能导致模型的泛化能力下降,而删除缺失值可以避免这种情况的发生。

坏处:

  1. 信息丢失:删除缺失值可能会导致有用的信息丢失。有时候,缺失值的存在本身也可能是数据的一部分,删除缺失值可能会丢失这些信息,从而影响对数据的完整理解。

  2. 可能引入偏差:如果缺失值的分布与数据的其他特征相关,那么删除缺失值可能会引入偏差,导致对数据的错误理解或分析结果的失真。在这种情况下,简单地删除缺失值可能会导致对数据的错误处理。

  3. 数据量减少:删除缺失值可能会导致数据量减少,从而减少了可用于建模和分析的样本数量。特别是在数据量本来就不足的情况下,删除缺失值可能会限制模型的训练和分析的可靠性。

s.dropna(inplace=True)
df1=df[['value1','value2']].dropna()
print(df1.isnull().sum())

2)用值(0,该数据前后的数据)填补缺失值

好处:

  1. 保持数据结构:可以保持数据的结构完整性,不会改变数据的总体分布。这对于一些需要保持数据完整性的情况下是非常有用的,尤其是在时间序列数据等领域。

  2. 方便处理:在一些算法中,需要对缺失值进行特殊处理,而用临近数填补缺失值可以简化这个过程,使得处理更加简便。

坏处:

  1. 可能影响模型性能:在一些情况下,用填补缺失值可能会影响模型的性能和准确性。特别是对于一些机器学习模型,可能会影响模型的训练过程,导致模型的泛化能力下降。

  2. 信息丢失:用临近数填补缺失值可能会导致有用的信息丢失。如果缺失值本身包含了有用的信息,则可能会丢失这些信息,影响对数据的完整理解。

#用0来填补缺失值
s.fillna(0,inplace=True)

#用缺失值之前/之后的数填充
df['value1'].fillna(method='pad',inplace=True)
# method参数:
# pad / ffill → 用之前的数据填充
# backfill / bfill → 用之后的数据填充
# print(df['value1'])

#替换缺失值
df['value2'].replace([1,2,3],np.nan,inplace=True)

3)均值/中位数/众数插补(常见)

先计算均值,中位数,众数

u = df["value1"].mean()# 均值
me = df["value2"].median()  # 中位数
mod = df["value3"].mode()   # 众数
均值插补:

好处:

  1. 保持数据分布: 以保持数据的总体分布,不会引入新的值,因此对数据的整体影响较小。
  2. 简单快捷: 计算均值相对简单,可以迅速地填补缺失值,适用于大规模数据集。
  3. 适用性广泛:适用于数值型数据,无需考虑数据的特征或分布情况,因此适用性较广。

坏处:

  1. 不考虑数据分布:忽略了数据的分布特征,可能会在某些情况下引入偏差,特别是在数据存在异常值或偏斜分布时。
  2. 可能影响模型准确性: 在某些情况下,用均值填充可能会影响模型的准确性,特别是对于对异常值敏感的模型而言。
df["value1"].fillna(u,inplace = True)
中位数插补:

好处:

  1. 对异常值不敏感: 中位数对异常值不敏感,因此适用于存在异常值的数据集。
  2. 保持数据分布的中心趋势: 中位数插补可以保持数据的中心趋势,对整体数据的影响较小。
  3. 适用性广泛: 与均值相比,中位数插补同样简单快捷,且适用于各种数值型数据。

坏处:

  1. 不考虑数据分布的形状: 同样忽略了数据的分布形状,可能会在某些情况下引入偏差。
  2. 可能导致信息丢失: 使用中位数插补可能会导致一部分数据的信息丢失,特别是在数据存在较多不同分布的情况下。
df["value2"].fillna(me,inplace = True)
众数插补:

好处:

  1. 保留数据的模式: 使用众数进行插补可以保留数据的模式,适用于类别型数据或呈现出明显集中趋势的数值型数据。
  2. 简单易行: 计算众数简单,且对数据的整体影响较小。

坏处:

  1. 可能引入偏差: 众数插补忽略了数据的分布情况,可能会在某些情况下引入偏差,尤其是在数据存在多个峰值或分布不均匀的情况下。
  2. 不适用于连续型数据: 众数插补适用于类别型数据,不适用于连续型数据。对于连续型数据,使用众数进行插补可能会导致信息丢失和模型准确性降低。
df["value3"].fillna(mod,inplace = True)

4)滑动窗口插值(适用于时间序列数据)

好处:

  1. 保留数据趋势: 能够利用相邻时间点的数据来填补缺失值,有助于保留数据的趋势和模式,使得填补后的数据更加接近真实情况。

  2. 简单易行: 不需要复杂的数学模型或特殊的数据预处理,适用于一般的时间序列数据处理场景。

  3. 灵活性: 滑动窗口法可以根据需要设置不同的窗口大小和填补策略,使得方法具有一定的灵活性,适用于不同的数据情况和需求。

坏处:

  1. 可能引入噪声: 在填补缺失值时,滑动窗口法使用相邻时间点的数据进行估计,如果数据中存在较大的波动或噪声,可能会导致填补值的不准确性,进而影响后续的分析和建模结果。

  2. 局限性: 滑动窗口法主要适用于连续的时间序列数据,对于非连续的数据或特定的数据分布形式可能效果不佳。

  3. 对窗口大小敏感: 窗口大小的选择对填补效果有较大影响,窗口太小可能无法捕捉数据的整体趋势,窗口太大可能使得填补结果过于平滑,不足以反映数据的真实情况。

  4. 不适用于大缺失值: 当数据缺失值的数量较多时,滑动窗口法可能无法有效填补缺失值,尤其是当缺失值的连续性较强时,可能会导致填补结果不准确

import pandas as pd
import numpy as np


data = {
    'Date': pd.date_range(start='2022-01-01', end='2022-01-10'),
    'Value': [10, np.nan, 20, 25, np.nan, 30, 35, np.nan, 40, 45]
}
df = pd.DataFrame(data)

# 定义滑动窗口大小
window_size = 5

for i in range(len(df)):
    if pd.isnull(df.loc[i, 'Value']):
        start_index = max(0, i - window_size // 2)
        end_index = min(len(df), i + window_size // 2)
        window_values = df.loc[start_index:end_index, 'Value']
        window_values = window_values.dropna()  # 去除窗口中的缺失值
        if len(window_values) > 0:
            df.loc[i, 'Value'] = window_values.mean()  # 填补缺失值为窗口内非缺失值的均值

print(df)

5)插值法插值

  • 拉格朗日插值法: 相对于牛顿插值法来说,拉格朗日插值法构建的插值多项式形式更为简洁,但计算复杂度较高,尤其在高次多项式时。

  • 牛顿插值法: 相对于拉格朗日插值法来说,牛顿插值法的计算复杂度较低,适合于大规模数据集的插值计算,但构建的插值多项式形式较为复杂。

拉格朗日插值法(很常用)

好处:

  1. 精度较高: 拉格朗日插值法可以通过多项式函数对数据进行拟合,因此在一定范围内可以提供较高的插值精度,尤其是在数据分布较为连续、平滑的情况下效果更好。

  2. 适用性广泛: 拉格朗日插值法适用于各种类型的数据,包括数值型数据、时间序列数据以及非线性数据,因此具有较广泛的应用范围。

  3. 简单易行: 拉格朗日插值法相对简单易行,只需要根据已知数据点构建相应的拉格朗日多项式,然后利用多项式函数计算缺失值即可。

坏处:

  1. 过拟合问题: 拉格朗日插值法容易受到过拟合的影响,特别是在数据点较少或分布不均匀的情况下,构建的多项式函数可能会过度拟合已知数据点,导致插值结果不稳定。

  2. 对数据分布敏感: 拉格朗日插值法对数据分布的敏感性较强,如果数据分布不均匀或存在较大的噪声,可能会导致插值结果不准确,甚至出现震荡现象。

  3. 计算复杂度高: 随着数据点的增加,构建拉格朗日多项式的计算复杂度会逐渐增加,可能会导致计算时间较长,尤其是对于大规模数据集而言。

  4. 局部性限制: 拉格朗日插值法是一种局部插值方法,其插值结果仅依赖于最近的已知数据点,因此可能无法充分利用数据集中的全局信息,导致插值结果局限于局部区域。

from scipy.interpolate import lagrange
data = pd.Series(np.random.rand(100)*100)
data = pd.read_excel("r")
data[3,6,33,56,45,66,67,80,90] = np.nan

# 创建函数,做插值,由于数据量原因,以空值前后5个数据(共10个数据)为例做插值
def na_c(s,n,k=5):
    y = s[list(range(n-k,n+1+k))] # 取数
    y = y[y.notnull()]  # 剔除空值
    return(lagrange(y.index,list(y))(n))


# 缺失值插值
na_re = []
for i in range(len(data)):
    if data.isnull()[i]:
        data[i] = na_c(data,i)
        print(na_c(data,i))
        na_re.append(data[i])
data.dropna(inplace=True)  # 清除插值后仍存在的缺失值
data.plot(kind = 'kde',style = '--k',ax = axes[3],grid = True,title = '拉格朗日插值后',xlim = [-50,150])
牛顿插值法
import numpy as np
import pandas as pd

#计算差商
def divided_difference(x, y):
    n = len(x)
    F = np.zeros((n, n))
    F[:, 0] = y
    for j in range(1, n):
        for i in range(n - j):
            F[i, j] = (F[i + 1, j - 1] - F[i, j - 1]) / (x[i + j] - x[i])
    return F[0]

#牛顿插值
def newton_interpolation(x, y, xi):
    n = len(x)
    F = divided_difference(x, y)
    result = F[0]
    for i in range(1, n):
        term = F[i]
        for j in range(i):
            term *= (xi - x[j])
        result += term
    return result

#使用牛顿插值法填补DataFrame中的缺失值
def fill_missing_values(df, column_name):
    x = df.index.values
    y = df[column_name].values
    missing_indices = df[df[column_name].isnull()].index
    for index in missing_indices:
        df.at[index, column_name] = newton_interpolation(x, y, index)
    return df

data = {
    'x': [0, 1, 2, 3, 4],
    'y': [0, 1, np.nan, 9, 16]
}
df = pd.DataFrame(data)


filled_df = fill_missing_values(df, 'y')

6)随机抽样法

适用于数据本身具备随机性特征的数据

import numpy as np
import pandas as pd

#使用随机抽样法填补DataFrame中的缺失值
def fill_missing_values_random(df, column_name):
    missing_indices = df[df[column_name].isnull()].index
    non_missing_values = df[column_name].dropna()
    for index in missing_indices:
        df.at[index, column_name] = np.random.choice(non_missing_values)
    return df

data = {
    'x': [0, 1, 2, 3, 4],
    'y': [0, 1, np.nan, 9, 16]
}
df = pd.DataFrame(data)

filled_df = fill_missing_values_random(df, 'y')

3、调整数据尺度

无论0-1化,-1-1化,z-score化,还是robust scaling都具备的特征:

好处:

  1. 保留数据分布信息

  2. 适用性广泛: 适用于各种类型的数据,包括数值型数据、类别型数据以及文本型数据

  3. 减少极端值的影响:提高模型对极端值的稳健性。

坏处:

  1. 不适用于稀疏数据

  2. 容易造成信息丢失

1)0-1标准化

特点:将数据按照最小值和最大值进行线性变换,射到[0, 1],但对异常值敏感,且受极端值影响,不适用于非线性关系


import pandas as pd

df = pd.DataFrame({"value1":np.random.rand(10)*20,
                  'value2':np.random.rand(10)*100})

#创建函数,标准化数据

def data_norm(df,*cols):
    df_n = df.copy()
    for col in cols:
        ma = df_n[col].max()
        mi = df_n[col].min()
        df_n[col + '_n'] = (df_n[col] - mi) / (ma - mi)
    return(df_n)


df_n = data_norm(df,'value1','value2')

2)z-score标准化

特点:减少极端值的影响,但对异常值敏感,不适用于非正态数据

它将数据按照均值和标准差进行线性变换,使得数据的均值为0,标准差为1,符合标准正态分布。在分类、聚类算法中,需要使用距离来度量相似性的时候,Z-score表现更好。

import pandas as pd
from  sklearn import preprocessing
df = pd.DataFrame({"value1":np.random.rand(10) * 100,
                  'value2':np.random.rand(10) * 100})

# 创建函数,标准化数据
def data_Znorm(df, *cols):
    df_n = df.copy()
    for col in cols:
        u = df_n[col].mean()
        std = df_n[col].std()
        df_n[col + '_Zn'] = (df_n[col] - u) / std
    return(df_n)


#或者直接用scale函数
dd=preprocessing.scale(df)
print(dd)

df_z = data_Znorm(df,'value1','value2')
u_z = df_z['value1_Zn'].mean()
std_z = df_z['value1_Zn'].std()
print(df_z)
print('标准化后value1的均值为:%.2f, 标准差为:%.2f' % (u_z, std_z))

3)-1-1标准化

特点:将数据映射到[-1, 1],减少极端值影响,但对异常值敏感,不适用于非线性关系

import numpy as np
import pandas as pd

#使用-1到1标准化方法对DataFrame中的数据进行标准化
def min_max_scaling(df):
    scaled_df = df.copy()
    for column in df.columns:
        min_val = df[column].min()
        max_val = df[column].max()
        scaled_df[column] = (df[column] - min_val) / (max_val - min_val) * 2 - 1
    return scaled_df

data = {
    'x': [0, 1, 2, 3, 4],
    'y': [0, 1, 4, 9, 16]
}
df = pd.DataFrame(data)

scaled_df = min_max_scaling(df)

4)Robust Scaling

特点:对异常值不敏感,减少极端值影响,但计算量稍大且不适用于非线性关系

import pandas as pd
from sklearn.preprocessing import RobustScaler

#使用Robust Scaling方法对DataFrame中的数据进行标准化
def robust_scaling(df):
    scaler = RobustScaler()
    scaled_data = scaler.fit_transform(df)
    scaled_df = pd.DataFrame(scaled_data, columns=df.columns)
    return scaled_df

data = {
    'x': [0, 1, 2, 3, 4],
    'y': [0, 1, 4, 9, 16]
}
df = pd.DataFrame(data)

scaled_df = robust_scaling(df)

5)正态化

好处:

  1. 满足模型假设: 许多统计模型,如线性回归和逻辑回归,都假设数据服从正态分布。通过正态化,可以使数据更加符合模型的假设,提高模型的准确性和可靠性。

  2. 提高模型性能: 正态化可以减少数据的偏斜和峰度,使得数据更加接近于正态分布,有助于提高模型的性能和稳定性。

  3. 减少异常值的影响: 正态化可以减少数据中极端值(outliers)的影响,使得数据更加平滑和稳定,有助于模型更好地拟合数据。

  4. 提高统计检验的效果: 在进行统计检验时,很多检验方法都要求数据服从正态分布。通过正态化,可以使得数据满足检验的前提条件,提高检验的效果和可靠性。

坏处:

  1. 可能导致信息丢失

  2. 不适用于所有情况: 正态化并不适用于所有类型的数据,尤其是在数据分布本身已经接近正态分布或者需要保留原始数据分布特性的情况下,正态化可能并不合适。

  3. 可能增加计算量: 对数据进行正态化可能需要进行一些复杂的数学计算或转换过程,可能会增加计算量和时间成本。

  4. 对异常值敏感: 在进行正态化过程中,对于数据中的异常值可能会产生一定的影响,尤其是在数据量较少或异常值较多的情况下。

# 正态化
from sklearn.preprocessing import StandardScaler #导入正态化所需库
from numpy import set_printoptions
from pandas import read_csv
import pandas as pd
names = ['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class']
data = pd.read_csv(r"D:\python\.idea\COMAP\数据分析、算法&模型评估\pima_data.csv", names=names)
new_data = data[['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age']]

X = new_data.values
Y = data['class'].values
transformer = StandardScaler().fit(X)
newX = transformer.transform(X)
data[['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age']] = data[['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age']].round(3)
df = pd.DataFrame(newX,columns=['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age'])
df['class'] = data['class']

6)二值化

原理:数值大于某个阈值的数据被映射为1,而小于等于阈值的数据被映射为0

好处:

  1. 简化模型: 二值化可以将连续型数据转换为离散型数据,从而简化了模型的复杂度,使得模型更容易理解和解释。

  2. 减少计算量: 在某些情况下,二值化可以减少计算量和内存消耗,特别是在处理大规模数据时,将连续型数据转换为二元特征可以节省计算资源。

  3. 去除不必要信息: 通过二值化,可以去除数据中一些不必要的细节和噪声,只保留对模型有用的信息,有助于提高模型的泛化能力。

  4. 适用于某些模型: 一些机器学习算法,如决策树、朴素贝叶斯等,对于离散型数据更为友好,因此二值化可以使得这些算法更加适用。

坏处:

  1. 信息丢失: 二值化会丢失数据中的一些信息,尤其是在选择阈值时,可能会损失一些原始数据的特征,从而影响模型的性能。

  2. 可能引入偏差: 在选择阈值时,如果选择不合适的阈值,可能会引入偏差,影响模型的准确性和泛化能力。

  3. 不适用于所有情况: 二值化并不适用于所有类型的数据,尤其是在数据分布本身不是连续型的情况下,二值化可能会导致信息丢失和模型性能下降。

  4. 对特征工程的依赖性: 选择合适的二值化阈值需要一定的领域知识和经验,对特征工程的依赖性较高,可能需要进行多次试验和调整。

# 二值数据
from sklearn.preprocessing import Binarizer #导入二值化所需库
from numpy import set_printoptions
from pandas import read_csv
import pandas as pd

names = ['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class']
data = pd.read_csv(r"D:\python\.idea\COMAP\数据分析、算法&模型评估\pima_data.csv", names=names)
new_data = data[['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age']]

X = new_data.values
Y = data['class'].values
transformer = Binarizer(threshold=0.0).fit(X)
newX = transformer.transform(X)
data[['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age']] = data[['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age']].round(3)
df = pd.DataFrame(newX,columns=['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age'])
df['class'] = data['class']

4、数据降维

1)主成分分析(PCA)

常用:线性降维,数据可视化、特征提取、数据预处理、模式识别和图像处理

通过线性变换将原始特征映射到新的特征空间,新特征是原始特征的线性组合,保留大部分原始数据的方差

影响因素

  1. 保留的信息量:PCA能否在降维的同时尽可能保留原始数据的信息是评价其好坏的重要指标。通常情况下,保留的信息量可以通过解释方差的比例来衡量,保留的信息量越高,PCA的效果越好。

  2. 数据特征的线性可分性:PCA是一种线性降维方法,对于非线性的数据结构可能效果不佳。在数据特征的线性可分性较强时,PCA通常表现良好;但对于非线性数据,可能需要其他更适合的降维方法,如t-SNE等。

  3. 应用场景的适用性:PCA适用于许多数据分析和模式识别任务,但并不是适用于所有情况。在选择是否使用PCA时,需要考虑具体的应用场景以及数据的特点。

import pandas as pd
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt

X = df.values

# 初始化 PCA 模型,设置降维后的维度为2
pca = PCA(n_components=2)

# 将数据集 X 降维至2维
X_pca = pca.fit_transform(X)

# 绘制降维后的数据点
plt.scatter(X_pca[:, 0], X_pca[:, 1])
plt.title('PCA of Custom Dataset')
plt.xlabel('Principal Component 1')
plt.ylabel('Principal Component 2')
plt.show()

2)独立成分分析(ICA)

常用:盲源分离,信号去噪,特征提取(用于声音,图像)

假设数据原本独立,寻找独立的成分,以便将原始数据通过变换矩阵分解为不相关的独立成分

影响因素

  1. 信号独立性:ICA 假设原始信号是相互独立的,这意味着在实际应用中,如果原始信号不满足独立性假设,ICA 的效果可能会受到影响。在应用 ICA 之前,需要对信号独立性做出一定的合理假设,并根据实际情况进行验证。

  2. 噪声水平:如果混合信号中存在较大的噪声,可能会干扰对原始信号的恢复,影响 ICA 的效果。在应用 ICA 时,需要考虑噪声的水平,并采取适当的预处理方法来降低噪声的影响。

  3. 混合模型:ICA 的效果还取决于混合模型的复杂程度。如果混合模型是线性的且稳定的,那么 ICA 的效果通常较好;但如果混合模型是非线性的或不稳定的,可能会导致 ICA 的性能下降。因此需要对混合模型进行合理的假设和验证。

  4. 计算复杂度:与 PCA 相比,ICA 的计算复杂度通常较高。在大规模数据集上应用 ICA 可能会导致计算时间较长,因此需要权衡计算成本和性能要求。

from sklearn.decomposition import FastICA
import matplotlib.pyplot as plt

X = df.values

# 初始化 ICA 模型,设置独立成分的数量为2
ica = FastICA(n_components=2)

# 将数据集 X 进行独立成分分析,降维至2维
X_ica = ica.fit_transform(X)

# 绘制降维后的数据点
plt.scatter(X_ica[:, 0], X_ica[:, 1])
plt.title('ICA of Custom Dataset')
plt.xlabel('Independent Component 1')
plt.ylabel('Independent Component 2')
plt.show()

3)t-分布邻域嵌入(t-SNE)

常用:数据可视化、聚类分析、特征提取、异常检测和自然语言处理

保留数据样本直接的局部结构,常用于可视化高维数据

影响因素

  1. 数据的局部结构:t-SNE 擅长保留数据的局部结构,能够有效地发现数据集中相似的数据点,并将它们映射到降维空间中的相邻位置。如果数据集的局部结构非常重要,那么 t-SNE 往往能够产生较好的降维效果。

  2. 嵌入空间的维度:t-SNE 主要用于将高维数据映射到二维或三维空间进行可视化,对于更高维度的数据,可能会导致信息丢失或失真。因此,在使用 t-SNE 时需要权衡嵌入空间的维度和降维效果。

  3. 超参数的选择:t-SNE 有一些重要的超参数,如 perplexity(困惑度)和 learning rate(学习率)。这些超参数的选择对于 t-SNE 的效果至关重要。不同的超参数组合可能会产生不同的降维效果,因此需要进行实验和调参来找到最佳的超参数设置。

  4. 计算复杂度:t-SNE 的计算复杂度较高,特别是对于大规模数据集。在处理大规模数据时,t-SNE 可能会变得非常耗时,甚至无法进行。因此,在选择是否使用 t-SNE 以及如何使用时,需要考虑计算资源的限制。

from sklearn.manifold import TSNE
import matplotlib.pyplot as plt

X = df.values

# 初始化 t-SNE 模型,设置降维后的维度为2
tsne = TSNE(n_components=2)

# 将数据集 X 进行 t-SNE 降维,降维至2维
X_tsne = tsne.fit_transform(X)

# 绘制降维后的数据点
plt.scatter(X_tsne[:, 0], X_tsne[:, 1])
plt.title('t-SNE of Custom Dataset')
plt.xlabel('t-SNE Component 1')
plt.ylabel('t-SNE Component 2')
plt.show()

TSNE超参数:

  1. n_components:降维后的维度。默认值为 2,表示将数据集降维到二维空间,也可以设置为其他值。

  2. perplexity:困惑度,用于指定 t-SNE 中每个数据点周围的近邻数。较高的困惑度会考虑更多的近邻点,通常在 5 到 50 之间。默认值为 30。

  3. learning_rate:学习率,用于控制 t-SNE 中梯度下降的步长。较高的学习率可能导致降维结果不稳定,而较低的学习率可能导致收敛速度过慢。通常选择较小的学习率。默认值为 200.0。

  4. n_iter:迭代次数,用于指定 t-SNE 迭代的次数。默认值为 1000。

4)线性判别分析(LDA)

通过投影最大化类别间距离和最小化类别内距离,实现数据降维,并保留类别信息,可用于分类问题

常用:模式识别、分类、特征提取和数据可视化,适用于具有明显类别结构的数据集,例如人脸识别,生物信息识别,文本分类,医学诊断。                 

影响因素

  1. 数据的线性可分性:LDA 是一种线性分类器,适用于线性可分的数据。如果数据在低维空间中具有明显的类别边界,那么 LDA 的效果通常较好;但如果数据在高维空间中混合并且类别之间有重叠,LDA 的效果可能不佳。

  2. 类别之间的方差比:LDA 假设类别内部的方差相等,类别之间的方差不同。如果类别内部的方差相对较大,而类别之间的方差相对较小,那么 LDA 的效果通常较好;但如果类别之间的方差非常大,那么 LDA 可能会受到影响。

  3. 维度选择:LDA 是一种降维方法,需要选择降维后的维度。选择合适的维度可以帮助提取最具判别性的特征,从而提高分类效果;但选择不合适的维度可能导致信息丢失或过拟合。

  4. 样本数量:LDA 对样本数量要求较高,特别是对于高维数据。如果样本数量太少,可能会导致 LDA 的估计不准确,从而影响分类效果。

  5. 超参数调优:LDA 有一些超参数需要调优,如正则化参数。合适的超参数设置可以提高 LDA 的鲁棒性和泛化能力,但不合适的超参数设置可能会导致过拟合或欠拟合。

from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
import matplotlib.pyplot as plt

X = df.values  

# 假设 y 是目标变量,即类别标签
y = df['target_column'].values  

# 初始化 LDA 模型,设置降维后的维度为2
lda = LDA(n_components=2)

# 将数据集 X 进行 LDA 降维,降维至2维
X_lda = lda.fit_transform(X, y)

# 绘制降维后的数据点
plt.scatter(X_lda[:, 0], X_lda[:, 1], c=y, cmap='viridis')
plt.title('LDA of Custom Dataset')
plt.xlabel('LDA Component 1')
plt.ylabel('LDA Component 2')
plt.colorbar(label='Target')
plt.show()

LDA超参数:

  1. n_components:降维后的维度。默认为 None

  2. solver:求解器参数,用于指定求解 LDA 时要使用的算法。可选的值有 'svd','lsqr' 和 'eigen'。'svd' 使用奇异值分解进行求解,在大数据集上更有效;'lsqr' 使用最小二乘法进行求解,适用于稀疏数据;'eigen' 使用特征值分解进行求解,适用于小型数据集。默认值为 'svd'。

  3. shrinkage:正则化参数,用于控制 LDA 转换中的收缩。它是一个浮点数或字符串,用于指定收缩类型。如果为 'auto',则根据样本数量和特征数量自动选择合适的收缩策略。默认值为 None,表示不使用收缩。

  4. priors:类别先验概率,用于指定每个类别的先验概率。它是一个数组,数组的长度应与类别数相同,每个元素表示相应类别的先验概率。默认值为 None,表示每个类别的先验概率相等。

  5. store_covariance:一个布尔值,用于指定是否存储每个类别的协方差矩阵。如果为 True,则协方差矩阵将存储在模型属性 covariance_ 中,可以在之后的分析中使用。默认值为 False。

5)特征选择

如基于相关性,方差,信息增益等指标的特征选择方法,直接选择重要特征,从而间接实现降维

常用:解释模型,噪声过滤,数据可视化

影响因素

  1. 特征与目标变量的相关性:特征选择的最重要因素之一是特征与目标变量之间的相关性。与目标变量相关性高的特征更可能对模型的预测能力产生积极影响,因此通常会优先选择这些特征。

  2. 特征之间的相关性:特征之间的相关性也会影响特征选择的效果。高度相关的特征可能会导致冗余信息,因此在特征选择过程中可能会选择其中之一,以减少冗余性并提高模型的泛化能力。

  3. 数据集的大小:数据集的大小对特征选择的影响也很重要。在大型数据集上进行特征选择可能会更为可靠,因为有更多的数据可以用于评估特征的相关性和重要性。相比之下,在小型数据集上进行特征选择可能会更容易过拟合。

  4. 特征选择方法:特征选择的方法选择也是影响效果的关键因素。不同的特征选择方法具有不同的假设和算法,因此可能适用于不同类型的数据和问题。选择适合特定数据和问题的方法是至关重要的。

import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_selection import SelectFromModel

data = {
    'feature1': [1, 2, 3, 4, 5],
    'feature2': [5, 4, 3, 2, 1],
    'feature3': [2, 3, 1, 5, 4],
    'feature4': [3, 1, 2, 4, 5],
    'target': ['A', 'B', 'A', 'B', 'A']
}

df = pd.DataFrame(data)

# 分割特征和目标变量
X = df.drop(columns=['target'])
y = df['target']

# 初始化随机森林分类器作为特征选择模型
clf = RandomForestClassifier(n_estimators=100, random_state=42)

# 使用 SelectFromModel 进行特征选择
selector = SelectFromModel(clf)
selector.fit(X, y)

# 获取选择的特征
selected_features = X.columns[selector.get_support()]

# 打印选择的特征
print("Selected features:", selected_features)

  • 35
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在MATLAB中进行数学建模时,数据预处理是一个非常重要的步骤,它可以帮助我们清洗、转换和准备数据,以便后续的分析和建模。下面是一些常见的MATLAB数据预处理技术: 1. 数据清洗:数据清洗是指处理数据中的异常值、缺失值和重复值等问题。在MATLAB中,可以使用函数如`isnan`、`isoutlier`和`unique`来检测和处理这些问题。 2. 数据转换:数据转换是指将原始数据转换为适合建模和分析的形式。常见的数据转换方法包括标准、归一、对数转换和平滑等。在MATLAB中,可以使用函数如`zscore`、`normalize`和`log`来进行这些转换。 3. 特征选择:特征选择是指从原始数据中选择最相关或最具有代表性的特征。在MATLAB中,可以使用特征选择算法如相关系数、方差分析和主成分分析等来进行特征选择。 4. 数据降维:数据降维是指将高维数据转换为低维表示,以减少数据的复杂性和存储空间。常见的数据降维方法包括主成分分析(PCA)和线性判别分析(LDA)。在MATLAB中,可以使用函数如`pca`和`lda`来进行数据降维。 5. 数据平衡:数据平衡是指处理不平衡数据集中类别不均衡的问题。在MATLAB中,可以使用函数如`undersample`和`oversample`来平衡数据集中的类别。 以上是MATLAB中常见的数据预处理技术,它们可以帮助我们提高数据的质量和准确性,从而更好地进行数学建模和分析。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值