MOOC-R语言数据分析-听课笔记-3

2020-05-15

疫情居家起间,学习了北京邮电大学 艾新波老师的MOOC 课程之R语言数据分析,十分精彩,特整理听课笔记,便于回顾,也飨读者。全课程共有三篇笔记,本篇为第三篇。

数据科学最令人着迷的地方:一旦进行量化,看似风马牛不相及的事物或属性,经过数学运算,居然可以画上等号,刻画各种各样的规律。

观数以形:一维数据作图

茎叶图

stem()函数。 注意先用as_vector()函数处理一下。

# 1101班数学成绩的茎叶图
cjb %>%
	filter(bj == "1101") %>%
	select(sx) %>%
	as_vector() %>%
	stem(scale = 0.5)
# 1110班数学成绩的茎叶图

茎叶图的缺点就是,不能展示数据量太大的情况。

直方图

基础的hist()函数和ggplot2扩展包中的函数

ggplot2的作图逻辑

ggplot(data = DATA) + GEOM_FUNCTION(mapping = aes(MAPPING), stat = STAT, postion = POSITION) + <COORDINATE_FUNCTION> + <FACET_FUNCTION> + <SCALE_FUNCTION> + <THEME_FUNCTION>

其中,ggplot(data = DATA) + GEOM_FUNCTION(mapping = aes(MAPPING), stat = STAT, postion = POSITION) 是必须的。STAT 是统计变换,也可以不做统计变化,就是设置成identity。

直方图作图 + 概率密度曲线

sx_hist_result <- hist(cjb$sx, plot = FALSE)
names(sx_hist_result)
ggplot(data = cjb,mapping = aes(sx)) +
	geom_histogram(
    breaks = sx_hist_results$breaks,
    color = "darkgrey",
    fill = "white") + 
	stat_bin(breaks = sx_hist_result$breaks,
            geom = "text",
            aes(label = ..count..)) +
	geom_density(colour = "blue") +
	coord_flip()

小提琴图

其实就是概率密度曲线做了一个对称

ggplot(cjb, aes(x = factor(0), y = sx)) + 	
	geom_violin(fill = "orange", alpha = 0.2) + 
	coord_flip()

箱线图

IQR = Q3 - Q1

下边界: max(Q1 - 1.5 * IQR, min(x))

Q1

median

Q3

上边界: min(Q3 + 1.5 * IQR, max(x))

cjb %>%
	ggplot(aes(x = factor(0), y = sx)) + 
	geom_boxplot(width = 0.25,
                fill = "#E69F00",
                outlier.colour = "red",
                outlier.shape = 1,
                outlier.size = 2) +
	geom_rug(position = "jitter",
            size = 0.1,
            sides = "b") +
	coord_flip()
# 直接显示boxplot的统计参数值
boxplot.stats(cjb$sx)

小提琴图 + 箱线图

cjb %>%
	ggplot(aes(x = factor(0), y = sx)) + 
	geom_violin(fill = "#56B4E9",
               width = 0.75) +
	geom_boxplot(width = 0.25,
                fill = "#E69F00",
                outlier.colour = "red",
                outlier.shape = 1,
                outlier.size = 2) +
	geom_rug(position = "jitter",
            size = 0.1,
            sides = "b") +
	coord_flip()

观数以形:二维数据作图

二维数据空间的形态

类别 刻画方法
类别变量vs类别变量(因子vs因子) 矩形树图
数值变量vs数值变量(向量vs向量) 散点图,相关系数图
类别变量vs数值变量(因子vs向量) 分组绘制箱线图/直方图/概率密度图等

矩形树图:采用嵌套的矩形来表达层级数据,数值大小映射为面积和颜色。比如,男生 女生,选择文理分科的数量。R语言中调用treemap 扩展包

分组绘制概率密度图,就是层峦叠嶂图,需要ggridges扩展包

观数以形:高维数据作图

一般是三维数据空间

主要讲三种

三维散点图

library(rgl),plot3d()

是一个交互的3d图

脸谱图

library(aplpack)

可以表达很多维特征,但是不便于表达太多点。

平行坐标图

平行的坐标,一条条的连线。

数据空间的其他形态:密度

数据空间的其他形态:均匀程度(Hopkins统计量,告诉我们所拿到的数据,多大程度上接近于均匀散步的形态,通过clustertend扩展包计算hopkins统计量)。

11章 相随相伴,谓之关联

数据分析简史(数据分析的经典算法出现很久了)

  1. 1805年,linear regression
  2. 1936年,linear discriminant analysis
  3. 1951年, k nearest neighbors algorithm
  4. 1957年, k means
  5. 1958年, logistic regression
  6. 1984年, classification and regression tree
  7. 1986年, back prepagation
  8. 1993年, Apriori,关联规则的算法,
  9. 1995年, support vector machine
  10. 2001年, random forest
  11. 2006年, deep learning
  12. 2016年, AlphaGo

关联导论:啤酒和尿不湿的故事(购物篮分析)、下载了这篇文章的读者还下载了这些文章、购买了这本书的读者还购买了如下书籍。

关联规则 I

关联分析(association analysis):用于发现大型数据集中有意义的联系,所发现的联系可以用频繁项集或关联规则的形式表示。

关系分析中的基本概念:项、项集、事务。

I = {i1,i2,i3…}

T = {t1, t2, t3,…}

项,就是类似于例子中的每一个商品。

项的集合就是项集。

包含k个项的项集称为k-项集。

项集出现的频度是,包含这个项集的事务数,简称为项集的频度,支持度计数(是一个频数),而支持度就是一个比例(支持度计数除以总的事务数)。

如果项集的支持度满足预定义的最小支持度阈值,称该项集为频繁项集。所以,频繁项集的概念,蕴含了“同时性”的含义。

关联规则:是一个蕴含式。A=>B,其中A和B 是不相交的项集。具体含义就是,A出现的时候,B也出现;或者说,B伴随着A出现。

同时性: 用支持度来表征

置信度:是一个条件概率,A B 同时出现的支持度频数/A出现的频数

所以,支持度,减少偶然性; 置信度,增加推断能力。

满足最小支持度和置信度的规则成为强规则。

一旦获得A、B 和A B同时出现的支持度,则容易导出对应的关联规则。

换言之,关联规则的挖掘可以分为两个步骤

  1. 找出所有频繁项集,满足最小支持度。避免野蛮搜索,基于支持度对候选集进行剪枝。
  2. 由频繁项集产生强关联规则,满足最小置信度。避免野蛮搜索,基于置信度对规则进行剪枝。

首先看第一步骤,挖频繁项集。

用到Apriori算法,原理是,一个包含k个项的数据集可能产生2的k次方-1个频繁项集,野蛮搜索的话,频繁项集的搜索空间是指数规模的。

幸好我们有一下先验性质 Apriori Proper

  • 频繁项集的所有非空子集也一定是频繁的
  • (上一个的逆否命题就是)非频繁项集的超集必定是非频繁的

每一个项集的支持度,不会超过它的子集的支持度。利用这个性质,就可以大大减少频繁项集的搜索数。于是,在搜索频繁项集的时候,有一个方向性,就是先搜索一项集,再搜索二项集,以此类推。

第二步骤,Apriori算法,由候选集产生关联规则

如果规则X =>(Y - X)不满足置信度阈值,则对于X’ 包含于X, X‘ =>(Y - X’)d的规则也一定不满足置信度阈值。所以,就可以基于置信度对规则进行剪枝了。

Apriori算法,可以解决蛮力搜索带来的组合爆炸的问题。

Apriori算法是平凡的,因为算法的过程,无非就是做搜索、匹配、计数、求比例,……

Apriori算法是美好的,因为算法利用了支持度剪枝(大大减少了频繁项集的搜索)、置信度剪枝(大大减少了规则的搜索)。

关联规则 II

所有的模型必须经过评估,所以关联规则也不例外。频繁模式依然产生,那么根据数据分析的套路,接下来,如何评估所评估所产生的模式?

所以计算完支持度和置信度之后,找出了强的关联规则,还应该再计算一下,提升度,如下:

提升度: \(\text { lift } (A, B)=\frac{P(A \cup B)}{P(A) P(B)}=\frac{P(A | B)}{P(A)}=\frac{P(B | A)}{P(B)}\)

  • 如果提升度小于1,则A的出现和B的出现是负相关的,意味着一个出现可能导致另一个不出现
  • 如果提升度大于1,则A的出现和B的出现是正相关的,意味着一个出现蕴含另一个的出现
  • 换言之,它评估一个的出现“提升”另一个出现的程度。

关联规则 III

关联分析的R语言实现,所以首先去task views中看可以用的包。

Task views – Machine learning – Association Rules

R code for association analysis with cjb dataset.

用一句话形容关联规则的挖掘:

所谓关联规则的学习,其实就是观察历史记录,

如果

B总是频繁地和A一起出现(体现的是支持度)

当A出现时,B出现的概率很大(体现的是置信度),甚至是更大(体现的是提升度),

那么

很自然形成一条关联规则 A=> B

思考一下:除了经典的Aprori算法外,还有好的新的算法如,FP-Growth, Eclat等,它们能挖出更多更好的规则么(在支持度、置信度框架下)?

答案其实是没有的,新的算法,改进的只是搜索的效率。这是关联规则分析比较特殊的地方,不同的算法的性能是一样的,只是效率有所不用。

分类:既是世间法,自当有分别

分类属于有监督学习的代表。

分类与回归:根据目前所拥有的信息(数据)来建立人们所关心的变量和其他有关变量的关系。假如用y表示感兴趣的变量,用x表示其他可能与y有关的变量(也可能是由若干变量组成的向量),分类与回归就是建立如下函数关系 y = f (x)

y被称为因变量,响应变量(类别/标签)

x被称为自变量(属性/特征),也被称为解释变量或协变量。

当因变量为数量变量时,叫做回归

当因变量为类别变量时,叫做分类

分类:构造一个classifer

回归:构造一个predictor

分类回归模型,主要有数十种。改进的模型,体量是百计。

分类的两个步骤,就是训练、测试。训练集和测试集一般是七三开。同一个数据集的训练集和测试集要求没有交集。

模型的评估,和模型的建立同等重要!为了评估、选择、调试不同的分类器,我们需要通过实际的数据来测试不同分类器的泛化误差,采用测试误差作为泛化误差的近似。

模型评估的方法

评估方法 基本原理
留出法 训练集+测试集 = 数据集,训练集和测试集的交集为空集。随机抽样,而不是原始数据的先后顺序;对于分层的数据集,保持抽样比例
交叉验证法 将数据分成大小相同的k份,每一次将其中一份作为测试集,剩余的k-1份作为训练集,以k次测试结果的平均值作为最终的测试误差
自助法 采用有放回抽样,产生训练集。有36%左右的样本不会被抽到,作为测试集。在随机森林等组合学习算法中使用较多。

模型评估的R自定义函数

global_performance

有生于无

  • 当你无从下手时,基本上也就是掷骰子——一切只好随机了
  • 梯度下降亦然——千里之行,始于随机
  • 在没有模型的时候,用来做预测的基本就是均值和众数。在诸多模型中,都仰仗均值和众数的预测功能

近邻法, k-最近邻分类

有生于无:KNN也没好得了多少

在数据空间环顾四周,看看周边小伙伴额头大多贴着什么标签。

算法

k-最近邻分类算法

输入:最近邻数据k,训练集S,测试集T

输出:对测试集T中所有测试样本预测其类标号值

  1. for 每个测试样本 z = (X’, y’)

    1. 计算z和每个训练样本(X, y)之间的距离d (X, X’)

    2. 选择离z最近的k最近邻集合 Sz 包含于S

    3. 多数表决(argmax就是一个表决函数) \(y^{\prime}=\underset{v}{\operatorname{argmax}} \sum_{\left(x_{i} y_{i}\right) \in D_{x}} I\left(v=y_{i}\right)\)

  2. end for.

计算距离有

  • 欧几里得距离 \(\operatorname{dist}\left(X, X^{\prime}\right)=\sqrt{\sum_{i=1}^{n}\left(x_{i}-x_{i}^{\prime}\right)^{2}}\)

  • 曼哈顿距离 \(\operatorname{dist}\left(X, X^{\prime}\right)=\sum_{j=1}^{n}\left|x_{j}-x_{j}^{\prime}\right|\)

R中用dist函数计算距离,然后cluster扩展包中有一个daisy()函数,可以计算混合距离。

尽管有k个最近邻点决定,但距离不同,这些点决定能力应有所区别。距离加权表决: \(y^{\prime}=\arg \max _{v} \sum_{\left(x_{i}, y_{i}\right) \in D_{x}} w_{i} \times I\left(v=y_{i}\right)\) k的确定,一般没有明确的算法原理告诉你在某组数据中,应该选k等于几。一般就是通过实验的方法,看看k取哪个值比较好。

从k=1开始,使用检验集估计分类器的错误率,重复该过程,每次k增加1,允许增加一个近邻,选取产生最小错误率的k。一般而言,训练元组越多,k的值越大,需要进行多次训练,找到k值。k-近邻一般采用k为奇数,跟投票表决一样,避免因两种票数相等而难以决策。

R语言实现

使用kknn扩展包

近邻法究竟是什么关系呢? 空间划分的角度。

树模型(决策树) CART

决策树,是另外一种空间划分的方法,就是将数据空间不断细分,达到分类的目的。今天介绍一种经典的树模型,叫做分类回归树。

数据框背后的规律,一张表,一棵树。

决策树实质上是一堆特征的条件组合,如果直接蛮力搜索,是组合爆炸,是NP问题。

而决策树的核心思想是:局部最优,步步为赢。“先盯着当前的最优特征对空间展开细分”

在走好当前一步的时候,也需要找好比较好的特征,因为不同的特征有不同的判别能力。

算法

决策树的生长,就是递归花粉、不断生长;局部最优、步步为赢。如果数据无需再分(如当前数据记录类标签为同一类)、或者无法再分(如所有记录的属性值相同、或是数据记录数太少,无法建立叶子结点),并按照少数服从多数(有生于无)的原则,给叶子结点打上标签。否则,寻找一个属性,根据该属性的不同取值情况,把数据分成纯度较大的两个(或几个)子集,对于这些子集,递归执行以上操作,开枝散叶。

所以,决策树是一个归纳算法。

拆分的标准就是不纯度。

最小化子节点不纯度加权平均值

R语言实现

rpart扩展包,可以加载rpart.plot,就可以自动加载rpart包。

决策树生长是第一个环节,还需要第二个环节,即剪枝。比如识别瓶子的时候,如果决策树生长过于茂密,考虑将蓝色作为分支特征,就会产生过拟合的问题,因为蓝色是瓶子只是目前的数据集的局限,将决策树泛化时,效果就会很差。

决策树剪枝,的目的就是为了提高模型的泛化能力。

定义损失函数

规则导出:借助R的rattle包

随机森林

属于集成学习的算法,又称为分类器组合学习。将多个弱分类器,集成为强分类器。

算法

作者是brieman,也是CART的作者。

设:给定d个元组(就是行)的训练集D,为组合分类器产生k棵决策树

  1. 使用有放回抽样生成训练集Di,每个Di都是D的一个自助样本,某些元组在Di中出现多次,而某些元组不出现
  2. 每个自助样本集生长为单棵分类树(随机选取分裂属性集):设F是用来在每个节点决定划分的属性数,其中F远小于可用属性数。为构造决策树分类器Mi,在每个节点随机选择F个属性作为该节点划分的候选属性。使用CART方法增长树,增长到最大规模且不剪枝
  3. 采用简单多数投票法得到随机森林的结果

随机森林的优点,体现在随机选择“不同经验”的专家(即训练集是不同行),然后专家”从不同的视角“进行生成决策树(即每次使用了不同的列即不同的属性),最后进行简单集成,最后的模型性能提升十分显著。

R语言实现

library(randomForest)

朴素贝叶斯

前面的树模型,其中就是为了提高子空间的纯度,就是减少子空间的不确定性。

另一种不确定性减少的办法,就是朴素贝叶斯。

贝叶斯公式 \(P(A | B)=\frac{P(B | A) P(A)}{P(B)}\)

算法

贝叶斯算法用于分类

朴素一点,就是假定特征之间相互独立,以降低计算的复杂度。

既然所有规律都是关系,那么请问:朴素贝叶斯究竟是什么关系,得到规律的表现形式是什么? 其实就是先验概率和条件概率。

R语言实现

library(e1071)

imodel <- naiveBayes(wlfk~., data = cjb([train_set_idx,]))

逻辑斯蒂回归

机器学习而言,几何和概率的视角是同等重要的。空间细分就是几何的视角。

朴素贝叶斯,逻辑斯蒂回归就是概率的视角。这里引入一个新的函数,就是sigmoid函数。 \(p=\frac{1}{1+e^{-x}}\) 虽然它的名字叫回归,但是其实还是一个分类的方法。

算法

R语言实现

人工神经网络–延伸到深度学习

算法

R语言实现

支持向量机SVM

算法

R语言实现

聚类分析

数据有几个变量,就形成几维空间,每个观测值是该空间的一个点。聚类分析就是根据点之间的疏密,远近,把它们自然分成不同的簇,聚类结果就是簇及其特征。

聚类分析,cluster analysis,简称为clustering. 是把一个数据对象划分成子集的过程。

每一个子集是一个簇,即cluster,使得簇中的对象彼此相似,但与其他簇中的对象不相似。

和分类不同,聚类属于无监督学习。

簇的类型有四种

  • 明显分离的簇
  • 基于中心的簇
  • 基于临近的簇
  • 基于密度的簇

聚类分析的算法模型,典型的主要有五大类。

  • 基于划分的方法:本课介绍,如k-means,等
  • 基于层次的方法:本课介绍
  • 基于密度的方法
  • 基于网格的方法
  • 基于模型的方法

距离计算的问题:

若特征均为数值型变量,常常采用闵科夫斯基距离(就是不同分量上的差别,然后再综合): \(\operatorname{dist}\left(x^{(i)}, x^{(j)}\right)=\left(\sum_{k=1}^{n}\left|x_{k}^{(i)}-x_{k}^{(j)}\right|^{p}\right)^{\frac{1}{p}}\) 其实闵科夫斯基距离是一组距离,当p = 2时,表示的欧式距离;当p = 1时,表示的是曼哈顿距离(即出租车距离,或阶梯距离)。

对于文本类数据,常采用余弦相似性: \(\text { similarity }\left(x^{(i)}, x^{(j)}\right)=\frac{x^{(i)} x^{(j)}}{\left[x^{(i)}\right]\left(x^{j}\right)}\) 在R中距离的计算:

若均为数值型变量,用的最多的是dist()实现;

对于混合类型的数据,可以采用cluster::daisy()来实现。

这个过程中,需要做数据标准化,消除不同量纲的影响。

  • min-max标准化: 极差法

  • Z-score标准化:

  • 正项序列的归一化:

R中实现,都是scale()函数。而且,阅读scale()函数的参数设置,可以发现,数据分析完之后,将标准化后的数据回归原来量纲,也是通过scale()函数实现。

综合而言,不过是,乘以散之,约以聚之,齐同以通之。

首先分析可行性,看数据是否聚类,就可以先计算霍普金斯统计量。

最后对聚类的结果进行模型评估:轮廓系数silhouette coefficient来评估聚类结果,综合考虑了凝聚性和分离性,计算公式也可以理解,是综合考虑了凝聚性和分离性,计算步骤如下:

  1. 对于第i个对象,计算它到所属簇中所有其他对象的平均举例,记做 a(i)
  2. 对于第i 个对象和所有不包含该对象的其他簇,计算该对象到各簇每个对象距离的平均值,并找到不同簇平均值中的最小值,记做b (i)
  3. 对于第i个对象,轮廓系数为:
\[s_{i}=\frac{b_{i}-a_{i}}{\max \left(a_{i}, b_{i}\right)}\]

可以计算所有对象轮廓系数的平局值,得到聚类效果的总体度量

cluster::silhouette() , compute or extract silhouette information

基于划分方法的聚类

基于划分的方法是聚类分析中最简单、最基本的版本:

试图发现用户指定个数(k)的簇(由质心代表)

目标函数用来评估划分的质量,使得簇内对象相似

目标函数– 误差平方和 SSE, sum of squared error \(S S E=\sum_{i-1}^{k} \sum_{x \in C} \operatorname{dist}\left(c_{i}, x\right)^{2}\) Ci 就是每一簇的一个质心。

这个目标函数试图使得生成的结果簇尽可能紧凑和独立。所以,聚类的目的是使这个目标函数SSE值尽可能小。

要通过蛮力的方法,枚举或者说遍历所有可能的划分,并计算相应的SSE值,即便是k - 2, 计算量仍特别大。解决问题的一个常用方法是k -Means方法。 算法原理以及R代码实现讲解如下:

基于层次方法的聚类

层次聚类,试图在不同层次上对数据集进行划分,通过树状图dengrogram进行展示结果(也称为谱系图)。

层次聚类的基本原理

基本凝聚层次聚类算法(自下而上的凝聚):

采用自下而上的聚合策略,初始每个样本为一个簇,然后每步找到距离最近的两个簇,并将它们融合,依次进行下去,直到所有样本在一个簇。

  1. 计算临近性矩阵
  2. repeat
    1. 合并最接近的两个簇
    2. 更新临近性矩阵,以反应新的簇和原来簇之间的临近性
  3. until 仅剩下一个簇。

所以,层次聚类关注的是簇之间的距离,由其所包含的点所定义,

最小距离:\(\operatorname{dis}_{\min }\left(C_{i}, C_{j}\right)=\operatorname{MIN}_{p \in C_{i}, q \in C_{j}} \operatorname{dist}(p, q)\) 最大距离:\(\operatorname{dist}_{\max }\left(C_{i}, C_{j}\right)=\operatorname{MAX}_{p \in C_{t}, q \in C_{j}} \operatorname{dist}(p, q)\)

平均距离:\(d_{\mathrm{avg}}\left(C_{i}, C_{j}\right)=\frac{1}{\left|C_{i} \| C_{j}\right|} \sum_{x \in C_{i}} \sum_{z \in C_{j}} \operatorname{dist}(x, z)\) 均值距离:\(\text { dist_min }\left(\boldsymbol{c}_{i}, \boldsymbol{c}_{j}\right)=\left|\boldsymbol{m}_{i}-\boldsymbol{m}_{j}\right|\)

算法实现:task view, cluster analysis & finite mixture models

hclust() from stats package

尝试一些学术创新:

比如将层次聚类和离群值检测结合起来,会有新的发现

层次发给我们的直觉:离群值不易于合并,即当他们最终被合并的时候,他们合并前所属 类的大小和他们被合并进去的类的大小相差应该很大。大多数情况下会在聚类的后期进行合并,通常是与一个更大的类进行合并。

转换成数学语言:若对象x参与第i次合并,令离群分值为: \(o f_{i}(x)=\max \left(0, \frac{\left|g_{y, i}\right|-\left|g_{x, l}\right|}{\left|g_{y, i}\right|+\left|g_{x, l}\right|}\right)\)