2017年12月4日 星期一

R | ggplot: summary & error bar

這篇要講的應該是很常會用到的,就是加 error bar。

課堂講義請看這:R Graphics with Ggplot2 - Day 3

這邊我們要用到檔案資料 diamonds。

library(ggplot2)
library(dplyr)
data(diamonds)

我們先用 xtabs() 來看檔案中 color 的分佈情形。(xtabs() 的功能請看這篇。)

xtabs(~ color, diamonds)

Table 1
color
    D     E     F     G     H     I     J
 6775  9797  9542 11292  8304  5422  2808 

下面用 bar graph 看分佈,然後和上面做比對。

ggplot(diamonds, aes(color)) + geom_bar()

也可以分開設:(這個部分可以參考這篇會比較清楚)

d <- ggplot(diamonds, aes(color))
d + geom_bar()

#1 (color only)


接著,來看看每種 color 的價格,這裡可以用 by() function 來看每種鑽石顏色的價格總整理(summary)。

語法:by(response var, predictor var, summary)

這裡的 predictor variable (又稱 explanatory or independent variable)通常是個 factor (不是數字),會放在圖的 X-axis,response (or dependent) variable 指的是對應 factor 出現的反應,因此通常是放在圖的 Y-axis。

by(diamonds$price, diamonds$color, summary)

Table 2
diamonds$color: D
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
    357     911    1838    3170    4214   18690
----------------------------------------------------------
diamonds$color: E
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
    326     882    1739    3077    4003   18730
----------------------------------------------------------
diamonds$color: F
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
    342     982    2344    3725    4868   18790
----------------------------------------------------------
diamonds$color: G
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
    354     931    2242    3999    6048   18820
----------------------------------------------------------
diamonds$color: H
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
    337     984    3460    4487    5980   18800
----------------------------------------------------------
diamonds$color: I
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
    334    1120    3730    5092    7202   18820
----------------------------------------------------------
diamonds$color: J
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
    335    1860    4234    5324    7695   18710 

也可以用 tapply() 功能來看。

tapply(diamonds$price, diamonds$color, summary)

$D
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
    357     911    1838    3170    4214   18690

$E
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
    326     882    1739    3077    4003   18730

$F
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
    342     982    2344    3725    4868   18790

$G
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
    354     931    2242    3999    6048   18820

$H
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
    337     984    3460    4487    5980   18800

$I
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
    334    1120    3730    5092    7202   18820

$J
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
    335    1860    4234    5324    7695   18710 

如果只想看平均值,就把 summary 改成 mean。

tapply(diamonds$price, diamonds$color, mean)

       D        E        F        G        H        I        J
3169.954 3076.752 3724.886 3999.136 4486.669 5091.875 5323.818

在圖裡,可以用 summary_bin 這個功能來看,語法是:stat = "summary_bin"

或是:stat = "summary" (這兩個是一樣的)

summary_bin 裡面的功能有:fun.data, fun.y, fun.ymax (設最大值), fun.ymin (設最小值)

ggplot(diamonds, aes(color, price)) +
       geom_bar(stat = "summary_bin", fun.y = "mean")

在上面的語法裡,Y-axis 雖然已經設好是 price,但你需要在 geom_bar() 裡面的設定 Y-axis 是 price 的平均值(mean)或是中間值(median)等等,設定的語法是:fun.y = 

也可以用上面的已設定好的 d <- ggplot(diamonds, aes(color)) 把上面的語法改成下面這樣:(上面的語法是把 Y-axis 設在 ggplot() 裡面,但是 d 裡面只設了 X-axis,所以在下面的語法中,需要把 Y-axis 設在 geom_bar() 裡面。)

d + geom_bar(aes(y = price),
             stat = "summary_bin", fun.y = "mean")

#2 (color vs. price mean)


可以把圖和上面的 summary 表格做比較,看畫出來的是不是 mean。(和上面不同的是表一和圖一的指的是每種顏色的鑽石有幾個,所以圖 #1 的 Y-axis 是 count,而表二和圖二不只是每種顏色有幾個,而是每種鑽石顏色的價格,所以它的 Y-axis 指的不只是價格,還要指定是每種鑽石顏色的平均價格,還是每鑽石顏色其價格的中間值等等。)

下面來畫看看 median 的圖。

d + geom_bar(aes(y = price),
             stat = "summary_bin", fun.y = "median")

#3 (color vs. price median)


除了用 geom_bar(),我們也可以直接用 stat_summary_bin()stat_summary() 的功能。

stat_summary: summarise y values at distinct x values.

ggplot(diamonds, aes(color, price)) +
       stat_summary_bin(fun.y = "mean", geom = "bar")

或是用上面設好的 d。stat_summary_bin() 和 stat_summary() 是一樣的東西,上面和下面的這兩個語法畫出來的圖和上面用 geom_bar() 畫出來的 mean 那張圖是一樣的(圖 #2)。

d + stat_summary(aes(y = price),
                 fun.y = "mean", geom = "bar")

我們也可以畫點圖,用 geom_point() 就是下面那樣:

ggplot(diamonds, aes(color, price)) +
       geom_point(stat = "summary_bin", fun.y = "mean")

#4


上圖的 Y-axis 不是從 0 開始,而是從三千,我們可以把它設成從 0 開始。下面用上面設好的 d 和 stat_summary來畫圖,但基本上跟上面是一樣的,只是多了 ylim() 的語法。

ylim(min, max): Y-axis 的最小值和最大值

在下面的語法中,Y-axis 的最小值我們設為 0,最大值沒設,用 NA。

d + stat_summary(aes(y = price),
                 fun.y = mean, geom = "point") +
    ylim(0, NA)

#5


可以看到上圖的 Y-axis 是從 0 開始,而不是像圖 #4 那樣從三千開始。

接下來,試試在圖中加個 error bar,有四種方法。

注意,下面四種的預設都是 standard error (SE),除非你有指定另外的算法在裡面。

1. geom_pointrange(): vertical line with centre (中間點和其 error bar / standard error)

ggplot(diamonds, aes(color, price)) +
       geom_pointrange(stat = "summary_bin")



我們可以改變點和其 error bar 的大小和顏色。

ggplot(diamonds, aes(color, price)) +
       geom_pointrange(stat = "summary_bin",
                       size = 0.2, color = 'blue')

## No summary function supplied, defaulting to `mean_se()



summary_bin 裡面的 error bar 預設是 standard error (SE),你也可以把他設成別的,例如把它設成最大值 fun.ymax = max 和最小值 fun.ymin = min

ggplot(diamonds, aes(color, price)) +
       geom_pointrange(stat = "summary",
                       fun.y = mean,
                       fun.ymin = min,
                       fun.ymax = max)



可以把 pointrange() 和其他圖合在一起,這裡要注意一下,放在後面的會覆蓋住前面的,所以需要把 geom_point() 放在 geom_pointrange() 後面,如果相反的話會在圖裡看不出 geom_point() 裡面的設定。下面把兩個用不同顏色和不同大小的點區分。

ggplot(diamonds, aes(color, price)) +
       geom_pointrange(stat = "summary_bin",
                       size = 0.5, color = 'red') +
       geom_point(stat = "summary_bin", fun.y = "mean",
                  size = 1.5, color = 'blue')



Error bar 除了以上兩種外,也可以設其他的,例如讓它的範圍為中間 50%,最小值為第 25%,最大值為第 75%。

下面先用 group_by() 的功能把 color 分類,然後再用 summarize() 的功能在每種顏色裡算出其的價格。先把每種顏色的平均價格設為 y = mean(price),把誤差值(SE)的最小值指定為 ymin,設其為每種鑽石顏色的第 25%;把最大值指定為 ymax,設其為每種顏色的第 75%。

ps. quantile 分為:0 (0%), 0.25 (25%), 0.75 (75%), 1 (100%)

在下面先把算出來的指定為一個 data frame:df_color_price

df_color_price <- group_by(diamonds, color) %>%
  summarize(ymin = quantile(price, 0.25),
            ymax = quantile(price, 0.75),
            y = mean(price)),
            n = n())

# A tibble: 7 x 5
  color   ymin    ymax        y     n
  <ord>  <dbl>   <dbl>    <dbl> <int>
1     D  911.0 4213.50 3169.954  6775
2     E  882.0 4003.00 3076.752  9797
3     F  982.0 4868.25 3724.886  9542
4     G  931.0 6048.00 3999.136 11292
5     H  984.0 5980.25 4486.669  8304
6     I 1120.5 7201.75 5091.875  5422
7     J 1860.5 7695.00 5323.818  2808

上面語法中的 n() 是指在那個分類裡有幾個,在 R 裡面的解釋是:"The number of observations in the current group."(也就是說,上面得出的 n 的數字應該是要表 #1 裡的是一樣的。)

從上面的表可以看出 summarize() 算出每種鑽石顏色的平均價格(y)和前後 25% 的價格(ymin, ymax),還有每種顏色的鑽石有多少個(n),接著我們用這個 data frame 畫圖。

ggplot(df_color_price, aes(color, y,
                       ymin = ymin, ymax = ymax)) +
       geom_pointrange() +
       geom_point(aes(size = n))

在前面 df_color_price 的語法裡加入 n = n() 是要讓點圖裡的點會依據每種顏色的數量呈現出不同的大小,例如在前面 pointrange() 的例子裡加入 size =  的話可以指定點的大小。之前都是設數字(例如 size = 2),但是在這裡設 size = n 的話,因為在前面已經設了 n = n(),表示 n 是每種顏色的數量的話,那點的大小就會因數量而異。

也可以把上面兩個語法用 %>% 合在一起。

group_by(diamonds, color) %>%
         summarize(ymin = quantile(price, 0.25),
                   ymax = quantile(price, 0.75),
                   y = mean(price),
                   n = n()) %>%
ggplot(aes(color, y, ymin = ymin, ymax = ymax)) +
       geom_pointrange() +
       geom_point(aes(size = n))



也可以和 geom_bar() 合用。

ggplot(diamonds, aes(color, price)) +
       geom_bar(stat = "summary", fun.y = "mean") +
       geom_pointrange(stat = "summary",
                       size = 0.1, color = 'blue')



在上面的圖中,因為每種鑽石顏色的 standard error (SE) 很小,所以看不出來。

可以用上面已經設好的 df_color_price 來畫圖:

ggplot(df_color_price, aes(color, y,
                       ymin = ymin, ymax = ymax)) +
        geom_bar(stat = "summary") +
        geom_pointrange(size = 0.2, color = 'blue')

因為在 geom_pointrange() 裡面並沒有設 stat = "summary",所以圖呈現出來的會是設在第一層 ggplot() 裡的 error bar (ymin, ymax)。

也可以用 %>% 把兩個語法合在一起,變成下面這樣:(這裡沒用 n = n() 是因為並非畫點圖,所以不需要。)

group_by(diamonds, color) %>%
  summarize(ymin = quantile(price, 0.25),
            ymax = quantile(price, 0.75),
            y = mean(price)) %>%
ggplot(aes(color, y, ymin = ymin, ymax = ymax)) +
       geom_bar(stat = "summary") +
       geom_pointrange(size = 0.2, color = 'blue')



下面把 geom_pointrange() 和 geom_line() 合在一起用。

ggplot(diamonds, aes(color, price)) +
     geom_pointrange(stat = "summary_bin",
                     size = 0.2, color = 'steelblue') +
     geom_line(stat = "summary_bin", fun.y = "mean",
               group = 1, alpha = 1/2)



geom_line() 裡面的語法可以參考這篇:ggplot | Regression Line & Grouping

2. geom_linerange(): vertical line (只有一條直線 error bar / standard error)

如果單用的話就會像下圖那樣只有一條線,所以通常會和其他圖和用。

ggplot(diamonds, aes(color, price)) +
       geom_linerange(stat = "summary_bin")



可以和點圖和 bar graph 合用。和點圖一起用的話,其實畫出來的圖會跟只用 pointrange 差不多。你也可以幫 geom_linerange() 指定顏色,就是那條代表 SE 的直線,可以設成和點不一樣的顏色,如果直接用 pointrange() 的話就無法用不同的顏色。

ggplot(diamonds, aes(color, price)) +
       geom_point(stat = "summary_bin", fun.y = "mean",
                  size = 2, color = 'skyblue') +
       geom_linerange(stat = "summary_bin", color = 'blue')



同樣可以和 bar graph 合用。

ggplot(diamonds, aes(color, price)) +
       geom_bar(stat = "summary_bin", fun.y = "mean") +
       geom_linerange(stat = "summary_bin", color = 'red')



預設的 SE 跟之前 pointrange 的圖一樣太小看不出來,下面再用之前設的 df_color_price 再畫一次。

ggplot(df_color_price, aes(color, y,
                       ymin = ymin, ymax = ymax)) +
       geom_bar(stat = "summary") +
       geom_linerange(size = 0.5, color = 'steelblue')



geom_linerange() 裡面的 size =  是條線條的粗細。

3. geom_errorbar(): error bars.

如果只有 error bar 的話是這樣:

ggplot(diamonds, aes(color, price)) +
       geom_errorbar(stat = "summary_bin")



通常都是和 bar graph 合在一起用。

ggplot(diamonds, aes(color, price)) +
       geom_bar(stat = "summary", fun.y = "mean") +
       geom_errorbar(stat = "summary",
                     width = 0.1, color = 'grey')



geom_errorbar() 裡面的 width =  是用來設 error bar 的寬度。

下面用上面設好的 df_color_price 再畫一次。

ggplot(df_color_price, aes(color, y,
                           ymin = ymin, ymax = ymax)) +
       geom_bar(stat = "summary") +
       geom_errorbar(width = 0.1, color = 'steelblue')



4. geom_crossbar(): vertical bar with center.

終於來到最後一個。只用 geom_crossbar() 的話是這樣:

ggplot(diamonds, aes(color, price)) +
       geom_crossbar(stat = "summary_bin")



用上面的 df_color_price 畫圖:

ggplot(df_color_price, aes(color, y,
                           ymin = ymin, ymax = ymax)) +
       geom_crossbar(width = 0.2, color = 'steelblue')



接下來我想用檔案資料 mpg,用裡面的 class (X-axis) 和 cty (Y-axis) 畫圖。下面跟前面一樣先做總整理。

by(mpg$cty, mpg$class, summary)

mpg$class: 2seater
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
   15.0    15.0    15.0    15.4    16.0    16.0
----------------------------------------------------------
mpg$class: compact
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
  15.00   18.00   20.00   20.13   21.00   33.00
----------------------------------------------------------
mpg$class: midsize
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
  15.00   18.00   18.00   18.76   21.00   23.00
----------------------------------------------------------
mpg$class: minivan
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
  11.00   15.50   16.00   15.82   17.00   18.00
---------------------------------------------------------
mpg$class: pickup
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
      9      11      13      13      14      17
----------------------------------------------------------
mpg$class: subcompact
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
  14.00   17.00   19.00   20.37   23.50   35.00
----------------------------------------------------------
mpg$class: suv
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
   9.00   12.00   13.00   13.50   14.75   20.00 

把點圖和 crossbar 合用:

ggplot(mpg, aes(class, cty)) +
       geom_point(alpha = 0.5) +
       geom_crossbar(stat = "summary",
                     width = 0.2, color = 'red')



也可以像上面一樣,把 error bar 設為中間的 50%。

df_mpg <- group_by(mpg, class) %>%
  summarize(cty_ymin = quantile(cty, 0.25),
            cty_ymax = quantile(cty, 0.75),
            y = mean(cty))

ggplot(df_mpg, aes(class, y,
                   ymin = cty_ymin,
                   ymax = cty_ymax)) +
       geom_crossbar(width = 0.2, color = 'steelblue') +
       ylim(10, 35)



我把上面的 Y-axis 設了最大值和最小值 ylim(10,35),讓它和上面點和 crossbar 合圖的 scale 相同,比較好做比較。上面總整理的表格有 1st Qu. 和 3rd Qu.,Qu. 即是 quantile;1st Qu. 就是第一個 quantile,即第 25% ;3rd Qu. 是第三個 quantile,即第 75%。也就是說 1st Qu. 和 3rd Qu. 即是我們在 df_mpg 裡設的 cty_ymin 和 cty_ymax。可以把 crossbar 的最大值和最小值和表格裡的 1st. Qu 和 3rd Qu. 做比對,看看是否一樣。

下面同樣是把點圖和 crossbar 合用,但是用 stat_summary() 來畫。在 error bar 的部分另外設成 sd,用的是 package "Hmisc" 裡面的 mean_sdl

mean_sdl: smean.sdl computes the mean plus or minus a constant times the standard deviation

library(Hmisc)

ggplot(mpg, aes(class, cty)) +
       geom_point(alpha = 0.5) +
       stat_summary(aes(y = cty), fun.y = mean,
                    fun.data = mean_sdl,
                    width = 0.2, color = 'pink',
                    geom = "crossbar")



mean_cl_normal: computes 3 summary variables: the sample mean and lower and upper Gaussian confidence limits based on the t-distribution.

ggplot(mpg, aes(class, cty)) +
       geom_point(alpha = 0.5) +
       stat_summary(aes(y = cty), fun.y = mean,
                    fun.data = mean_cl_normal,
                    width = 0.2, color = 'pink',
                    geom = "crossbar")





最後,來練習一下。

比較 cut 和 depth 的關係,用點畫出中間值(median)和中間 50% 的 error bar。

group_by(diamonds, cut) %>%
  summarize(ymin = quantile(depth, 0.25),
            ymax = quantile(depth, 0.75),
            y = median(depth),
            n = n()) %>%
  ggplot(aes(cut, y, ymin = ymin, ymax = ymax)) +
  geom_pointrange(color = 'steelblue') +
  geom_point(aes(size = n), color = 'skyblue')




【 其他補充 】

有的地方可以看到 stat = "identity",這和 stat = "summary_bin" 有什麼不同呢?

stat = "identity" 通常是用在每個項目只有一個,例如在 diamonds 的檔案裡面,每個 color 的觀察只有一個,也就是說當你畫 price vs. color 的圖的時候,每個 color 裡不需要算價格的平均值或找它的中間值,因為就只有一個數(一個觀察)可以畫在 Y-axis。但是當資料變多的時候,也就是每種 color 裡面有好幾個,每個都是不同的價格的時候,那你畫 price vs. color 時就需要算出每種 color 的平均值,才能畫出 Y-axis,這時候就需要用 stat = "summary_bin" 來算出平均值或找出中間值。

另外,當你沒設 stat = " "  的時候,geom_bar() 裡的預設是 stat = "bin"

ggplot2: geom_bar() 網頁裡的解釋是這樣:

The heights of the bars commonly represent one of two things: either a count of cases in each group, or the values in a column of the data frame. By default, geom_bar uses stat="bin". This makes the height of each bar equal to the number of cases in each group, and it is incompatible with mapping values to the y aesthetic. If you want the heights of the bars to represent values in the data, use stat="identity" and map a value to the y aesthetic.



其他網頁參考:

My ggplot2 cheat sheet: Search by task

R document / ggplot2: geom_bar() function

ggplot 2 / Bar charts

ggplot2 / Vertical intervals: lines, crossbars & errorbars

ggplot2 / Summarise y values at unique/binned x










沒有留言:

張貼留言

歡迎發表意見