我如何仅总结一部分表格?

时间:2015-04-21 22:46:14

标签: r dplyr

我有两个相关的用例,我需要总结一下表的一部分,以类似于filter的方式指定。

简而言之,我想要这样的事情:

iris %>%
    use_only(Species == 'setosa') %>%
    summarise_each(funs(sum), -Species) %>%
    mutate(Species = 'setosa_sum') %>%
    use_all()

产生这个:

Source: local data frame [101 x 5]

   Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
1         250.3       171.4         73.1        12.3 setosa_sum
2           7.0         3.2          4.7         1.4 versicolor
3           6.4         3.2          4.5         1.5 versicolor
4           6.9         3.1          4.9         1.5 versicolor
5           5.5         2.3          4.0         1.3 versicolor
…

因此,不是按列的值进行分组,而是使用过滤条件对表的视图进行操作,而实际上丢失表的其余部分(与过滤器不同)。

如何巧妙地实施use_only / use_all更好的是,此功能是否已包含在dplyr中,我该如何使用?

生成上面的结果当然很容易,但我需要针对许多不同的情况做类似的事情,使用复杂和可变的过滤标准。

3 个答案:

答案 0 :(得分:13)

我实现了这一点,方法是让use_only将表格的其余部分保存到全局选项dplyr_use_only_rest中,并让use_all将其重新绑定在一起。

use_only <- function(.data, ...) {
    if (!is.null(.data$.index)) {
        stop("data cannot already have .index column, would be overwritten")
    }
    filt <- .data %>%
        mutate(.index = row_number()) %>%
        filter(...)

    rest <- .data %>% slice(-filt$.index)
    options(dplyr_use_only_rest = rest)
    select(filt, -.index)
}

use_all <- function(.data, ...) {
    rest <- getOption("dplyr_use_only_rest")
    if (is.null(rest)) {
        stop("called use_all() without earlier use_only()")
    }
    options(dplyr_use_only_rest = NULL)
    bind_rows(.data, rest)
}

我认识到设置全局选项并不是理想的函数式编程设计,但我不认为还有另一种方法可以确保数据帧的其余部分不受任何中间函数的影响。向对象添加额外属性不会使dosummarize等函数生效。

此时,

iris %>%
    use_only(Species == 'setosa') %>%
    summarise_each(funs(sum), -Species) %>%
    mutate(Species = 'setosa_sum') %>%
    use_all()

根据需要返回:

   Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
1         250.3       171.4         73.1        12.3 setosa_sum
2           7.0         3.2          4.7         1.4 versicolor
3           6.4         3.2          4.5         1.5 versicolor
4           6.9         3.1          4.9         1.5 versicolor
5           5.5         2.3          4.0         1.3 versicolor
...

可以使用任何中间步骤代替summarize_eachmutatedofilter等),它们只会发生在指定的行中。您甚至可以添加或删除列(其余部分将填入NA)。

答案 1 :(得分:13)

我认为您搜索函数以满足特定语法的方法过于严格。这就是我使用data.table做的事情(我不确定dplyr是否允许这样的变量行,我知道它已经成为FR一段时间了):< / p>

library(data.table)
dt = as.data.table(iris)

dt[, if (Species == 'setosa') lapply(.SD, sum) else .SD, by = Species]
#        Species Sepal.Length Sepal.Width Petal.Length Petal.Width
#  1:     setosa        250.3       171.4         73.1        12.3
#  2: versicolor          7.0         3.2          4.7         1.4
#  3: versicolor          6.4         3.2          4.5         1.5
#  4: versicolor          6.9         3.1          4.9         1.5
#  5: versicolor          5.5         2.3          4.0         1.3
# ---                                                             

您还可以在末尾添加[Species == 'setosa', Species := 'setosa_sum']以修改名称。应该可以直接扩展到多个标准/任何函数。

答案 2 :(得分:5)

您可以创建一个新列来分组:

iris %>%
  mutate( group1 = ifelse(Species == "setosa", "", row_number()))  %>%
  group_by( group1, Species ) %>%
  summarise_each(funs(sum), -Species, -group1) %>%
  ungroup() %>%
  select(-group1)

更新 - 作为更通用的解决方案

library(lazyeval)

use_only_ <- function(x, condition, ...) {
  condition <- as.lazy(condition, parent.frame())
  mutate_(x, .group = condition) %>% 
    group_by_(".group", ...)
}

use_only <- function(x, condition, ...) {
  use_only_(x, lazy(condition), ...)
}

use_all <- function(x) {
  ungroup(x) %>%
    select(- .group)
}

在数据框和调用环境的上下文中使用use_only条件。在这种情况下:

iris %>%
  use_only( ifelse(Species == "setosa", "", row_number()), "Species") %>%
  summarise_each(funs(sum), -Species, -.group) %>%
  use_all()

use_only_可以与公式或字符串一起使用。例如:

condition <- ~ifelse(Species == "setosa", "", row_number())

condition <- "ifelse(Species == 'setosa' , "", row_number())"

并致电:

iris %>%
  use_only_(condition, "Species") %>%
  summarise_each(funs(sum), -Species, -.group) %>%
  use_all()

use_onlyuse_all来电之间进行变异时,您必须注意只更改标记组内的值。