疫情居家起间,学习了北京邮电大学 艾新波老师的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章 相随相伴,谓之关联
数据分析简史(数据分析的经典算法出现很久了)
- 1805年,linear regression
- 1936年,linear discriminant analysis
- 1951年, k nearest neighbors algorithm
- 1957年, k means
- 1958年, logistic regression
- 1984年, classification and regression tree
- 1986年, back prepagation
- 1993年, Apriori,关联规则的算法,
- 1995年, support vector machine
- 2001年, random forest
- 2006年, deep learning
- 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同时出现的支持度,则容易导出对应的关联规则。
换言之,关联规则的挖掘可以分为两个步骤
- 找出所有频繁项集,满足最小支持度。避免野蛮搜索,基于支持度对候选集进行剪枝。
- 由频繁项集产生强关联规则,满足最小置信度。避免野蛮搜索,基于置信度对规则进行剪枝。
首先看第一步骤,挖频繁项集。
用到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中所有测试样本预测其类标号值
-
for 每个测试样本 z = (X’, y’)
-
计算z和每个训练样本(X, y)之间的距离d (X, X’)
-
选择离z最近的k最近邻集合 Sz 包含于S
-
多数表决(argmax就是一个表决函数) \(y^{\prime}=\underset{v}{\operatorname{argmax}} \sum_{\left(x_{i} y_{i}\right) \in D_{x}} I\left(v=y_{i}\right)\)
-
-
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棵决策树
- 使用有放回抽样生成训练集Di,每个Di都是D的一个自助样本,某些元组在Di中出现多次,而某些元组不出现
- 每个自助样本集生长为单棵分类树(随机选取分裂属性集):设F是用来在每个节点决定划分的属性数,其中F远小于可用属性数。为构造决策树分类器Mi,在每个节点随机选择F个属性作为该节点划分的候选属性。使用CART方法增长树,增长到最大规模且不剪枝
- 采用简单多数投票法得到随机森林的结果
随机森林的优点,体现在随机选择“不同经验”的专家(即训练集是不同行),然后专家”从不同的视角“进行生成决策树(即每次使用了不同的列即不同的属性),最后进行简单集成,最后的模型性能提升十分显著。
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来评估聚类结果,综合考虑了凝聚性和分离性,计算公式也可以理解,是综合考虑了凝聚性和分离性,计算步骤如下:
- 对于第i个对象,计算它到所属簇中所有其他对象的平均举例,记做 a(i)
- 对于第i 个对象和所有不包含该对象的其他簇,计算该对象到各簇每个对象距离的平均值,并找到不同簇平均值中的最小值,记做b (i)
- 对于第i个对象,轮廓系数为:
可以计算所有对象轮廓系数的平局值,得到聚类效果的总体度量
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进行展示结果(也称为谱系图)。
层次聚类的基本原理
基本凝聚层次聚类算法(自下而上的凝聚):
采用自下而上的聚合策略,初始每个样本为一个簇,然后每步找到距离最近的两个簇,并将它们融合,依次进行下去,直到所有样本在一个簇。
- 计算临近性矩阵
- repeat
- 合并最接近的两个簇
- 更新临近性矩阵,以反应新的簇和原来簇之间的临近性
- 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)\)