r语言 - 如何仅汇总表格的一部分



我有两个相关的用例,其中我只需要总结表格的一部分,以类似于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中,我该如何使用它?

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

我用

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 s 填充)。

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

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']以就地修改名称。扩展到多个标准/任何功能应该很简单。

您可以创建一个新列以按以下方式进行分组:

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调用之间切换时,必须注意仅更改标记组中的值。

最新更新