Data mining with R

变量类型及处理方法

变量类型

在数据挖掘中,变量可以根据测量尺度主要分为四类:

  1. 定类尺度(Nominal Scale):这是最低级的尺度,用于表示差异。例如,性别(男、女),血型(A、B、AB、O)等。

  2. 定序尺度(Ordinal Scale):定序尺度不仅可以区分项目,还能够确定项目之间的顺序。例如,产品质量评价(优、良、中、差),教育程度(小学、初中、高中、大学)等。

  3. 定距尺度(Interval Scale):定距尺度除了具有定序尺度的特性外,还可以测量项目之间的距离。例如,温度(摄氏度),年份等。

  4. 定比尺度(Ratio Scale):定比尺度是最高级的尺度,它具有固定的零点和单位尺度,并且可以进行加、减、乘、除等运算。例如,长度(米)、重量(千克)、收入等。

对变量的理解和处理直接影响到数据挖掘的结果,因此我们需要根据不同的变量类型选择合适的数据挖掘方法。

如何处理离散变量

处理离散(分类)变量一般有以下几种方法:

  1. 独热编码(One-hot Encoding):对于每一个类别生成一个布尔列,这个类别表示为1,其它类别表示为0。例如,如果你的数据集中有一个名为“颜色”的分类变量,其中包含三个值:“红色”,“蓝色”和“绿色”。通过独热编码,可以将这一分类变量转化为3个布尔变量:“是红色”,“是蓝色”和“是绿色”。

  2. 标签编码(Label Encoding):每一个唯一的分类值被赋予一个整数。例如,“红色”为1,“蓝色”为2,“绿色”为3。这种方式适用于有序的分类变量,如评级(好、中、差等)。但对于无序的分类变量,可能导致模型误解其中存在顺序关系。

  3. 二进制编码(Binary Encoding):首先,将所有的分类值按照出现的频率从高到低排序,并分配一个唯一的整数值。然后,将这些整数值转换为二进制形式。

  4. 哑变量(Dummy Variable):哑变量是独热编码的一种特例,常用于统计学中。在哑变量编码中,会为分类变量的每一个类别创建一个新的变量,然后使用0或1来表示类别是否存在。与独热编码不同的是,为了避免共线性问题,哑变量会少创建一个变量。

  5. 效应编码(Effect Encoding):效应编码是哑变量编码的一种扩展,通常用于线性模型(如线性回归)。对于每个类别,如果该类别对应的观察值是1,则编码为1,如果是0,则编码为-1,否则编码为0。

  6. 哈希编码(Hashing Encoding):哈希编码通过哈希函数将分类变量映射到比原先分类数目更小的空间。它可以处理大规模分类特征,并且在内存和计算上更高效。但是由于哈希碰撞的存在,可能会导致信息损失。

选择哪种方法取决于你的数据和模型。有些算法(如决策树和随机森林)可以直接处理分类变量,而其他算法(如线性回归和支持向量机)则需要进行以上编码。

如何处理时间变量

时间自变量无法直接进入建模数据集。因为时间是无限增长的,在建模数据集中出现的时间肯定早于预测数据集中出现的时间,所以如果需要再建模过程中考虑时间自变量,就必须对其进行变换。

处理时间信息的方式主要取决于其在数据分析中的作用和含义。以下是一些常见的处理方式:

  1. 日期/时间分解:将日期或时间字段分解为年、月、日、小时、分钟和秒等较小的部分。这样可以帮助我们更好地从中找出潜在的趋势、周期性和模式。

  2. 计算时间间隔:在许多情况下,可能需要知道两个日期或时间之间的间隔,例如计算用户的留存时间、产品的生命周期等。

  3. 日期/时间编码:将日期转换为季度、星期几、工作日或节假日等分类变量,这对于考察特定事件对结果的影响非常有用。

  4. 时间序列分析:如果数据具有时间顺序,那么就可能涉及到时间序列分析。例如通过自相关和偏自相关图来检查数据是否存在季节性和趋势。

  5. 设立时间窗口:在某些场景下,我们可能只关注数据的一部分时间段,比如用户近7天、30天的行为数据等,此时就需要设立合适的时间窗口进行分析。

  6. 离散化/分箱:把连续的时间信息转换成类别,例如将24小时制的时间划分为“早上”、“中午”、“下午”和“晚上”。

以上都是处理时间信息的一些常见策略,具体采取哪种方式需要根据你的数据和需求来定。

如何处理数据中的异常值

处理建模数据集中的极值(即异常大或异常小的值)是很重要的一部分,因为它们可能对模型的学习产生不利影响。以下是一些常见的处理方法:

  1. 删除:如果你确定这些极值是由于错误或其他我们不关心的原因导致的,可以选择直接删除。

  2. 截断:将所有超出某个范围的值设定为范围的上限或下限。这种方法可以减少极值的影响,但保留了它们的存在。

  3. 变换:如对数变换、Box-Cox变换等,可以减小极值造成的影响,并使数据更接近正态分布,便于后续建模处理。

  4. 标准化/归一化:通过尺度变换将所有数据转换到同一尺度,可以降低极值对模型的影响。

  5. 使用鲁棒性模型:某些模型(如基于树的模型)对极值具有较强的鲁棒性,不需要额外处理。

  6. 分位数离散化:将连续变量根据其值的大小进行排序,按照分位数(如四分位数)划分为多个等级,较大或较小的极值会被归入最高或最低的分位数中。

对待极值没有通用的最佳做法,具体处理方式需要基于你的数据特性、模型选择以及业务需求来决定。

Box-Cox 变换

Box-Cox变换是一种数值稳定化(稳定方差)和正态化的技术,适用于连续响应变量。它的目标是找到一个合适的指数(λ)来转换数据,使得转换后的数据接近正态分布。

Box-Cox 变换公式如下:

y^{(\lambda)} = \begin{cases} \frac{y^{\lambda}-1}{\lambda} & \text{如果}\ \lambda \neq 0, \\ ln(y) & \text{如果}\ \lambda = 0, \end{cases}

其中,y 是需要被转换的原始数据,λ 是转换参数。在实际操作中,通常会选择使得数据更接近正态分布的 λ 值。

这种方法可以有效地处理偏态分布的数据,并且对异常值具有良好的稳健性。然而,Box-Cox 变换要求输入数据必须是正的。对于包含零或负数的数据,可能需要进行平移或其他预处理步骤,以便应用 Box-Cox 变换。

如何处理缺失值

处理缺失值的方法有很多种,具体使用哪种取决于数据丢失的性质以及应用领域。

如果缺失值实际存在但是没有被观测到,那可以进行填充、插值等。如果缺失值本身就没有,比如新用户没有购买所以就没有购买记录,那么可以使用缺失指示变量。

以下是一些常见的处理方法:

  1. 删除:直接删除包含缺失值的记录。这是处理缺失值最简单的方法,但如果数据丢失是随机的,或者缺失值较多时,可能会导致信息损失。

  2. 填充:将缺失值替换为某个值。常见的填充方式包括使用固定值、平均值、中位数或众数等。对于分类变量,通常使用众数来填充;对于连续变量,可以考虑使用平均值或中位数。

  3. 插值:在时间序列数据中,如果一个观测点的前后数据都是存在的,那么可以根据前后数据对其进行插值。插值方法有线性插值、多项式插值、样条插值等。

  4. 预测模型:利用存在的数据建立模型,预测缺失值。例如使用回归模型、决策树、K-最近邻(KNN)等算法。

  5. 使用缺失值指示变量:创建一个新的变量来指示数据是否丢失。例如,在处理调查问卷时,某个问题的非回答(NA)可能就意味着被调查者对这个问题选择了不回答,而这本身可能就是一个重要的信息。

  6. 多重插补:多重插补(Multiple Imputation)是一种统计技巧,通过自相关关系生成多份替代缺失值的完整数据集,在每个数据集上进行分析,然后将结果合并。它可以有效处理数据丢失不确定性的问题。

在处理缺失值时,首先需要理解数据丢失的机制,例如是完全随机丢失、随机丢失还是非随机丢失,然后再选择适当的方法。对于不同的问题,可能需要尝试不同的方法,以找到最适合的处理方式。

类别不平衡问题

类别不平衡问题(Class Imbalance Problem)是在监督学习中常见的问题,其特点是目标变量的类别分布不均匀。例如,在二分类问题中,大部分样本可能属于一个类别,而另一类别的样本数量相对非常少。

这种问题在现实世界中很常见,比如信用卡欺诈检测、疾病诊断、电子邮件垃圾过滤等场景。在这些情况下,负面类(如欺诈、疾病或垃圾邮件)通常会远少于正面类。

类别不平衡问题给机器学习任务带来挑战,因为大多数算法会偏向于多数类,从而忽略了数量较少但可能更重要的少数类,导致模型性能降低。

处理类别不平衡问题有各种方法,例如:

  1. 重新采样:包括过抽样(增加少数类的样本)和欠抽样(减少多数类的样本)。

  2. 产生合成样本:例如SMOTE(Synthetic Minority Over-Sampling Technique)算法,通过插值的方式生成新的少数类样本。

  3. 调整分类阈值:针对不同的类别设定不同的分类阈值。

  4. 代价敏感学习:给予少数类样本更高的权重。

  5. 使用集成方法:如随机森林、Adaboost等,这些算法对不平衡数据具有较好的鲁棒性。

过抽样(Oversampling)和欠抽样(Undersampling)通常用于处理不平衡分类问题。

  1. 过抽样:当我们的数据集中少数类的样本数量远少于多数类时,过抽样方法会通过增加少数类的样本数量来达到类别平衡。这可以通过复制少数类样本或生成新的少数类样本(例如使用SMOTE算法)来实现。但是,过度抽样可能会导致模型过拟合,因为它会复制少数类样本,这可能会引入噪声。

  2. 欠抽样:与过抽样相反,当我们的数据集中多数类的样本数量远大于少数类时,欠抽样方法会通过减少多数类样本的数量来达到类别均衡。这可以通过随机删除多数类样本或使用聚类技术将多数类样本进行合并等方式实现。然而,欠抽样可能会丢失多数类的一些重要信息。

以上两种方法都有其优势和缺点,具体使用哪种方法需要基于你的数据特性以及业务需求来决定。另外,也可以考虑使用组合采样(Combination Sampling),同时应用过抽样和欠抽样,或者使用更复杂的方法如代价敏感学习(Cost-Sensitive Learning)等。

降维操作

数据的降维是指通过某种数学方法,将有很多特征(维度)的数据集转化为具有较少特征的数据集,同时还能保留原始数据中的大部分重要信息。

以下是进行数据降维的主要理由:

  1. 减少计算量:大量的特征可能会导致计算量巨大,增加处理数据和训练模型的时间。

  2. 降低模型复杂性:去除不相关或冗余的特征可以降低模型的复杂性,提高模型的可解释性。

  3. 避免维度灾难:在高维度空间中,数据可能会非常稀疏,这使得许多机器学习算法难以有效地工作,这被称为“维度灾难”。

  4. 可视化:对于二维或三维的数据,我们可以利用图形直观地展示其结构或者模式。而更高维度的数据无法直接可视化,需要通过降维技术转换到二维或三维。

最常见的降维技术包括主成分分析(PCA)、线性判别分析(LDA)、t-分布邻域嵌入算法(t-SNE)等。选择哪种技术取决于你的数据特性和需求。

数据挖掘方法

关联规则挖掘

关联规则挖掘是一种在大型数据集中发现变量间有趣关系的方法,常被用于市场篮分析,它可以帮助我们理解哪些商品经常同时被购买。下面是一个具体的例子:

假设你是一个零售商,出售各种不同的商品,你希望知道客户的购买行为模式来改善产品布局或优化销售策略。你可能有一个大型的交易数据库,每条记录包含一次购买行为以及该次购买中包含的商品。

通过关联规则挖掘,你可能会发现像“如果购买了面包和黄油,那么很可能也会购买牛奶”这样的规则。这种规则表明面包、黄油和牛奶之间存在强烈的关联性。利用这个信息,你可能会把牛奶放在面包和黄油附近,从而增加销售量。

在实际操作中,挖掘关联规则通常使用 Apriori 算法或 FP-Growth 算法等。

Apriori算法是一种用于频繁项集挖掘和关联规则学习的常见方法,主要应用在事务数据上。它最初被提出是为了解决市场篮子分析的问题,即找出哪些商品会被同时购买。

Apriori算法基于一个关键的概念,称为“先验原理”。这个原理指出,如果一个项集是频繁的,那么它的所有子集也一定是频繁的。反过来,如果一个项集是稀疏的(非频繁的),那么它的所有超集也一定是稀疏的。

Apriori算法的工作流程如下:

  1. 第一步:首先扫描数据库,计算每个项目的支持度(也就是在所有交易中的出现频率)。然后根据给定的最小支持度阈值,删除那些支持度低于阈值的项目,得到一组频繁的1-项集。

  2. 第二步:然后,算法使用频繁的k-项集生成候选的(k+1)-项集,并计算它们的支持度。删除支持度低于阈值的项集。这个过程不断重复,直到不能生成更大的项集。

  3. 第三步:当我们有了频繁项集后,就可以用它们来生成关联规则。对于每个频繁项集,我们生成所有可能的规则,并计算它们的置信度(也就是规则的条件概率)。删除那些置信度低于最小置信度阈值的规则。剩下的规则就是我们挖掘出来的关联规则。

Apriori算法的优点是原理简单、易于实现。但是,当数据库很大或者项集数量很多时,Apriori算法的计算和存储需求可能会非常大。因此,在实际应用中,可能需要使用更高效的算法,如FP-Growth等。

关联规则分析的一种典型应用是购物篮分析,我们来举一个假设的购物篮数据分析例子。

首先,我们需要一个交易数据集。每个交易代表一个购物篮,包含若干商品。假设我们有以下数据:

# 安装并加载arules包
# install.packages("arules")
library(arules)
载入需要的程辑包:Matrix

载入程辑包:'Matrix'
The following objects are masked from 'package:tidyr':

    expand, pack, unpack

载入程辑包:'arules'
The following object is masked from 'package:dplyr':

    recode
The following objects are masked from 'package:base':

    abbreviate, write
# 创建交易数据
transactions <- list(
  c("面包", "黄油"),
  c("牛奶", "面包", "黄油"),
  c("茶", "糖"),
  c("面包", "黄油", "果酱"),
  c("牛奶", "面包", "黄油", "茶"),
  c("面包"),
  c("茶"),
  c("茶", "糖"),
  c("面包", "牛奶"),
  c("面包", "黄油", "茶")
)
transactions <- as(transactions, "transactions")

然后,使用Apriori算法来找出频繁项集和关联规则:

# 查找频繁项集
frequent_itemsets <- apriori(transactions, parameter = list(support = 0.2, target = "frequent itemsets"))
Apriori

Parameter specification:
 confidence minval smax arem  aval originalSupport maxtime support minlen
         NA    0.1    1 none FALSE            TRUE       5     0.2      1
 maxlen            target  ext
     10 frequent itemsets TRUE

Algorithmic control:
 filter tree heap memopt load sort verbose
    0.1 TRUE TRUE  FALSE TRUE    2    TRUE

Absolute minimum support count: 2 

set item appearances ...[0 item(s)] done [0.00s].
set transactions ...[6 item(s), 10 transaction(s)] done [0.00s].
sorting and recoding items ... [5 item(s)] done [0.00s].
creating transaction tree ... done [0.00s].
checking subsets of size 1 2 3 done [0.00s].
sorting transactions ... done [0.00s].
writing ... [13 set(s)] done [0.00s].
creating S4 object  ... done [0.00s].
inspect(head(frequent_itemsets))
    items    support count
[1] {糖}     0.2     2    
[2] {牛奶}   0.3     3    
[3] {茶}     0.5     5    
[4] {黄油}   0.5     5    
[5] {面包}   0.7     7    
[6] {茶, 糖} 0.2     2    
# 查找关联规则
rules <- apriori(transactions, parameter = list(support = 0.2, confidence = 0.6))
Apriori

Parameter specification:
 confidence minval smax arem  aval originalSupport maxtime support minlen
        0.6    0.1    1 none FALSE            TRUE       5     0.2      1
 maxlen target  ext
     10  rules TRUE

Algorithmic control:
 filter tree heap memopt load sort verbose
    0.1 TRUE TRUE  FALSE TRUE    2    TRUE

Absolute minimum support count: 2 

set item appearances ...[0 item(s)] done [0.00s].
set transactions ...[6 item(s), 10 transaction(s)] done [0.00s].
sorting and recoding items ... [5 item(s)] done [0.00s].
creating transaction tree ... done [0.00s].
checking subsets of size 1 2 3 done [0.00s].
writing ... [10 rule(s)] done [0.00s].
creating S4 object  ... done [0.00s].
inspect(head(rules))
    lhs       rhs    support confidence coverage lift     count
[1] {}     => {面包} 0.7     0.7000000  1.0      1.000000 7    
[2] {糖}   => {茶}   0.2     1.0000000  0.2      2.000000 2    
[3] {牛奶} => {黄油} 0.2     0.6666667  0.3      1.333333 2    
[4] {牛奶} => {面包} 0.3     1.0000000  0.3      1.428571 3    
[5] {黄油} => {面包} 0.5     1.0000000  0.5      1.428571 5    
[6] {面包} => {黄油} 0.5     0.7142857  0.7      1.428571 5    

上述代码会返回支持度大于0.2的频繁项集,以及支持度大于0.2且置信度大于0.6的关联规则。

然后可以根据业务需求来解读这些结果,例如把经常一起购买的商品放在一起,或者针对某个商品的购买者进行推荐等。

请注意,这只是一个基础示例。在实际应用中,可能需要更复杂的数据预处理步骤,以及调整Apriori算法的参数以找到最有价值的项集和规则。

聚类分析

以下是一个使用R语言进行k-means聚类分析的例子。我们将使用内置的iris数据集。

首先,加载所需的库并读取数据:

# 读取数据
data(iris)

然后进行k均值聚类,这里我们设定cluster的数量为3(实际情况下可能会需要用到一些方法比如elbow method来确定k的值):

set.seed(123) # 设置随机数种子以保证结果可复制

# 执行k-means聚类
clusters <- kmeans(iris[, 1:4], centers = 3)

# 查看聚类结果
print(clusters)
K-means clustering with 3 clusters of sizes 50, 62, 38

Cluster means:
  Sepal.Length Sepal.Width Petal.Length Petal.Width
1     5.006000    3.428000     1.462000    0.246000
2     5.901613    2.748387     4.393548    1.433871
3     6.850000    3.073684     5.742105    2.071053

Clustering vector:
  [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [38] 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 [75] 2 2 2 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 2 3 3 3 3 2 3 3 3 3
[112] 3 3 2 2 3 3 3 3 2 3 2 3 2 3 3 2 2 3 3 3 3 3 2 3 3 3 3 2 3 3 3 2 3 3 3 2 3
[149] 3 2

Within cluster sum of squares by cluster:
[1] 15.15100 39.82097 23.87947
 (between_SS / total_SS =  88.4 %)

Available components:

[1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss"
[6] "betweenss"    "size"         "iter"         "ifault"      

现在,clusters对象包含了聚类的结果。我们可以将其添加回原始数据,并对结果进行可视化:

# 将聚类结果添加到原始数据
iris$Cluster <- as.factor(clusters$cluster)

# 可视化聚类结果
ggplot(iris, aes(Petal.Length, Petal.Width, color = Cluster)) + 
  geom_point() +
  theme_minimal()

以上就是一个基本的k-means聚类分析的例子。注意,k-means聚类假设簇是凸形和圆形的,如果你的数据不满足这些假设,可能需要选择其他的聚类算法。

广义线性模型

广义线性模型(Generalized Linear Model,简称GLM)是一类灵活的统计模型。它扩展了传统的线性回归模型,允许因变量(响应变量)服从广义的概率分布,而不仅仅是正态分布。

GLM主要包含以下三个部分:

  1. 随机分布成分:这指定了因变量或响应变量服从的概率分布。在GLM中,响应变量可以服从包括二项分布、泊松分布、负二项分布、伽马分布等在内的指数族分布。

  2. 系统部分:这和传统的线性回归模型类似,假设预测值是自变量(解释变量)的线性组合。

  3. 连结函数:连结函数定义了响应变量的期望值和线性预测子之间的关系。最常见的例子包括恒等链接函数(用于正态分布)、对数链接函数(用于泊松分布或伽马分布)和logit链接函数(用于二项分布)。

GLM的一个重要优点是它能够处理各种类型的响应变量,包括连续的、二元的、计数的等等。因此,GLM被广泛应用于各种领域,包括社会科学、生物科学、医学和工程等。

在R语言中,可以使用glm函数来拟合GLM模型,例如:

# 使用泊松回归(对数链接函数)预测计数数据
model <- glm(y ~ x1 + x2, data = mydata, family = poisson(link = "log"))

连接函数

在广义线性模型(GLM)中,连接函数用于描述因变量(响应变量)的期望值和线性预测子之间的关系。根据响应变量的类型和分布,可能会选择不同的连接函数。

以下是一些常见的连接函数及其适用场景:

  • 恒等链接函数(Identity link):这种连接函数指的是线性预测子直接等于响应变量的期望值。也就是说,g(μ) = μ。适用于因变量服从正态分布的线性回归。
model <- glm(y ~ x1 + x2, data = mydata, family = gaussian(link = "identity"))
  • 对数链接函数(Log link):其中 g(μ) = log(μ)。这种连接函数通常用于因变量是计数数据且服从泊松分布的泊松回归或者伽马回归。
model <- glm(y ~ x1 + x2, data = mydata, family = poisson(link = "log"))
  • Logit链接函数:其中 g(μ) = log[μ / (1 - μ)]。这种连接函数用于二元响应变量,比如逻辑回归中的二项分布。
model <- glm(y ~ x1 + x2, data = mydata, family = binomial(link = "logit"))
  • Probit链接函数:其中 g(μ) = Φ^(-1)(μ),Φ为标准正态分布的累积分布函数。也用于二元响应变量,它提供了一种与logit链接稍有不同的对于概率的建模方式。
model <- glm(y ~ x1 + x2, data = mydata, family = binomial(link = "probit"))

以上只是部分常见链接函数的示例,实际上还有许多其他类型的连接函数,可以根据具体的数据和建模需求进行选择。

泰坦尼克存活数据

在R语言的titanic包中就包含了泰坦尼克号数据集,使用该数据分析乘客的存活情况与其他变量之间的关系。

以下是一个简单的示例:

# 安装和加载所需的R包
# pak::pak("titanic")
library(titanic)
library(tidymodels)
── Attaching packages ────────────────────────────────────── tidymodels 1.1.1 ──
✔ broom        1.0.5     ✔ rsample      1.2.0
✔ dials        1.2.0     ✔ tune         1.1.2
✔ infer        1.0.5     ✔ workflows    1.1.3
✔ modeldata    1.2.0     ✔ workflowsets 1.0.1
✔ parsnip      1.1.1     ✔ yardstick    1.2.0
✔ recipes      1.0.9     
── Conflicts ───────────────────────────────────────── tidymodels_conflicts() ──
✖ scales::discard()     masks purrr::discard()
✖ recipes::discretize() masks arules::discretize()
✖ Matrix::expand()      masks tidyr::expand()
✖ dplyr::filter()       masks stats::filter()
✖ recipes::fixed()      masks stringr::fixed()
✖ dplyr::lag()          masks stats::lag()
✖ Matrix::pack()        masks tidyr::pack()
✖ arules::recode()      masks dplyr::recode()
✖ yardstick::spec()     masks readr::spec()
✖ recipes::step()       masks stats::step()
✖ Matrix::unpack()      masks tidyr::unpack()
✖ recipes::update()     masks Matrix::update(), stats::update()
• Dig deeper into tidy modeling with R at https://www.tmwr.org
# 加载数据
data("titanic_train")

# 使用 tidymodels 处理数据,并进行模型训练
data <- titanic_train %>% 
 select(Survived, Pclass, Sex, Age, Fare) %>% 
 mutate(Survived = factor(Survived))  |> 
 drop_na()

# 划分训练集和测试集
set.seed(123)
data_split <- initial_split(data, prop = 0.75, strata = Survived)
data_train <- training(data_split)
data_test <- testing(data_split)

# 定义预处理步骤
rec <- recipe(Survived ~ ., data = data_train) %>%
 step_dummy(all_nominal(), -all_outcomes()) %>%
 step_zv(all_predictors()) %>%
 prep()

# 应用预处理步骤到训练集和测试集
data_train_preprocessed <- bake(rec, new_data = data_train)
data_test_preprocessed <- bake(rec, new_data = data_test)

# 定义模型
model <- logistic_reg() %>%
 set_engine("glm") %>%
 fit(Survived ~ ., data = data_train_preprocessed)

# 预测测试集的结果
results <- predict(model, data_test_preprocessed) %>%
 bind_cols(data_test)

model
parsnip model object


Call:  stats::glm(formula = Survived ~ ., family = stats::binomial, 
    data = data)

Coefficients:
(Intercept)       Pclass          Age         Fare     Sex_male  
   4.603776    -1.245534    -0.029056     0.001864    -2.377828  

Degrees of Freedom: 534 Total (i.e. Null);  530 Residual
Null Deviance:      722.5 
Residual Deviance: 491.8    AIC: 501.8

在泰坦尼克号生存预测的模型中,我们使用了逻辑回归模型。逻辑回归模型的结果可以通过其系数进行解释。

逻辑回归模型的基本形式是(Equation 1):

logit(p) = β0 + β1*X1 + β2*X2 + ... + βn*Xn \tag{1}

其中,p 是正例(这里是”Survived”)的概率,βi是第i个特征的系数,Xi是第i个特征的值。logit(p)是p的对数几率。

每个系数βi表示当其他所有特征保持不变时,X_i增加一单位时其对应的对数几率的改变量。如果βi > 0,那么该特征与正例的概率正相关;如果βi < 0,那么该特征与正例的概率负相关。

例如,如果得到的模型系数为:

(Intercept)      4.60
Sexmale         -2.37
Age             -0.03
Fare             0.002
Pclass          -1.24

那么,可以解释为性别为男性、乘客等级为2和3等级相比于1等级、年龄增加,都会降低生存的对数几率,而票价增加则会增加生存的对数几率。

上面采用了 tidymodels 的流程,如果采用基础 R 方法,则有如下结果。

model <- glm(Survived ~ ., data = data_train, family = binomial(link = "logit"))
model

Call:  glm(formula = Survived ~ ., family = binomial(link = "logit"), 
    data = data_train)

Coefficients:
(Intercept)       Pclass      Sexmale          Age         Fare  
   4.603776    -1.245534    -2.377828    -0.029056     0.001864  

Degrees of Freedom: 534 Total (i.e. Null);  530 Residual
Null Deviance:      722.5 
Residual Deviance: 491.8    AIC: 501.8

两个结果是一致的。

神经网络模型

我们使用Keras库和tensorflow后端在R环境中进行神经网络的建立和训练。这里我们使用UCI机器学习存储库中的红酒质量数据集。

# 加载所需库
library(keras)
reticulate::use_condaenv("tf")

# 读取并预处理数据
# 安装和加载rattle包
if (!require(rattle)) {
  pak::pak("rattle")
}
# 加载wine数据集
data(wine, package = "rattle")

# 将数据分为训练集和测试集
set.seed(123)  
indices <- sample(1:nrow(wine), nrow(wine)*0.7)
train_data <- wine[indices, ] |> as.numeric()
test_data <- wine[-indices, ]  |> as.numeric()

# 分离特征和标签
train_X <- as.matrix(train_data[, -1]) 
train_Y <- train_data$type

test_X <- as.matrix(test_data[, -1])
test_Y <- test_data$type

# 数据标准化
mean <- apply(train_X, 2, mean, na.rm = TRUE)
std <- apply(train_X, 2, sd, na.rm = TRUE)
train_X <- scale(train_X, center = mean, scale = std)
test_X <- scale(test_X, center = mean, scale = std)

# 构建神经网络模型
model <- keras_model_sequential() 
model %>% 
  layer_dense(units = 64, activation = 'relu', input_shape = ncol(train_X)) %>% 
  layer_dense(units = 32, activation = 'relu') %>%
  layer_dense(units = 1)

# 编译模型
model %>% compile(
  loss = 'mse',
  optimizer = optimizer_rmsprop(),
  metrics = c('mae')
)

# 训练模型
history <- model %>% fit(
  train_X, train_Y,
  epochs = 300, batch_size = 16, 
  validation_split = 0.2
)

以上就是使用keras在R中构建神经网络模型进行红酒质量预测的示例。注意,实际应用中可能需要更多的步骤来优化模型性能和解释模型结果。