2014-02-08 105 views
102

我用dplyr語法掙扎了一下。我有一個數據框與不同的變量和一個分組變量。現在我想計算平均每個組內的每個列,在R.用dplyr總結多列?

df <- data.frame(
    a = sample(1:5, n, replace = TRUE), 
    b = sample(1:5, n, replace = TRUE), 
    c = sample(1:5, n, replace = TRUE), 
    d = sample(1:5, n, replace = TRUE), 
    grp = sample(1:3, n, replace = TRUE) 
) 
df %>% group_by(grp) %>% summarise(mean(a)) 

使用dplyr這給了我的平均列「A」通過「GRP」表示每個組。

我的問題是:是否有可能一次獲得每個組內的每列的方法?或者我必須重複每個列的df %>% group_by(grp) %>% summarise(mean(a))

我想吃點什麼是一樣的東西

df %>% group_by(grp) %>% summarise(mean(a:d)) # "mean(a:d)" does not work 
+6

@Jaap這個問題不重複。這是因爲這是一個關於如何處理「dplyr」的問題。請取消重複標記。 – Keiku

回答

176

dplyr包包含summarise_all爲此目的:

df %>% group_by(grp) %>% summarise_all(funs(mean)) 
#> Source: local data frame [3 x 5] 
#> 
#>  grp  a  b  c  d 
#> (int) (dbl) (dbl) (dbl) (dbl) 
#> 1  1 3.000000 2.666667 2.666667 3.333333 
#> 2  2 2.666667 2.666667 2.500000 2.833333 
#> 3  3 4.000000 1.000000 4.000000 3.000000 

如果要總結只有某些列,使用summarise_atsummarise_if功能。

另外,在purrrlyr包提供相同的功能:

df %>% slice_rows("grp") %>% dmap(mean) 
#> Source: local data frame [3 x 5] 
#> 
#>  grp  a  b  c  d 
#> (int) (dbl) (dbl) (dbl) (dbl) 
#> 1  1 3.000000 2.666667 2.666667 3.333333 
#> 2  2 2.666667 2.666667 2.500000 2.833333 
#> 3  3 4.000000 1.000000 4.000000 3.000000 

而且不要忘了data.table

setDT(df)[, lapply(.SD, mean), by = grp] 
#> grp  a  b  c  d 
#> 1: 3 3.714286 3.714286 2.428571 2.428571 
#> 2: 1 1.000000 4.000000 5.000000 2.000000 
#> 3: 2 4.000000 4.500000 3.000000 3.000000 

讓我們嘗試比較性能。

library(dplyr) 
library(purrrlyr) 
library(data.table) 
library(benchr) 
n <- 10000 
df <- data.frame(
    a = sample(1:5, n, replace = TRUE), 
    b = sample(1:5, n, replace = TRUE), 
    c = sample(1:5, n, replace = TRUE), 
    d = sample(1:5, n, replace = TRUE), 
    grp = sample(1:3, n, replace = TRUE) 
) 
dt <- setDT(df) 
benchmark(
    dplyr = df %>% group_by(grp) %>% summarise_all(funs(mean)), 
    purrrlyr = df %>% slice_rows("grp") %>% dmap(mean), 
    data.table = dt[, lapply(.SD, mean), by = grp] 
) 
#> Benchmark summary: 
#> Time units : microseconds 
#>  expr n.eval min lw.qu median mean up.qu max total relative 
#>  dplyr 100 3490 3550 3710 3890 3780 15100 389000  6.98 
#> purrrlyr 100 2540 2590 2680 2920 2860 12000 292000  5.04 
#> data.table 100 459 500 531 563 571 1380 56300  1.00 
+1

這很好,但是我應該怎麼做,如果我只是想應用函數,即粘貼到最後一列,而對於其他列我只想採取第一個要素或保持原樣? – biocyberman

+1

我的意思是,像'select'這樣的行爲會很棒:'總結(df,a:c,d = paste(d,collaspe =',')'只想放入更多原始列以供參考 – biocyberman

+1

區別在purrr和dplyr之間?? –

28

你可以簡單地傳遞更多參數summarise

df %>% group_by(grp) %>% summarise(mean(a), mean(b), mean(c), mean(d)) 

來源:本地數據幀[3×5]

grp mean(a) mean(b) mean(c) mean(d) 
1 1 2.500000 3.500000 2.000000  3.0 
2 2 3.800000 3.200000 3.200000  2.8 
3 3 3.666667 3.333333 2.333333  3.0 
+1

太棒了!如果列名和數量未知,是否甚至有可能這樣做?例如。有3或6而不是4個固定列? – Daniel

+3

這是'dplyr'中的TODO我相信(像'plyr'' colwise'),在這裏看到一個相當尷尬的當前解決方案:http://stackoverflow.com/a/21296364/1527403 –

+0

非常感謝兩個您!我可能只是使用循環遍歷所有列。 – Daniel

5

對於完整性:與dplyr V0.2 ddplycolwise也將做到這一點:

> ddply(df, .(grp), colwise(mean)) 
    grp  a b  c  d 
1 1 4.333333 4.00 1.000000 2.000000 
2 2 2.000000 2.75 2.750000 2.750000 
3 3 3.000000 4.00 4.333333 3.666667 

,但它是慢,至少在這種情況下:

> microbenchmark(ddply(df, .(grp), colwise(mean)), 
        df %>% group_by(grp) %>% summarise_each(funs(mean))) 
Unit: milliseconds 
              expr  min  lq  mean 
       ddply(df, .(grp), colwise(mean))  3.278002 3.331744 3.533835 
df %>% group_by(grp) %>% summarise_each(funs(mean)) 1.001789 1.031528 1.109337 

    median  uq  max neval 
3.353633 3.378089 7.592209 100 
1.121954 1.133428 2.292216 100 
+1

需要在大型數據集上進行測試。 –

+1

'ddply'不在'dplyr'中,它在'plyr'中。 – Axeman

38

我們可以通過使用summarize_at,summarize_allsummarize_ifdplyr 0.7.4進行總結。我們可以使用varsfuns參數作爲下面的代碼來設置多個列和函數。 funs公式的左側被分配到彙總變量的後綴。在dplyr 0.7.4,summarise_each(和mutate_each)已被棄用,所以我們不能使用這些功能。

options(scipen = 100, dplyr.width = Inf, dplyr.print_max = Inf) 

library(dplyr) 
packageVersion("dplyr") 
# [1] ‘0.7.4’ 

set.seed(123) 
df <- data_frame(
    a = sample(1:5, 10, replace=T), 
    b = sample(1:5, 10, replace=T), 
    c = sample(1:5, 10, replace=T), 
    d = sample(1:5, 10, replace=T), 
    grp = as.character(sample(1:3, 10, replace=T)) # For convenience, specify character type 
) 

df %>% group_by(grp) %>% 
    summarise_each(.vars = letters[1:4], 
       .funs = c(mean="mean")) 
# `summarise_each()` is deprecated. 
# Use `summarise_all()`, `summarise_at()` or `summarise_if()` instead. 
# To map `funs` over a selection of variables, use `summarise_at()` 
# Error: Strings must match column names. Unknown columns: mean 

您應該更改爲下面的代碼。以下代碼全部具有相同的結果。

# summarise_at 
df %>% group_by(grp) %>% 
    summarise_at(.vars = letters[1:4], 
       .funs = c(mean="mean")) 

df %>% group_by(grp) %>% 
    summarise_at(.vars = names(.)[1:4], 
       .funs = c(mean="mean")) 

df %>% group_by(grp) %>% 
    summarise_at(.vars = vars(a,b,c,d), 
       .funs = c(mean="mean")) 

# summarise_all 
df %>% group_by(grp) %>% 
    summarise_all(.funs = c(mean="mean")) 

# summarise_if 
df %>% group_by(grp) %>% 
    summarise_if(.predicate = function(x) is.numeric(x), 
       .funs = funs(mean="mean")) 
# A tibble: 3 x 5 
# grp a_mean b_mean c_mean d_mean 
# <chr> <dbl> <dbl> <dbl> <dbl> 
# 1  1 2.80 3.00 3.6 3.00 
# 2  2 4.25 2.75 4.0 3.75 
# 3  3 3.00 5.00 1.0 2.00 

您也可以有多個功能。

df %>% group_by(grp) %>% 
    summarise_at(.vars = letters[1:2], 
       .funs = c(Mean="mean", Sd="sd")) 
# A tibble: 3 x 5 
# grp a_Mean b_Mean  a_Sd  b_Sd 
# <chr> <dbl> <dbl>  <dbl> <dbl> 
# 1  1 2.80 3.00 1.4832397 1.870829 
# 2  2 4.25 2.75 0.9574271 1.258306 
# 3  3 3.00 5.00  NA  NA 
+0

是否有可能我可以應用每個功能的列, 即,列'a'只適用''平均值'和列'b'只適用於'sd' 使用summaise_at – user7462639

+0

@ user7462639在您的情況下,你可以使用'summarise'。即總結(a_mean = mean(a),b_sd = sd(b))' – Keiku

3

所有的例子都是偉大的,但我想我會多加一個展示如何在一個「整潔」格式工作簡化了操作。現在數據框處於「寬」格式,這意味着變量「a」到「d」以列表示。要獲得「整齊」(或長)格式,可以使用tidyr包中的gather(),該包將列「a」到「d」中的變量轉換爲行。然後,您使用group_by()summarize()函數來獲取每個組的平均值。如果您想以寬格式顯示數據,只需添加對spread()功能的額外呼叫即可。


library(tidyverse) 

# Create reproducible df 
set.seed(101) 
df <- tibble(a = sample(1:5, 10, replace=T), 
      b = sample(1:5, 10, replace=T), 
      c = sample(1:5, 10, replace=T), 
      d = sample(1:5, 10, replace=T), 
      grp = sample(1:3, 10, replace=T)) 

# Convert to tidy format using gather 
df %>% 
    gather(key = variable, value = value, a:d) %>% 
    group_by(grp, variable) %>% 
    summarize(mean = mean(value)) %>% 
    spread(variable, mean) 
#> Source: local data frame [3 x 5] 
#> Groups: grp [3] 
#> 
#>  grp  a  b  c  d 
#> * <int> <dbl> <dbl> <dbl> <dbl> 
#> 1  1 3.000000 3.5 3.250000 3.250000 
#> 2  2 1.666667 4.0 4.666667 2.666667 
#> 3  3 3.333333 3.0 2.333333 2.333333 
+0

這是另一個值得記住的好方法。只有一件事:我不同意哈德利對整潔數據的定義,它始終處於長格式。通常情況下,您不希望繁殖您的觀察結果,但希望每個觀察結果都有一行。 – Daniel

+0

我不反對。每個人都有偏好,對於一些廣泛的方法來說,從更直觀的角度來看,或者因爲實際上存在結構性原因,您不希望長格式。對我而言,我的偏好是長格式,因爲當我開始使用'dplyr'時,更長的格式使事情變得更容易。 –