前面简要介绍了R语言的基本数据结构和基础图形,本节将简单介绍如何得到数据的描述性统计分析,以及进一步了解列联表(也即分组)。
1、描述性统计分析
本节将关注分析连续型变量的中心趋势、变化性和分布形状的方法。以mtcars数据中的mpg、hp和wt为例子对常用的统计方法进行说明。
对于数据的基础性统计分析,R中有许多方法,从R自带的summary()
函数,到三方包中的方法,我们将逐步进行介绍。
> df <- mtcars[c('mpg','hp','wt')];head(df)
mpg hp wt
Mazda RX4 21.0 110 2.620
Mazda RX4 Wag 21.0 110 2.875
Datsun 710 22.8 93 2.320
Hornet 4 Drive 21.4 110 3.215
Hornet Sportabout 18.7 175 3.440
Valiant 18.1 105 3.460
> summary(df)
mpg hp wt
Min. :10.40 Min. : 52.0 Min. :1.513
1st Qu.:15.43 1st Qu.: 96.5 1st Qu.:2.581
Median :19.20 Median :123.0 Median :3.325
Mean :20.09 Mean :146.7 Mean :3.217
3rd Qu.:22.80 3rd Qu.:180.0 3rd Qu.:3.610
Max. :33.90 Max. :335.0 Max. :5.424
如上所见,summary()
函数提供了最小值、最大值、四分位数和数值型变量的均值,以及因子向量和逻辑型向量的频数统计。你可以使用apply()函数或sapply()函数计算所选择的任意描述性统计量。函数fivenum()可返回图基五数总括(Tukey’s five-number summary,即最小值、下四分位数、中位数、上四分位数和最大值)。
mystats<-function(x,na.omit=FALSE){
if(na.omit){
x<-x[!is.na(x)]
}
m<-mean(x)
n<-length(x)
s<-sd(x)
skew<-sum((x-m)^3/s^3)/n
kurt<-sum((x-m)^4/s^4)/n-3
return(c(n=n,mean=m,stdenv=s,skew=skew,kurtosis=kurt))
}
> apply(df, 2, mystats)
mpg hp wt
n 32.000000 32.0000000 32.00000000
mean 20.090625 146.6875000 3.21725000
stdenv 6.026948 68.5628685 0.97845744
skew 0.610655 0.7260237 0.42314646
kurtosis -0.372766 -0.1355511 -0.02271075
三方包Hmsic、pastecs和psych中都提供了计算描述性统计的函数。Hmisc包中的describe()
函数可返回变量和观测的数量、缺失值和唯一值的数目、平均值、分位数,以及五个最大的值和五个最小的值。
> library(Hmisc)
> describe(df)
df
3 Variables 32 Observations
------------------------------------------------------------------------------------------------------------------------------------------
mpg
n missing distinct Info Mean Gmd .05 .10 .25 .50 .75 .90 .95
32 0 25 0.999 20.09 6.796 12.00 14.34 15.43 19.20 22.80 30.09 31.30
lowest : 10.4 13.3 14.3 14.7 15.0, highest: 26.0 27.3 30.4 32.4 33.9
------------------------------------------------------------------------------------------------------------------------------------------
hp
n missing distinct Info Mean Gmd .05 .10 .25 .50 .75 .90 .95
32 0 22 0.997 146.7 77.04 63.65 66.00 96.50 123.00 180.00 243.50 253.55
lowest : 52 62 65 66 91, highest: 215 230 245 264 335
------------------------------------------------------------------------------------------------------------------------------------------
wt
n missing distinct Info Mean Gmd .05 .10 .25 .50 .75 .90 .95
32 0 29 0.999 3.217 1.089 1.736 1.956 2.581 3.325 3.610 4.048 5.293
lowest : 1.513 1.615 1.835 1.935 2.140, highest: 3.845 4.070 5.250 5.345 5.424
------------------------------------------------------------------------------------------------------------------------------------------
pastecs包中有一个名为stat.desc()
的函数,可用来计算种类繁多的描述性统计量。使用方法为stat.desc(x,basic=TRUE,desc=TRUE,norm=FALSE,p=0.95)
,其中的x是一个数据框或时间序列。若basic=TRUE(默认值),则计算其中所有值、空值、缺失值的数量,以及最小值、最大值、值域,还有总和。若desc=TRUE(同样也是默认值),则计算中位数、平均数、平均数的标准误、平均数置信度为95%的置信区间、方差、标准差以及变异系数。最后,若norm=TRUE(不是默认的),则返回正态分布统计量,包括偏度和峰度(以及它们的统计显著程度)和Shapiro–Wilk正态检验结果。这里使用了p值来计算平均数的置信区间(默认置信度为0.95)。
> options(digits=2)
> stat.desc(df)
mpg hp wt
nbr.val 32.0 32.00 32.00
nbr.null 0.0 0.00 0.00
nbr.na 0.0 0.00 0.00
min 10.4 52.00 1.51
max 33.9 335.00 5.42
range 23.5 283.00 3.91
sum 642.9 4694.00 102.95
median 19.2 123.00 3.33
mean 20.1 146.69 3.22
SE.mean 1.1 12.12 0.17
CI.mean.0.95 2.2 24.72 0.35
var 36.3 4700.87 0.96
std.dev 6.0 68.56 0.98
coef.var 0.3 0.47 0.30
psych包中也有一个describe()
函数,它可以计算非缺失值的数量、平均数、标准差、中位数、截尾均值、绝对中位差、最小值、最大值、值域、偏度、峰度和平均值的标准误。
> library(psych)
载入程辑包:‘psych’
The following object is masked from ‘package:Hmisc’:
describe
The following objects are masked from ‘package:ggplot2’:
%+%, alpha
### 这里的describe()函数被重载了,如果想使用Hmisc中的describe()函数,可使用Hmisc::describe(df)
> describe(df)
vars n mean sd median trimmed mad min max range skew kurtosis se
mpg 1 32 20.1 6.03 19.2 19.7 5.41 10.4 33.9 23.5 0.61 -0.37 1.07
hp 2 32 146.7 68.56 123.0 141.2 77.10 52.0 335.0 283.0 0.73 -0.14 12.12
wt 3 32 3.2 0.98 3.3 3.1 0.77 1.5 5.4 3.9 0.42 -0.02 0.17
2、分组计算描述性统计量
在前面我们已经使用aggregate()
函数进行简单的分组计算,aggregate()
的缺点是只能使用平均数、标准差这样的单返回值函数。
若想一次性返回多个统计量,可使用by()
函数,格式为:by(data, Indices, FUN),其中data是一个数据框或矩阵,indices是一个因子或因子组成的列表,定义了分组,FUN则是任意函数。
### 使用aggregate()函数分组并获取单个返回值
> aggregate(df,by=list(mtcars$am),mean)
Group.1 mpg hp wt
1 0 17 160 3.8
2 1 24 127 2.4
###使用by()函数分组并返回多个统计量
> dstats <- function(x)sapply(x, mystats)
> by(df, mtcars$am, dstats)
mtcars$am: 0
mpg hp wt
n 19.000 19.000 19.00
mean 17.147 160.263 3.77
stdenv 3.834 53.908 0.78
skew 0.014 -0.014 0.98
kurtosis -0.803 -1.210 0.14
-------------------------------------------------------------------------------------------------------
mtcars$am: 1
mpg hp wt
n 13.000 13.00 13.00
mean 24.392 126.85 2.41
stdenv 6.167 84.06 0.62
skew 0.053 1.36 0.21
kurtosis -1.455 0.56 -1.17
###这里书中说的dstats()调用了mystats()函数,将其应用于数据框中的每一列中,在通过by()函数可得到am中每一水平的概括统计量。
###但是个人认为应该是先分类然后再应用统计函数才对。
doBy包和psych包同样提供了分祖计算统计量的函数。doBy包中的summaryBy()
函数使用方法为:summaryBy(formula, data=dataframe, FUN=function)
,其中formula接受以下格式:var1+var2+var3+...varN ~ groupvar1+groupvar2+...+groupvarN
,在~左侧的变量(varN)是需要分析的数值型变量,而右侧的变量(groupvarN)是类别型的分组变量。 function可为任何内建或用户自编的R函数。
> library('doBy')
> summaryBy(mpg+hp+wt~am,data=mtcars,FUN=mystats)
am mpg.n mpg.mean mpg.stdenv mpg.skew mpg.kurtosis hp.n hp.mean hp.stdenv hp.skew hp.kurtosis wt.n wt.mean wt.stdenv wt.skew
1 0 19 17 3.8 0.014 -0.8 19 160 54 -0.014 -1.21 19 3.8 0.78 0.98
2 1 13 24 6.2 0.053 -1.5 13 127 84 1.360 0.56 13 2.4 0.62 0.21
wt.kurtosis
1 0.14
2 -1.17
###########
> library(psych)
> describeBy(df)
vars n mean sd median trimmed mad min max range skew kurtosis se
mpg 1 32 20.1 6.03 19.2 19.7 5.41 10.4 33.9 23.5 0.61 -0.37 1.07
hp 2 32 146.7 68.56 123.0 141.2 77.10 52.0 335.0 283.0 0.73 -0.14 12.12
wt 3 32 3.2 0.98 3.3 3.1 0.77 1.5 5.4 3.9 0.42 -0.02 0.17
从上面可以看到doBy中的summaryBy()
函数返回结果的可读性比R自带by()
函数差,但其用起来较为简单。
psych包中的describeBy()
函数可计算和describe
相同的描述性统计量,只是按照一个或多个分组变量分层。但describeBy()
函数不允许指定任意函数,所以它的普适性较低。若存在一个以上的分组变量,可使用list(groupvar1, groupvar2, ... , groupvarN)来表示它们。
最后,在前面介绍分组方法时,使用过reshape2包,借助reshape2包可以灵活的按组导出描述性统计量(如我前面所言,个人感觉这个包有点难以理解)。但是reshape2相比于reshape有一个很大的缺点,那就是他的聚合函数只能有一个返回值(虽然reshape2比reshape效率高),就好比aggregate
与by
。他的使用步骤主要如下:
(1)融合数据框: dfm <- melt(dataframe, measure.vars=y, id.vars=g)
,其中的dataframe包含着数据, y是一个向量,指明了要进行概述的数值型变量(默认使用所有变量),而g是由一个或多个分组变量组成的向量;
(2)重铸数据:cast(dfm, groupvar1 + groupvar2 + ... + variable ~ . , FUN)
, 分组变量以+号分隔,这里的variable只取其字面含义(即仅表示重铸后数据框中的变量variable),而FUN是一个任意函数。reshape2使用的是dcast()
函数,只能有一个返回值,而cast函数中却允许多个返回值。
下面使用mtcars
数据看看reshape分组聚合的强大之处。
> library(reshape)
载入程辑包:‘reshape’
The following objects are masked from ‘package:reshape2’:
colsplit, melt, recast
### rehape2和reshape包的功能和函数用法基本相同,除了上面所说的重铸的统计返回值数量
> dfm<-melt(mtcars, measure.vars=c('mpg','hp','wt'),id.vars=c('am','cyl'))
> head(dfm)
am cyl variable value
1 1 6 mpg 21.0
2 1 6 mpg 21.0
3 1 4 mpg 22.8
4 0 6 mpg 21.4
5 0 8 mpg 18.7
6 0 6 mpg 18.1
> options(digits=2);cdf<-cast(dfm, am+cyl+variable~., mystats);head(cdf)
am cyl variable n mean stdenv skew kurtosis
1 0 4 mpg 3 22.9 1.45 0.069 -2.3
2 0 4 hp 3 84.7 19.66 -0.380 -2.3
3 0 4 wt 3 2.9 0.41 -0.381 -2.3
4 0 6 mpg 4 19.1 1.63 0.482 -1.9
5 0 6 hp 4 115.2 9.18 -0.094 -2.3
6 0 6 wt 4 3.4 0.12 -0.735 -1.7
3、频数表和列联表
本部分将着眼于类别型变量的频数表和列联表。范例中的数据来源于vcd包中的Arthritis数据集,这份数据展示了一项关于风湿性关节炎新疗法的双盲临床试验结果。
R中提供了多种用于创建频数表和列连表的方法,主要常用的函数如下表:
函数 | 描述 |
---|---|
table(var1, var2, var3,...,varN) | 使用N个类别型变量(因子)创建一个N维列联表 |
xtabs(formula, data) | 根据一个公式和一个矩阵或数据框创建一个N维列联表 |
pro.table(table, margins) | 依margins定义的边际列表将表中条目表示为分数形式 |
margin.table(table, margins) | 依margins定义的边际列表计算表中条目的和 |
addmargins(table,margins) | 将概述边margins(默认是求和结果)放入表中 |
ftable(table) | 创建一个紧凑的‘平铺式’列联表 |
接下来,我们将逐个使用以上函数来探索类别型变量。首先观察简单的频率表,然后是二维列联表,最后是多维列联表。
3.1 一维列联表
一维列联表可直接使用table()
生成。
> head(Arthritis)
ID Treatment Sex Age Improved
1 57 Treated Male 27 Some
2 46 Treated Male 29 None
3 77 Treated Male 30 None
4 17 Treated Male 32 Marked
5 36 Treated Male 46 Marked
6 23 Treated Male 58 Marked
### 查看治疗效果
> mytable <- table(Arthritis$Improved)
> mytable
None Some Marked
42 14 28
### 产看治疗效果的比例
> prop.table(mytable)
None Some Marked
0.50 0.17 0.33
> prop.table(mytable)*100
None Some Marked
50 17 33
###求和
> margin.table(mytable)
[1] 84
###同样求和,这里将结果加入了表中
> addmargins(mytable)
None Some Marked Sum
42 14 28 84
> class(mytable)
[1] "table
从上面可以看到,50%的患者都得到了改善。
3.2 二维列联表
创建二维列联表有两种方式,使用table(A,B)
或xtabs()
。xtabs()
的使用方式较为复杂,格式为:xtabs(~A+B, data=mydata)
,其中的mydata是一个矩阵或数据框。总的来说,要进行交叉分类的变量应出现在公式的右侧(即符号的右方),以+作为分隔符。若某个变量写在公式的左侧,则其为一个频数向量(在数据已经被表格化时很有用),对其进行求和运算。简言之,formula公式右边的应该都是类型变量,左边可选择加入数值型变量。
另外值得注意的一点是,table()
函数是默认忽略NA的,若需要将NA作为一类,使用useNA='ifany'
。
### 使用table()创建二维列联表
> table(Arthritis$Treatment,Arthritis$Improved)
None Some Marked
Placebo 29 7 7
Treated 13 7 21
### 使用xtabs()创建二维列联表
> mytable <- xtabs(~Treatment+Improved, Arthritis); mytable
Improved
Treatment None Some Marked
Placebo 29 7 7
Treated 13 7 21
### 生成边际频数,其中后面的1,2指代xtabs()的formula中变量的下标,
### 如 ~Treatment+Improved 中1指的是第一个变量Treatment,2指的是第二个变量Improved
> margin.table(mytable, 1); margin.table(mytable,2)
Treatment
Placebo Treated
43 41
Improved
None Some Marked
42 14 28
### 生成边际比例
> prop.table(mytable,1)*100;prop.table(mytable,2)*100
Improved
Treatment None Some Marked
Placebo 67 16 16
Treated 32 17 51
Improved
Treatment None Some Marked
Placebo 69 50 25
Treated 31 50 75
### 查看每个单元格所占比例
> prop.table(mytable)
Improved
Treatment None Some Marked
Placebo 0.345 0.083 0.083
Treated 0.155 0.083 0.250
### 添加边际和
> addmargins(mytable)
Improved
Treatment None Some Marked Sum
Placebo 29 7 7 43
Treated 13 7 21 41
Sum 42 14 28 84
######## 接下来我们再看看,在formula左边加入一个频数变量(这里将Age作为一个频数变量)的结果
> xtabs(Age~Treatment+Improved, Arthritis)
Improved
Treatment None Some Marked
Placebo 1439 402 403
Treated 648 397 1193
### 可以看到关联列表的行列变量没变,但是其中的数值变了,这些数值代表什么意思呢?其实是每一个分类的和
### 使用aggregate函数可以简单实现相同的结果
> aggregate(Arthritis$Age,by=list(Treatment=Arthritis$Treatment,Improvement=Arthritis$Improved),sum)
Treatment Improvement x
1 Placebo None 1439
2 Treated None 648
3 Placebo Some 402
4 Treated Some 397
5 Placebo Marked 403
6 Treated Marked 1193
除了R中自带的table()
类函数外,还可以使用三方包gmodels中的CrossTable()
函数创建二维列联表。
> library(gmodels)
> CrossTable(Arthritis$Treatment,Arthritis$Improved)
Cell Contents
|-------------------------|
| N |
| Chi-square contribution |
| N / Row Total |
| N / Col Total |
| N / Table Total |
|-------------------------|
Total Observations in Table: 84
| Arthritis$Improved
Arthritis$Treatment | None | Some | Marked | Row Total |
--------------------|-----------|-----------|-----------|-----------|
Placebo | 29 | 7 | 7 | 43 |
| 2.616 | 0.004 | 3.752 | |
| 0.674 | 0.163 | 0.163 | 0.512 |
| 0.690 | 0.500 | 0.250 | |
| 0.345 | 0.083 | 0.083 | |
--------------------|-----------|-----------|-----------|-----------|
Treated | 13 | 7 | 21 | 41 |
| 2.744 | 0.004 | 3.935 | |
| 0.317 | 0.171 | 0.512 | 0.488 |
| 0.310 | 0.500 | 0.750 | |
| 0.155 | 0.083 | 0.250 | |
--------------------|-----------|-----------|-----------|-----------|
Column Total | 42 | 14 | 28 | 84 |
| 0.500 | 0.167 | 0.333 | |
--------------------|-----------|-----------|-----------|-----------|
CrossTable()
结果较为直观,而且已经添加了边际和和边际频率。CrossTable()
函数还有很多选项,可以做许多事情:计算(行、列、单元格)的百分比;指定小数位数;进行卡方、 Fisher和McNemar独立性检验;计算期望和(皮尔逊、标准化、调整的标准化)残差;将缺失值作为一种有效值;进行行和列标题的标注;生成SAS或SPSS风格的输出。
3.3 多维列联表
table()和xtabs()都可以基于三个或更多的类别型变量生成多维列联。margin.table()、prop.table()和addmargins()函数可以自然地推广到高于二维的情况。另外, ftable()函数可以以一种紧凑而吸引人的方式输出多维列联表。
### 使用table()创建多维列联表
> with(Arthritis,table(Treatment, Sex, Improved))
, , Improved = None
Sex
Treatment Female Male
Placebo 19 10
Treated 6 7
, , Improved = Some
Sex
Treatment Female Male
Placebo 7 0
Treated 5 2
, , Improved = Marked
Sex
Treatment Female Male
Placebo 6 1
Treated 16 5
### 使用xtabs创建多维列联表
> mytable <- xtabs(~Treatment+Sex+Improved, data=Arthritis)
> mytable
, , Improved = None
Sex
Treatment Female Male
Placebo 19 10
Treated 6 7
, , Improved = Some
Sex
Treatment Female Male
Placebo 7 0
Treated 5 2
, , Improved = Marked
Sex
Treatment Female Male
Placebo 6 1
Treated 16 5
### 使用ftable()对三维列表输出格式进行美化
> ftable(mytable)
Improved None Some Marked
Treatment Sex
Placebo Female 19 7 6
Male 10 0 1
Treated Female 6 5 16
Male 7 2 5
> margin.table(mytable,1)
Treatment
Placebo Treated
43 41
4、小结
本节我们介绍了连续型数值变量和类别型变量的多种基本统计分析方法,在此对其进行一个小结,推荐一下个人认为最好用的方法。
(1)对于连续型数值变量的基本统计分析,可利用R内置的summary()
函数查看数据的描述性统计量(如均值,极值,方差)等,若有特定的要求,可使用apply()
、sapply()
函数通过自定义函数返回想要的结果。此外三方包Hmisc、pastecs、psych中也都提供了不错的统计函数。就输出结果的直观性和功能来说,查看基本统计分析推荐的方法为内置summary()
函数和pastecs包中的stat.desc()
函数。
(2)对于分组R同样提供了多种方法,如果只想对分组结果查看单个统计分析值(即作用函数只有单个返回值),aggregate()
无疑是最佳选择,简单快速。若想对分组结果进行处理返回多个统计值,R同样有多种方法,这里从输出直观性以及使用简单来说,首推的reshape
包(reshape2包无法返回多个统计分析值,因此放弃),其次为doBy包中的summaryBy()
函数和内置的by()
函数。
(3)对于类别型变量的分析,一般使用table()
和xtabs()
即可满足要求,ftable()
能够是多维关联列表的输出更为美观。若想进行更高级的分析,则可能需要用到三方包gmodels的CrossTable()
函数。
参考:
R语言实战.