使用R進行描述性統計分析(連續性變量)

使用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

可以看到,mpgwt是符合正態的,爲什麼在我們上邊的結果卻判斷爲了不符合正態,而且每一個值都是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裏修改。以後有空在優化吧~

參考文獻

  1. R語言實戰,作者:卡巴科弗 ,ISBN: 9787115299901
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章