r:分组,删除列和总和

时间:2016-12-16 07:24:30

标签: r dplyr

我在使用大型data.frame时遇到了一些麻烦。如果每个组列都没有任何0(完成),我需要对每列组进行求和。 I.E.我只想对每个组的列进行总结,即#34;完成"。

以下是需要对每列进行分组和求和的示例,但是,我无法弄清楚如何在dplyr管道中使用complete.cases

df <- data.frame(ca = c("a","b","a","c","b"),
             f = c(3,4,0,2,3),
             f2 = c(2,5,6,1,9),
             f3 = c(3,0,6,3,0)) 

结果应该是什么样的

  ca  f f2 f3
1  a NA  8  9
2  b  7 14 NA
3  c  2  1  3

这适用于对每个组进行求和

df2 <- df %>%
    arrange(ca) %>%
    group_by(ca) %>%
    summarize_at(.cols=vars(starts_with("f")),
            .funs=funs("sum"))

这是我无法开展的工作,但这似乎是我应该努力的目标

df2 <- df %>%
    arrange(ca) %>%
    group_by(ca) %>%
    summarize_(funs_(sum(complete.cases(.),na.rm=T)))

也许我需要一个summarize_if,我们将非常感谢任何帮助。

2 个答案:

答案 0 :(得分:2)

如果对一列进行分组,*_all函数将对所有非分组列进行操作。您可以使用na_if为特定值插入NA,这使整个过程非常简单:

df %>% mutate_all(funs(na_if(., 0L))) %>% 
    group_by(ca) %>%
    summarise_all(sum)

## # A tibble: 3 × 4
##       ca     f    f2    f3
##   <fctr> <dbl> <dbl> <dbl>
## 1      a    NA     8     9
## 2      b     7    14    NA
## 3      c     2     1     3
如果您愿意,可以

或组合两个电话:

df %>% group_by(ca) %>% summarise_all(funs(sum(na_if(., 0L))))

返回同样的东西。

基准

根据评论,10000行和100个非分组列的基准。非常宽的数据(超过1000列)对于任何一种方法都不是很好,但是如果你收集到很久并按前变量名分组,那么它是可以容忍的。

library(tidyr)
set.seed(47)

df <- data.frame(ca = sample(letters[1:3], 10000, replace = TRUE), 
                 replicate(100, rpois(100, 10)))

microbenchmark::microbenchmark(
    'two stp' = {
        df %>% mutate_all(funs(na_if(., 0L))) %>% 
            group_by(ca) %>% summarise_all(sum)
    }, 'one stp' = {
        df %>% group_by(ca) %>% summarise_all(funs(sum(na_if(., 0L))))
    }, 'two stp, reshape' = {
        df %>% gather(var, val, -ca) %>% 
            mutate(val = na_if(val, 0L)) %>% 
            group_by(ca, var) %>% summarise(val = sum(val)) %>% 
            spread(var, val)
    }, 'one stp, reshape' = {
        df %>% gather(var, val, -ca) %>% 
            group_by(ca, var) %>% summarise(val = sum(na_if(val, 0L))) %>% 
            spread(var, val)
    })

## Unit: milliseconds
##              expr       min        lq      mean    median        uq      max neval cld
##           two stp 311.36733 330.23884 347.77353 340.98458 354.21105 548.4810   100   c
##           one stp 299.90327 317.38300 329.78662 326.66370 341.09945 385.1589   100  b 
##  two stp, reshape  61.72992  67.78778  85.94939  73.37648  81.04525 300.5608   100 a  
##  one stp, reshape  70.95492  77.76685  90.53199  83.33557  90.14023 297.8924   100 a  

通过data.table使用dtplyr的速度要快得多。如果你不介意学习另一种语法,那么写data.table的速度会更快(h {t @docendodiscimus for replace)。重塑结果会导致更​​糟糕的时间,至少使用tidyr函数,但使用data.table::meltdcast它仍然可能是极宽数据的好选择。

library(data.table)
library(dtplyr)
set.seed(47)

df <- data.frame(ca = sample(letters[1:3], 10000, replace = TRUE), 
                 replicate(100, rpois(10000, 10)))
setDT(df)

microbenchmark::microbenchmark(
    'dtplyr 2 stp' = {
        df %>% mutate_all(funs(na_if(., 0L))) %>% 
            group_by(ca) %>% 
            summarise_all(sum)
    }, 'dtplyr 1 stp' = {
        df %>% group_by(ca) %>% 
            summarise_all(funs(sum(na_if(., 0L))))
    }, 'dt + na_if 2 stp' = {
        df[, lapply(.SD, function(x){na_if(x, 0L)})][, lapply(.SD, sum), by = ca]
    }, 'dt + na_if 1 stp' = {
        df[, lapply(.SD, function(x){sum(na_if(x, 0L))}), by = ca]
    }, 'pure dt 2 stp' = {
        df[, lapply(.SD, function(x){replace(x, x == 0L, NA)})][, lapply(.SD, sum), by = ca]
    }, 'pure dt 1 stp' = {
        df[, lapply(.SD, function(x){sum(replace(x, x == 0L, NA))}), by = ca]
    })

## Unit: milliseconds
##              expr       min        lq      mean    median        uq       max neval cld
##      dtplyr 2 stp 121.31556 130.88189 143.39661 138.32966 146.39086 355.24750   100   c
##      dtplyr 1 stp  28.30813  31.03421  36.94506  33.28435  43.46300  55.36789   100  b 
##  dt + na_if 2 stp  27.03971  29.04306  34.06559  31.20259  36.95895  53.66865   100  b 
##  dt + na_if 1 stp  10.50404  12.64638  16.10507  13.43007  15.18257  34.37919   100 a  
##     pure dt 2 stp  27.15501  28.91975  35.07725  30.28981  33.03950 238.66445   100  b 
##     pure dt 1 stp  10.49617  12.09324  16.31069  12.84595  20.03662  34.44306   100 a  

答案 1 :(得分:1)

基础R的一种方法是将NA填入NA,然后使用aggregate.

# fill 0s as NAs
is.na(df) <- df == 0

aggregate(cbind(f=df$f,f2=df$f2,f3=df$f3), df["ca"], sum)
  ca  f f2 f3
1  a NA  8  9
2  b  7 14 NA
3  c  2  1  3

注意:使用aggregate的公式界面可能会产生意外结果。

aggregate(.~ca, data=df, sum)
  ca f f2 f3
1  a 3  2  3
2  c 2  1  3

“b”类别退出,变量f中a的值为3,而不是NA。帮助文件中的规范指示na.action设置为na.omit,这会从计算中删除NA值。要使公式界面按需运行,请将此值更改为na.pass。

aggregate(.~ca, data=df, sum, na.action=na.pass)
  ca  f f2 f3
1  a NA  8  9
2  b  7 14 NA
3  c  2  1  3