使用R進行描述性統計分析(連續性變量)
對於描述性統計來說,R可以實現的方法有很多,基礎自帶的有summary()
函數,還有其他packages,如Hmisc包,pastecs包,psych包提供了計算更多內容的函數。
基礎函數
在R中,我們經常使用summary()
函數來計算最大值、最小值、四分位數、均值、頻數等等。
data(mtcars)
myvars <- c("mpg", "hp", "wt")
summary(mtcars[myvars])
## --- output------
## NOT RUN:
> summary(mtcars[myvars])
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()
函數就可以得到我們想要的描述性統計量了。不過summary()
函數提供的統計量較少,有時候滿足不了我們的需求,那麼我們可以使用其他包中提供的函數來進行計算。
其他方法
Hmisc包
Hmisc包是一個包含了很多數據分析函數的包,包括樣本量大小的計算,圖標繪製,字符串操作,輸出爲LaTeX及HTML格式的文檔等等。在這裏可以查看更多詳細的信息:
Contains many functions useful for data analysis, high-level graphics, utility operations, functions for computing sample size and power, importing and annotating datasets, imputing missing values, advanced table making, variable clustering, character string manipulation, conversion of R objects to LaTeX and html code, and recoding variables.
在Hmisc包中的describe()
函數提供了數量,缺失值,唯一值的數量,平均數,分位數,**基尼平均值(Geni mean difference, Gmd)**以及五個最大值和最小值:
library(Hmisc)
describe(mtcars[myvars])
## --- output---
## NOT RUN
> describe(mtcars[myvars])
mtcars[myvars]
3 Variables 32 Observations
--------------------------------------------------------------------------------
mpg
n missing distinct Info Mean Gmd .05 .10
32 0 25 0.999 20.09 6.796 12.00 14.34
.25 .50 .75 .90 .95
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
32 0 22 0.997 146.7 77.04 63.65 66.00
.25 .50 .75 .90 .95
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
32 0 29 0.999 3.217 1.089 1.736 1.956
.25 .50 .75 .90 .95
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包
有時候我們想要知道標準差,值域,方差,平均數的95%置信區間,是否符合正態等等結果,那麼可能上邊的提供的方法無法滿足我們的需求。那麼可以使用pastecs包中的stat.desc()
函數來計算相關統計量。這個函數將返還一個數據框,這種格式的數據或許比起列表更加容易後續的輸出和操作。stat.desc()
函數的主要使用形式是:stat.desc(x,basic = TRUE,desc=TRUE,norm=FALSE,p=0.95)
,其中:
- x:一個數據框對象
- basic:默認爲TRUE,計算其中所有值、空值、缺失值的數量,以及最小值、最大值、值域,還有總和
- desc:默認爲TRUE,計算中位數、平均數、平均數的標準誤、平均數95%置信區間、方差、標準差以及變異係數
- norm:默認爲FALSE,計算正態分佈統計量,包括偏度和峯度以及它們的統計顯著差異和Shapiro-Wilk正態檢驗結果
library(pastecs)
stat.desc(mtcars[myvars], norm = TRUE, p = 0.95)
## ---output---
## NOT RUN
> stat.desc(mtcars[myvars], norm = TRUE, p = 0.95)
mpg hp wt
nbr.val 32.0000000 32.00000000 32.00000000
nbr.null 0.0000000 0.00000000 0.00000000
nbr.na 0.0000000 0.00000000 0.00000000
min 10.4000000 52.00000000 1.51300000
max 33.9000000 335.00000000 5.42400000
range 23.5000000 283.00000000 3.91100000
sum 642.9000000 4694.00000000 102.95200000
median 19.2000000 123.00000000 3.32500000
mean 20.0906250 146.68750000 3.21725000
SE.mean 1.0654240 12.12031731 0.17296847
CI.mean.0.95 2.1729465 24.71955013 0.35277153
var 36.3241028 4700.86693548 0.95737897
std.dev 6.0269481 68.56286849 0.97845744
coef.var 0.2999881 0.46740771 0.30412851
skewness 0.6106550 0.72602366 0.42314646
skew.2SE 0.7366922 0.87587259 0.51048252
kurtosis -0.3727660 -0.13555112 -0.02271075
kurt.2SE -0.2302812 -0.08373853 -0.01402987
normtest.W 0.9475647 0.93341934 0.94325772
normtest.p 0.1228814 0.04880824 0.09265499
psych包
在psych包中也提供了一個describe()
函數來計算一般統計量,它可以計算非缺失值的數量、平均數、標準差、中位數、截尾均值,絕對中位數、最小值、最大值、值域、偏度、峯度和平均值的標準誤:
library(psych)
describe(mtcars[myvars])
## ---output ---
## NOT RUN
> describe(mtcars[myvars])
vars n mean sd median trimmed mad min max range skew kurtosis
mpg 1 32 20.09 6.03 19.20 19.70 5.41 10.40 33.90 23.50 0.61 -0.37
hp 2 32 146.69 68.56 123.00 141.19 77.10 52.00 335.00 283.00 0.73 -0.14
wt 3 32 3.22 0.98 3.33 3.15 0.77 1.51 5.42 3.91 0.42 -0.02
se
mpg 1.07
hp 12.12
wt 0.17
利用sapply()
函數計算描述性統計量
如果對於上述方法提供的結果還不是很滿意,那麼怎麼辦?在這種情況下,我們可以考慮使用sapply()
函數來實現我們自定義的統計學描述。關於sapply()
函數,大家是否會回想起以前我們使用的那個lapply()
函數呢?通過查詢文檔(使用?sapply
)我們可以看到以下的一些信息:
‘lapply’ returns a list of the same length as ‘X’, each element of which is the result of applying ‘FUN’ to the corresponding element of ‘X’.
‘sapply’ is a user-friendly version and wrapper of ‘lapply’ by default returning a vector, matrix or, if ‘simplify = “array”’, an array if appropriate, by applying ‘simplify2array()’. ‘sapply(x, f, simplify = FALSE, USE.NAMES = FALSE)’ is the same as ‘lapply(x, f)’.
這些信息有些難以閱讀,但是初步看來,這倆個函數差不多,但是sapply()
函數是一個用戶友好版本,而且封裝了lapply()
函數,使其返還成向量,矩陣或者數組。不用在意那麼多細節,我們看看例子也許就能明白了。
首先,我們需要一個自建的函數來滿足我們所需要的統計量:
## -------------------------bulid a func------------------------
mystats <- function(x, na.omit = FALSE){
if(na.omit)
x <- x[!is.na(x)] # exculde the na data
m <- mean(x)
n <- length(x)
s <- sd(x)
skew <- sum((x - m) ^ 3 / s ^ 3) / n # Skewness
kurt <- sum((x - m) ^ 4 / s ^ 4) / n # Kurtosis
return(c(n = n, mean = m, stdev = s, skew = skew, kurtosis = kurt))
}
在這裏,我們創建了一個計算均數,數量,標準差,偏度(skewness),峯度(kurtosis)的統計量。接下來我們要對每一個變量進行這些統計量的計算:
sapply(mtcars[myvars], mystats)
## ---output
## NOT RUN
> sapply(mtcars[myvars], mystats)
mpg hp wt
n 32.000000 32.0000000 32.0000000
mean 20.090625 146.6875000 3.2172500
stdev 6.026948 68.5628685 0.9784574
skew 0.610655 0.7260237 0.4231465
kurtosis 2.627234 2.8644489 2.9772892
接下來,我們想驗證下對於sapply()
函數的理解是否正確,於是打算查看下sapply()
到底返還的是什麼類型的對象。想要查看他的結構,首先我們需要把對象進行保存,然後運用str()
函數去查看:
test <- sapply(mtcars[myvars], mystats)
str(test)
## ---output---
## NOT RUN
> str(test)
num [1:5, 1:3] 32 20.091 6.027 0.611 2.627 ...
- attr(*, "dimnames")=List of 2
..$ : chr [1:5] "n" "mean" "stdev" "skew" ...
..$ : chr [1:3] "mpg" "hp" "wt"
這裏我們可以看到他的結構是個二維的5*3的表格,其中所有的數據是num。如果熟悉R語言的數據結構,那麼二維的,且每一個元素都相同的表格我們把他定義爲矩陣(matrix)。當然我們可以使用is.matrix()
進行驗證:
is.matrix(test)
## ---output---
## NOT RUN
> is.matrix(test)
[1] TRUE
由此,我們可以確認對於sapply()
函數的理解大致是準確的:
sapply()
函數是一個用戶友好版本,而且封裝了lapply()
函數,使其返還成向量,矩陣或者數組。
分組計算
有時候,我們需要的不是計算總體的統計量,而是要計算不同組別的統計量,那麼上述的一些方法就不太適用了。我們需要一些其他的方法來實現這個需求。
我們可以使用R自帶的aggregate()
函數來計算分組的統計量:
aggregate(mtcars[myvars], by = list(am = mtcars$am), mean)
## ---output---
## NOT RUN
> aggregate(mtcars[myvars], by = list(am = mtcars$am), mean)
am mpg hp wt
1 0 17.14737 160.2632 3.768895
2 1 24.39231 126.8462 2.411000
這裏我們分別計算了自動擋(am = 1)組和手動擋(am = 0)組的mpg,hp,wt的均數。aggregate()
函數只能一次計算一個統計量,當需要計算多個統計量的時候需要重複使用,比較麻煩。因此我們需要用其他方式來實現一次多個統計量的計算。
dstatas <- function(x) sapply(x, mystats)
by(mtcars[myvars], mtcars$am, dstatas)
## ---output---
## NOT RUN
> by(mtcars[myvars], mtcars$am, dstatas)
mtcars$am: 0
mpg hp wt
n 19.00000000 19.00000000 19.0000000
mean 17.14736842 160.26315789 3.7688947
stdev 3.83396639 53.90819573 0.7774001
skew 0.01395038 -0.01422519 0.9759294
kurtosis 2.19682174 1.79030267 3.1415676
------------------------------------------------------------
mtcars$am: 1
mpg hp wt
n 13.00000000 13.000000 13.0000000
mean 24.39230769 126.846154 2.4110000
stdev 6.16650381 84.062324 0.6169816
skew 0.05256118 1.359886 0.2103128
kurtosis 1.54464800 3.563463 1.8262642
在這裏,dstatas <- function(x) sapply(x, mystats)
使用了簡易的函數寫法。使用by()
將數據集分爲自動擋和手動擋兩組,分別使用函數計算出各個統計量。
除了這種我們自建函數使用by()
函數來進行分組的統計量以外,我們可以使用一些包裏提供的方法來計算。
doBy包中的summaryBy()
函數提供了分組計算的功能:
library(doBy)
summaryBy(mpg + hp + wt ~ am, data = mtcars, FUN = mystats)
## ---output---
## NOT RUN
> summaryBy(mpg + hp + wt ~ am, data = mtcars, FUN = mystats)
am mpg.n mpg.mean mpg.stdev mpg.skew mpg.kurtosis hp.n hp.mean hp.stdev
1 0 19 17.14737 3.833966 0.01395038 2.196822 19 160.2632 53.90820
2 1 13 24.39231 6.166504 0.05256118 1.544648 13 126.8462 84.06232
hp.skew hp.kurtosis wt.n wt.mean wt.stdev wt.skew wt.kurtosis
1 -0.01422519 1.790303 19 3.768895 0.7774001 0.9759294 3.141568
2 1.35988586 3.563463 13 2.411000 0.6169816 0.2103128 1.826264
psych包中的describeBy()
函數可計算和describe()
相同的描述性統計量,按照一個或多個分組變量進行分層:
library(psych)
describeBy(mtcars[myvars], list(am = mtcars$am))
## ---output---
## NOT RUN
> describeBy(mtcars[myvars], list(am = mtcars$am))
Descriptive statistics by group
am: 0
vars n mean sd median trimmed mad min max range skew
mpg 1 19 17.15 3.83 17.30 17.12 3.11 10.40 24.40 14.00 0.01
hp 2 19 160.26 53.91 175.00 161.06 77.10 62.00 245.00 183.00 -0.01
wt 3 19 3.77 0.78 3.52 3.75 0.45 2.46 5.42 2.96 0.98
kurtosis se
mpg -0.80 0.88
hp -1.21 12.37
wt 0.14 0.18
------------------------------------------------------------
am: 1
vars n mean sd median trimmed mad min max range skew kurtosis
mpg 1 13 24.39 6.17 22.80 24.38 6.67 15.00 33.90 18.90 0.05 -1.46
hp 2 13 126.85 84.06 109.00 114.73 63.75 52.00 335.00 283.00 1.36 0.56
wt 3 13 2.41 0.62 2.32 2.39 0.68 1.51 3.57 2.06 0.21 -1.17
se
mpg 1.71
hp 23.31
wt 0.17
describeBy()
函數不允許使用任意指定的函數,所以普適性低,但是勝在於不用自己編寫函數,直接就能得出一般的描述性統計量。
輸出一般統計描述的表格(連續性變量)
上述的這些方法我們很多都用於數據清洗完的一般性探索中。讓我們更加清楚的認清數據的結構,分佈等等,爲後期的統計建模等等做準備。那麼,我們常常見到的醫學論文中Table 1的一般統計學描述該如何輸出呢?這裏我們主要想輸出的是連續性變量的平均數和正負標準差(如果符合正態)或者是中位數和四分位數(如果不符合正態)。這裏,我們使用自建函數來實現這部分功能:
## 該函數用於一般計數資料的統計學描述
## 當資料符合正態時,使用均數和方差
## 當資料不符合正態時,使用中位數和四分位
library(nortest) # 載入進行正態性檢驗的包
gl.num.anysis <- function(varnames, source) {
# 第一部分:進行正態性檢驗,得到p值
value <- as.vector(as.matrix(source[, varnames]))
pvalue <- lillie.test(value)$p.value
# 判斷p值是否大於0.05,如果大於0.05,爲符合正態,並且用‘**’表示符合正態
if (pvalue > 0.05) {
Mean <- round(mean(value), 4)
SD <- round(sd(value), 4)
Mean_value <- paste0(Mean,
paste0('(',paste(Mean+SD,Mean-SD,
sep = '-'),')'),
'**')
table <- data.frame('Characteristics' = varnames,
'Value' = Mean_value)
return(table)
}
# 不符合正態就使用中位數和四分位數,並且用‘*’表示不符合正態
else {
Median <- paste0(round(median(value),4),
paste0('(',
paste(round(quantile(value,probs=0.25),4),
round(quantile(value,probs=0.75),4),
sep = "-"),
')'),'*')
table <- data.frame('Characteristics' = varnames,
'Value' = Median)
return(table)
}
}
這裏,我不對這個自建函數作出過多的解釋,主要原理就是首先判斷是否符合正態,然後進行分別的運算,使用paste0()
函數進行字符串的操作,黏貼。這裏給出下我們示例計算的結果:
gl.num.anysis(varnames = myvars, source = mtcars)
## ---output---
## NOT RUN
> gl.num.anysis(varnames = myvars, source = mtcars)
Characteristics Value
1 mpg 19.2(3.69-95.5)*
2 hp 19.2(3.69-95.5)*
3 wt 19.2(3.69-95.5)*
注意,這裏存在錯誤。因爲我們在前面使用stat.desc()
函數計算的時候,進行了正態性檢驗,發現:
> stat.desc(mtcars[myvars], norm = TRUE, p = 0.95)
mpg hp wt
normtest.p 0.1228814 0.04880824 0.09265499
可以看到,mpg和wt是符合正態的,爲什麼在我們上邊的結果卻判斷爲了不符合正態,而且每一個值都是19.2。這裏的主要原因是函數中的第二步:value <- as.vector(as.matrix(source[, varnames]))
。
這一步將提取出一個變量的數據,並且轉換爲向量,如果我們使用多個變量,並將其轉化爲向量,我們會得到這樣的結果:
as.vector(as.matrix(mtcars[, myvars]))
## ---output---
## NOT RUN
> as.vector(as.matrix(mtcars[, myvars]))
[1] 21.000 21.000 22.800 21.400 18.700 18.100 14.300 24.400 22.800
[10] 19.200 17.800 16.400 17.300 15.200 10.400 10.400 14.700 32.400
[19] 30.400 33.900 21.500 15.500 15.200 13.300 19.200 27.300 26.000
[28] 30.400 15.800 19.700 15.000 21.400 110.000 110.000 93.000 110.000
[37] 175.000 105.000 245.000 62.000 95.000 123.000 123.000 180.000 180.000
[46] 180.000 205.000 215.000 230.000 66.000 52.000 65.000 97.000 150.000
[55] 150.000 245.000 175.000 66.000 91.000 113.000 264.000 175.000 335.000
[64] 109.000 2.620 2.875 2.320 3.215 3.440 3.460 3.570 3.190
[73] 3.150 3.440 3.440 4.070 3.730 3.780 5.250 5.424 5.345
[82] 2.200 1.615 1.835 2.465 3.520 3.435 3.840 3.845 1.935
[91] 2.140 1.513 3.170 2.770 3.570 2.780
所有三個變量全部轉換爲了一個向量!這顯然會得到一個錯誤的結果。
那麼我們應該怎麼做?
正確的做法應該是讓每一個變量運行一遍這個函數,這裏我們將使用我們的老朋友lapply()
函數來解決:
library(plyr)
ldply(lapply(myvars, gl.num.anysis, mtcars))
## ---output---
## NOT RUN
> ldply(lapply(myvars, gl.num.anysis, mtcars))
Characteristics Value
1 mpg 20.0906(26.1175-14.0637)**
2 hp 123(96.5-180)*
3 wt 3.2172(4.1957-2.2387)**
這樣,我們就得到了正確的結果。
至於想要獲得不同分組的統計結果,這裏暫時還沒有完成,需要大家將數據集切分,同時得到三個表,進行合併,輸出,並且在WORD裏修改。以後有空在優化吧~
參考文獻
- R語言實戰,作者:卡巴科弗 ,ISBN: 9787115299901