一、引言
数据清洗和预处理是数据科学中必不可少的一部分,它们能够帮助我们准确地分析和预测未来趋势。如果你曾经尝试过进行分析或建模,你会发现数据往往不像我们所想象的那样干净、整洁。需要对数据进行仔细的检查、清理和处理,才能真正把数据转变成有用的信息。
在数据清洗过程中,我们需要处理各种问题如缺失值、异常值和重复值等。产品经理经常说,如果我们的产品不能停止用户的流失,那么改善用户体验就是沉船前所做的最后一项任务。而在数据处理的领域,我们同样不能忽略数据的质量。如果数据有质量问题,将会导致不准确、不可靠的结果和决策。在数据预处理过程中,我们需要对数据进行多种处理,包括划分、缩放、选择、变换和构造等。这些过程是将数据转化成可用的模型的必要步骤,也是提高模型性能的关键。
幸运的是,R语言提供了不少有用的包和函数能够帮助我们完成数据清洗和预处理的工作。它们可以让我们更轻松、更快速地处理数据,让我们聚焦于数据的分析和建模。在本文中,我们将探讨数据清洗和预处理的概述,介绍R语言的核心工具和应用,以及为您展示几个案例分析。在读完本文后,您将会更加熟悉如何使用R语言进行数据清洗和预处理,并且能够在自己的实践中取得更好的效果。
二、数据清洗
2.1 数据质量检查
数据质量检查是进行数据清洗和预处理的第一步,包括缺失值、异常值(离群值)、重复值等问题的检查。下面我们将介绍这些问题的检查方法以及在R语言中如何实现这些方法。
- 缺失值检查及解决方案:
缺失值是指在有观测值的情况下出现空值的情况。缺失值会影响到数据的准确性和可靠性,因此需要对其进行检查和处理。以下是一个简单的缺失值检查的示例代码:
# 创建一个含有缺失值的数据框
df <- data.frame(a = c(1, 2, NA, 4),
b = c(3, 4, 5, NA),
c = c(NA, 5, 6, 7))
# 检查缺失值
sapply(df, function(x) sum(is.na(x)))
# 删除缺失值
df_delete <- df[complete.cases(df), ]
结果展示:
# 缺失值,a列1个,b列1个,c列1个
a b c
1 1 1
#删除缺失值后的数据,只剩下第二行的数据
a b c
2 2 4 5
当然数据缺失不仅仅是野蛮的删除,也是可以补缺的,如:插值法、替代法、或者使用模型填充,多重回归、随机森林和神经网络等。本次课程是入门课程,不予讲解,属于进阶课程,有希望了解学习,欢迎关注一起讨论学习。
- 异常值(离群值)检查及解决方案:
异常值是指与大多数数据点差异极大的值。异常值通常会影响到模型的准确性和效率,因此需要对其进行检测和处理。以下是一个简单的异常值检测代码示例:
# 创建一个含有异常值的数据框
df <- data.frame(a = c(1, 2, 3, 100),
b = c(1, 2, 3, 4))
# 使用箱线图法检测异常值
boxplot(df$a)
# 计算四分位距
q1 <- quantile(df$a, 0.25)
q3 <- quantile(df$a, 0.75)
iqr <- q3 - q1
# 计算概述统计量
summary(df$a)
# 使用IQR方法检测离群值
low <- q1 - 1.5 * iqr
high <- q3 + 1.5 * iqr
# 从数据集中删除离群值
df <- df[df$a >= low & df$a <= high, ]
# 查看处理后的数据框
df
结果展示:
> df
a b
1 1 1
2 2 2
3 3 3
- 重复值检查及解决方案:
重复值是指在数据集中出现相同的数据行或列。重复值通常会引起模型分析中的偏差,因此需要对其进行检测和处理。以下是一个简单的重复值检测的代码示例:
# 创建一个含有重复值的数据框
df <- data.frame(a = c(1, 2, 2, 4, 5),
b = c(3, 4, 5, 7, 5))
# 检测重复值
df[duplicated(df), ]
# 删除重复值
df <- df[!duplicated(df), ]
df
结果展示:
[1] a b
<0 行> (或0-长度的row.names)
# df
a b
1 1 3
2 2 4
3 2 5
4 4 7
5 5 5
2.2 数据清洗包的使用案例
- tidyr包
# 载入tidyr包
library(tidyr)
# 创建一个宽表格
wide_df <- data.frame(id = c(1, 2),
var1 = c(2, 4),
var2 = c(5, 7))
# 使用gather()函数将宽表格变为长表格(行列转换)
long_df <- wide_df %>% gather(key = variable, value = value, -id)
# 将长表格变回宽表格
wide_df_2 <- long_df %>% spread(key = variable, value = value)
# 查看处理后的数据表格
wide_df
wide_df_2
结果展示:
# 转换为长格式
id variable value
1 1 var1 2
2 2 var1 4
3 1 var2 5
4 2 var2 7
# 转换回来
id var1 var2
1 1 2 5
2 2 4 7
练习题目:自主练习使用separate() 函数:将一列拆分成多列;unite() 函数:将多列合并为一列。
除了以上的函数之外,tidyr包还提供了其他一些函数来处理数据中缺失值,例如drop_na() 和replace_na() 等,可以根据不同的需求来选择使用
- dplyr包
# 载入dplyr包
library(dplyr)
# 创建一个含有NA值和重复行的数据框
df <- data.frame(a = c(1, 2, NA, 3, 4, 2),
b = c(1, 2, 3, NA, 4, 2))
# 删除NA值
df <- na.omit(df)
# 删除重复行
df <- distinct(df)
# 重置行名
rownames(df) <- NULL
# 查看处理后的数据框
df
dplyr包提供了一些基本函数,用于数据操作和清洗。这些函数包括filter()、select()、mutate()、summarize() 等等。
三、数据预处理
3.1 数据划分
数据预处理过程中,通常需要将收集到的数据划分为训练集和测试集两部分。训练集用于构建模型和调整模型参数,测试集则用于评估模型的性能和进行模型选择。有效的数据划分对于构建准确的机器学习模型非常重要,以下是一个简单的数据划分步骤的例子:
# 载入必要的包
library(caTools)
# 载入必要的包
library(caTools)
# 生成一组数据
data <- iris
# 随机划分数据集
split <- sample.split(data$Species, SplitRatio = 0.7)
# 训练集
train <- subset(data, split == TRUE)
# 测试集
test <- subset(data, split == FALSE)
#查看数据集的大小
dim(train)
dim(test)
结果展示:
#查看数据集的大小
# dim(train)
[1] 105 5
# dim(test)
[1] 45 5
3.2 特征缩放
特征缩放是一种常见的数据预处理技术,它的目的是将不同特征的取值范围标准化,使其在统一的尺度下进行比较和分析。特征缩放可以提高机器学习模型的性能和收敛速度,常用的特征缩放方法包括Z-score标准化和最小-最大缩放方法等。
- Z-score标准化
Z-score标准化是一种将数据缩放到标准正态分布的方法,它保留了原始数据的分布,并使得均值为0,标准差为1。Z-score标准化方法的数学公式为:
z = (x-μ)/σ
其中,z为标准化后的结果,x为原始数据,μ为均值,σ为标准差。
# 载入必要的包
library(caret)
# 生成一组数据
data <- iris
# 使用preProcess()函数进行标准化
preObj <- preProcess(data[, 1:4], method = "center", "scale")
# 对数据进行转换
data_standardized <- predict(preObj, data[, 1:4])
# 查看标准化的结果
head(data_standardized)
结果展示:
Sepal.Length Sepal.Width Petal.Length Petal.Width
1 -0.7433333 0.44266667 -2.358 -0.9993333
2 -0.9433333 -0.05733333 -2.358 -0.9993333
3 -1.1433333 0.14266667 -2.458 -0.9993333
4 -1.2433333 0.04266667 -2.258 -0.9993333
5 -0.8433333 0.54266667 -2.358 -0.9993333
6 -0.4433333 0.84266667 -2.058 -0.7993333
- 最小-最大缩放
最小-最大缩放是一种将数据缩放至特定区间的方法,例如[0,1]或[-1,1]。该方法使得所有数据都落在指定的范围内,保留了数据的原始分布。最小-最大缩放的数学公式为:
x`= (X−Xmin)/(Xmax-Xmin)
其中,x’为缩放后的结果,x为原始数据,x_min为数据的最小值,x_max为最大值。
以下是一个使用最小-最大缩放的示例:
# 生成一组数据
data <- iris
# 使用preProcess()函数进行最小-最大缩放
preObj <- preProcess(data[, 1:4], method = "range")
# 对数据进行转换
data_scaled <- predict(preObj, data[, 1:4])
# 查看缩放的结果
head(data_scaled)
结果展示:
Sepal.Length Sepal.Width Petal.Length Petal.Width
1 0.22222222 0.6250000 0.06779661 0.04166667
2 0.16666667 0.4166667 0.06779661 0.04166667
3 0.11111111 0.5000000 0.05084746 0.04166667
4 0.08333333 0.4583333 0.08474576 0.04166667
5 0.19444444 0.6666667 0.06779661 0.04166667
6 0.30555556 0.7916667 0.11864407 0.12500000
3.3 特征选择
特征选择是数据预处理中的一个重要环节,它的目的是在保证数据有效性的同时,减少多样性和噪声,提高模型的性能。在特征选择中,我们需要评估每个特征对模型的重要性,并选择最重要的特征进行建模。这样可以降低机器学习模型的复杂度,提高模型的泛化能力。
常见的特征选择方法包括过滤式、包裹式和嵌入式三种方法.
- 过滤式特征选择
过滤式特征选择是最简单和最快速的特征选择方法之一。它的基本思路是预先对特征进行排序,并选择排名最高的特征,代价是可能会忽略特征之间的交互作用。常用的过滤式特征选择方法包括卡方检验、相关系数和信息增益等。
以卡方检验为例,以下是一个使用卡方检验进行特征选择的示例:
# 载入必要的包
library(caret)
# 生成一组数据
data <- iris
# 计算特征和标签之间的卡方检验统计量以及相关的p值
chi_square <- apply(data[, 1:4], 2, function(x) chisq.test(x, data$Species)$statistic)
# 对卡方检验统计量进行排序
rank_chi_square <- rank(-chi_square)
# 选择排名靠前的特征
selected_features <- names(data[, 1:4])[rank_chi_square[1:2]]
# 查看选择的特征
selected_features
结果展示:
# 查看选择的特征
# selected_features
[1] "Petal.Length" "Petal.Width"
- 包裹式特征选择
包裹式特征选择是更为精确的特征选择方法,它的主要思想是将特征子集视为一个黑箱,并使用学习算法进行评估。包裹式特征选择往往需要更长的计算时间,在特征选择过程中也会带来过拟合的风险。在包裹式特征选择中,我们通常使用向前搜索、向后搜索或随机搜索来进行特征选择。
以向前搜索为例,以下是一个使用向前搜索进行特征选择的示例:
# 载入必要的包
install.packages("FSelector")
library(FSelector)
library(caret)
# 生成一组数据
data <- iris
# 进行基于卡方检验的特征选择
varSel <- cfs(Species~., iris)
# 查看选择的特征
varSel
结果展示:
[1] "Petal.Length" "Petal.Width"
- 嵌入式特征选择
嵌入式特征选择是一种更加高级的特征选择方法,它的思想是将特征选择嵌入到机器学习算法中,考虑到特征之间的交互关系以及算法本身带有的正则化机制。在嵌入式方法中,机器学习算法会将特征的重要性作为算法的一部分进行学习,提高了机器学习算法的性能和稳定性。
以下是一个使用Lasso算法进行特征选择的示例:
#载入必要的包
library(glmnet)
# 生成一组数据
data <- iris
# 将数据集划分为特征矩阵和标签向量
x <- as.matrix(data[, 1:4])
y <- as.numeric(as.factor(data$Species))
# 使用Lasso算法进行特征选择
lasso_mod <- cv.glmnet(x, y, alpha = 1)
# 查看选择的特征
elected_features <- coef(lasso_mod, s = “lambda.min”)
elected_features
结果展示:
5 x 1 sparse Matrix of class "dgCMatrix"
s1
(Intercept) 0.93207339
Sepal.Length .
Sepal.Width -0.09512694
Petal.Length 0.15246659
Petal.Width 0.65519060
和上面的两个结果一致:都是"Petal.Length"和"Petal.Width"。
3.4 特征变换
在机器学习中,特征变换指的是通过对原始特征进行转换来构建新特征的过程。特征变换能够提高机器学习模型的性能,主要是因为它们可以消除噪声、削减维度、提取标准化的特征等。
主成分分析(PCA) 和线性判别分析(LDA) 是两种常用的特征变换技术。PCA是一种无监督的数据转换技术,它通过找到高维数据的主成分来将数据从高维投影到低维,以求得更少的特征,从而帮助我们更好地理解数据所包含的信息。LDA是一种有监督的数据变换技术,它在构造特征时需要标签信息,在此基础上寻求最优的分类边界。
这部分是机器学习的范畴,我们这里是入门学习,这里不予深入。
3.5 特征构造
特征构造指的是通过对现有特征进行组合、转换或生成新的特征来增强机器学习模型的能力和性能。这个过程可以帮助我们更好地描述数据,提高模型对数据的拟合能力,并且可以反映数据之间的复杂关系。
特征构造过程通常涉及以下几个步骤:
-
数据理解:我们需要深入理解数据集的特征和问题,并在此基础上提出特征构造的想法。
-
特征设计:基于数据理解,我们可以通过将特征进行加、减、乘、除等方式的组合,或根据经验和领域知识来创造新的有用特征。
-
特征生成:在特征设计的基础上,我们可以生成新的特征,并逐步优化这些特征。在这个过程中,我们需要关注特征的正确性和有效性。
-
特征评估:我们需要使用新特征和模型评估工具来评估特征的效果和影响,看看它们是否能够如预期的那样提高模型的性能。
常用的特征构造方法包括:
-
离散化/分箱:将连续的特征值分为若干大小相等的离散的区间,从而将连续特征转换为离散特征。
-
缺失值处理:将缺失值填充为特定值,如平均数、中位数、众数等,或创建新的特征来描述缺失值的情况。
-
时间序列分析:根据时间序列数据的周期性和趋势来创造新特征,例如,最大值、最小值、平均值、波动率、汇总统计量等。
-
特征交叉:将两个或多个特征组合在一起,构造出新的、更具表达力的特征。
-
特征变换:基于公式或其他特征变换技术,将特征进行转换,以改变其分布或值域。
通过运用上述方法中的一种或多种,我们可以创建新的有用特征,从而增强机器学习模型的能力和性能。
这部分也是机器学习的部分,不与深入。如果对这部分感兴趣,欢迎关注我,后期会推出这部分的课程。
四、结论
数据清洗和预处理是数据科学家所面临的关键问题,对机器学习模型的训练和预测具有至关重要的作用。R语言作为一种广泛使用的工具,具有许多特点和优点,可以帮助我们更好地管理、操作和分析数据。
未来,我们将看到越来越多的新兴技术被开发和应用于数据清洗和预处理,以提高机器学习模型的性能和鲁棒性。这些新兴技术将在AutoML、可解释AI、数据隐私和安全、多模态学习等方面展现出更加广泛的应用和重要性。