使用不同的函数为不同的变量总结“summarise_at”

16

当我在dplyr中使用group_by和summarise时,可以自然地对不同的变量应用不同的摘要函数。例如:

    library(tidyverse)

    df <- tribble(
      ~category,   ~x,  ~y,  ~z,
      #----------------------
          'a',      4,   6,   8,
          'a',      7,   3,   0,
          'a',      7,   9,   0,
          'b',      2,   8,   8,
          'b',      5,   1,   8,
          'b',      8,   0,   1,
          'c',      2,   1,   1,
          'c',      3,   8,   0,
          'c',      1,   9,   1
     )

    df %>% group_by(category) %>% summarize(
      x=mean(x),
      y=median(y),
      z=first(z)
    )

输出结果为:

    # A tibble: 3 x 4
      category     x     y     z
         <chr> <dbl> <dbl> <dbl>
    1        a     6     6     8
    2        b     5     1     8
    3        c     2     8     1

我的问题是,使用summarise_at该怎么做?显然对于这个例子来说这是不必要的,但是假设我有很多变量需要求均值、中位数等等。

当我使用summarise_at时,是否会失去这种功能?我是否必须对所有变量组使用所有函数,然后扔掉我不想要的?

也许我只是漏看了什么,但我想不出来,而且文档中也没有这方面的示例。谢谢您的帮助。


基本的 Map 功能可以做到这一点,例如 Map(function(f,v) f(v), c(mean,median,first), df[c("x","y","z")])。也许 purrrmap 可以做类似的事情? - thelatemail
是的,我在想purrr是否能为我们提供一种解决方法。值得调查。但在您的示例中,您只是将所有函数应用于所有变量吗?而如果要使用group_by,该如何操作? - David Pepper
不,我正在使用 Map 将每个函数依次应用于每个变量 - 查看 mean(df$x); median(df$y); first(df$z) 的结果并将其与 Map 代码进行比较。 - thelatemail
好的,我明白你的意思,但我的问题与ycw的问题相同:如果第一个函数有三个变量,第二个函数有十个变量,第三个函数只有一个变量怎么办?而且这看起来更像是summarise_at的替代品,而不是放在其中的东西。我想我需要完整的代码,因为当我将你的建议应用到我的样本数据框中时,我没有得到我要找的答案。 - David Pepper
2个回答

12

这里有一个想法。

library(tidyverse)

df_mean <- df %>%
  group_by(category) %>%
  summarize_at(vars(x), funs(mean(.)))

df_median <- df %>%
  group_by(category) %>%
  summarize_at(vars(y), funs(median(.)))

df_first <- df %>%
  group_by(category) %>%
  summarize_at(vars(z), funs(first(.)))

df_summary <- reduce(list(df_mean, df_median, df_first), 
                     left_join, by = "category")

就像你所说的,在这个例子中没有必要使用 summarise_at。然而,如果你有很多列需要用不同的函数进行总结,这种策略可能有效。你需要在每个 summarize_at 中通过 vars(...) 指定列。规则与 dplyr::select 函数相同。

更新

这里有另一个想法。定义一个函数来修改 summarise_at 函数,然后使用一个查找列表来显示要应用的变量和相关函数来应用 map2 函数。在这个例子中,我把 mean 应用到 xy 列,将 median 应用到 z

# Define a function
summarise_at_fun <- function(variable, func, data){
  data2 <- data %>%
    summarise_at(vars(variable), funs(get(func)(.)))
  return(data2)
}

# Group the data
df2 <- df %>% group_by(category)

# Create a look-up list with function names and variable to apply
look_list <- list(mean = c("x", "y"),
                  median = "z")

# Apply the summarise_at_fun
map2(look_list, names(look_list), summarise_at_fun, data = df2) %>%
  reduce(left_join, by = "category")

# A tibble: 3 x 4
  category     x     y     z
     <chr> <dbl> <dbl> <dbl>
1        a     6     6     0
2        b     5     3     8
3        c     2     6     1

1
这确实是可能的,也比我考虑过的各种“长”解决方案更优雅。但是是否可以通过一个命令来完成呢?另外,在使用summarise_at时是否有一种控制结果列名称的方法? - David Pepper
@DavidEpstein 可以使用 summarise_at 分配名称。您可以执行 funs(x = mean(.)),这将导致 Col_x,其中 Col 是原始列名。 - www
谢谢提供链接,但我仍然没有看到关于将一个函数应用于一组变量的子集,将另一个函数应用于另一组变量的任何信息。 - David Pepper
@DavidEpstein 请查看我的更新。这可能更符合您的需求。您需要创建一个新函数并创建一个查找表来显示变量名称和要应用的函数之间的关系。 - www
我也喜欢你的更新,但是我理解map2中x和y变量的长度必须相同。在这个例子中当然是这样的,但是更一般地,每个函数可能会总结任意数量的变量。如果一个变量列表作为传递给map2的第一个元素,你的方法会起作用吗? - David Pepper
显示剩余5条评论

6

由于您的问题与“summarise_at”有关;

这是我的想法:

df %>% group_by(category) %>% 
 summarise_at(vars(x, y, z),
      funs(mean = mean, sd = sd, min = min),
      na.rm = TRUE)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接