【问题标题】:Why does this (grouped) mutate_at syntax work but summarise_at fails? [duplicate]为什么这个(分组的)mutate_at 语法有效但 summarise_at 失败? [复制]
【发布时间】:2021-02-02 05:04:36
【问题描述】:

示例数据:

(tmp_df <-
    expand.grid(id = letters[1:3], y = 1:3))
#    id y
# 1  a 1
# 2  b 1
# 3  c 1
# 4  a 2
# 5  b 2
# 6  c 2
# 7  a 3
# 8  b 3
# 9  c 3

以下作品:

tmp_df %>%
    group_by(id) %>%
    mutate_at(which(colnames(.) %in% c("y")),
              sum)
#   id        y
#   <fct> <int>
# 1 a         6
# 2 b         6
# 3 c         6
# 4 a         6
# 5 b         6
# 6 c         6
# 7 a         6
# 8 b         6
# 9 c         6

但以下会引发错误Error: Only strings can be converted to symbols

tmp_df %>%
    group_by(id) %>%
    summarise_at(which(colnames(.) %in% c("y")),
              sum)

请注意,以下代码 sn-ps 是成功生成预期结果的替代方法:

tmp_df %>%
    group_by(id) %>%
    summarise_at(vars(y),
                 sum)


tmp_df %>%
    group_by(id) %>%
    summarise_at("y",
                 sum)

编辑:按照 akrun 的回答,我应该注意我使用的 dplyr 版本是 dplyr_0.8.4

【问题讨论】:

  • 这是一个错误吗? .vars 参数的帮助文件明确表示:..., a numeric vector of column positions, ...(我认为这是引发错误的原因)
  • 声称重复的已接受答案声称此问题已得到修复,但如果是这样,则该问题会在以后的某个版本中重新引入,因此它仍然是一个问题。

标签: r dplyr


【解决方案1】:

似乎mutate_at 中的列号包括分组变量,但在summarize_at 中它们不作为下面的两行代码工作。尽管 _at 函数已被across 取代,但您可以报告此错误,我不知道它是否会被修复。

tmp_df %>% group_by(id) %>% mutate_at(2, sum)

tmp_df %>% group_by(id) %>% summarize_at(1, sum)

如果我们交换列,那么它们都会一致地工作,因为分组变量不再影响 y 列的位置,这一事实进一步强化了这一点。

tmp_df[2:1] %>% group_by(id) %>% mutate_at(1, sum)

tmp_df[2:1] %>% group_by(id) %>% summarize_at(1, sum)

【讨论】:

  • 这是一个非常聪明的诊断技巧来交换列顺序。而且我想我会提交一个错误,即使我认为它也不会被修复。
【解决方案2】:

which(colnames(.) %in% c("y")) 返回索引 2。

which(colnames(tmp_df) %in% c("y"))
#[1] 2

当您使用mutate_at 时这很好。

library(dplyr)
tmp_df %>% group_by(id) %>% mutate_at(2,sum)

#   id        y
#  <fct> <int>
#1 a         6
#2 b         6
#3 c         6
#4 a         6
#5 b         6
#6 c         6
#7 a         6
#8 b         6
#9 c         6

但是,当您使用 summarise_at 时,它不会计算分组列。所以当你这样做时你会得到一个错误:

tmp_df %>% group_by(id) %>% summarise_at(2,sum)

错误:只能将字符串转换为符号

你真正需要的是

tmp_df %>% group_by(id) %>% summarise_at(1,sum)

#   id        y
#* <fct> <int>
#1 a         6
#2 b         6
#3 c         6

但是,无法根据group_by 中的列数动态更改我们要在summarise_at 中使用的列号的位置,因此更好的选择是在vars 中传递列名,而不是列号。

tmp_df %>% group_by(id) %>% mutate_at(vars('y'),sum)

#  id        y
#  <fct> <int>
#1 a         6
#2 b         6
#3 c         6
#4 a         6
#5 b         6
#6 c         6
#7 a         6
#8 b         6
#9 c         6

tmp_df %>% group_by(id) %>% summarise_at(vars('y'),sum)

#  id        y
#* <fct> <int>
#1 a         6
#2 b         6
#3 c         6

across 的好处是它对 mutatesummarise 的行为一致。

tmp_df %>% group_by(id) %>% mutate(across(2,sum))

x 不能对不存在的列进行子集化。 x 位置 2 不存在。

tmp_df %>% group_by(id) %>% summarise(across(2,sum))

x 不能对不存在的列进行子集化。 x 位置 2 不存在。

即使使用across,最好使用列名而不是位置。

tmp_df %>% group_by(id) %>% mutate(across(y,sum))
tmp_df %>% group_by(id) %>% summarise(across(y,sum))

【讨论】:

  • 感谢您对错误信息的详细解释。正如对我的目的进行以下修改的注释一样:summarise_at(colnames(.)[colnames(.) %in% 'y'], ...)
【解决方案3】:

我们可以使用contains

library(dplyr)
tmp_df %>% 
    group_by(id) %>% 
    summarise(across(contains('y'), sum), .groups = 'drop')

_at_all 后缀函数已被弃用,取而代之的是当前使用的 across

【讨论】:

  • 谢谢。我想是时候更新我的 dplyr 版本了:|
猜你喜欢
  • 2017-01-26
  • 1970-01-01
  • 2014-01-16
  • 2021-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-11
相关资源
最近更新 更多