r - n() 在 summarise_at() 中使用时行为不一致

标签 r dplyr tidyr tidyverse purrr

使用此示例数据:

library(tidyverse)

set.seed(123)
df <- data_frame(X1 = rep(LETTERS[1:4], 6),
                 X2 = sort(rep(1:6, 4)),
                 ref = sample(1:50, 24),
                 sampl1 = sample(1:50, 24),
                 var2 = sample(1:50, 24),
                 meas3 = sample(1:50, 24))

我可以使用 summarise_at()计算列子集中的值数:
df %>% summarise_at(vars(contains("2")), funs(sd_expr = n() ))

这不是很令人兴奋,因为它与行数相同。但是,它在具有嵌套列的表中很有用,其中每个单元格包含一个数据框,每个单元格中的行数不同。

例如,
df %>% 
  mutate_at(vars(-one_of(c("X1", "X2", "ref"))), funs(first = . - ref)) %>% 
  mutate_at(vars(contains("first")),  funs(second = . *2 )) %>%
  nest(-X1) %>%  
  mutate(mean = map(data, 
                  ~ summarise_at(.x, vars(contains("second")),
                                     funs(mean_second = mean(.) ))),
         n = map(data, 
                  ~ summarise_at(.x, vars(contains("second")),
                                     funs(n_second = n()  ))) ) %>%
  unnest(mean, n)

但是我得到了错误:

Error in mutate_impl(.data, dots) : Evaluation error: Can't create call to non-callable object.



为什么 mean()函数在此上下文中工作,n()才不是?

现在有几个解决方法可能是:
n = map(data, ~ summarise_at(.x, vars(contains("second")),    
                                 funs(n_second = length(unique(.))  )))

但这对于不同行上存在相同值的情况或替代方法并不稳健:
n = map(data, ~ nrow(.x)  )

但这不允许我构建更复杂的 summarise_at()功能,这是我真正的目标。最终我想做这样的事情来计算标准误差:
se = map(data, ~ summarise_at(.x, vars(contains("second")),
                                         funs(se_second = sd(.)/sqrt(n())  ))) 

为什么是 n()在这种情况下没有做我认为应该做的事情?

最佳答案

我相信 aosmith 的评论是正确的,这是这个问题的一个例子:

#2080: Using n() in nested mutate()/summarize() calls gives unexpected results

原因是因为 dplyr 的混合评估,其中它将某些 R 函数识别为它知道如何在 C++ 代码中处理的东西,并替换它们。在这种情况下,替换过于激进。特别是 mutate替换 n()数字为 4(因为嵌套后外部数据框中有 4 行,尽管嵌套的数据框本身各有 6 行)。您可以通过运行以下命令来查看:

library(tidyverse)

set.seed(123)
df <- data_frame(X1 = rep(LETTERS[1:4], 6),
                 X2 = sort(rep(1:6, 4)),
                 ref = sample(1:50, 24),
                 sampl1 = sample(1:50, 24),
                 var2 = sample(1:50, 24),
                 meas3 = sample(1:50, 24))

df1 <- df %>% 
  mutate_at(vars(-one_of(c("X1", "X2", "ref"))), funs(first = . - ref)) %>% 
  mutate_at(vars(contains("first")),  funs(second = . *2 )) %>% print %>% 
  nest(-X1)

debugonce(map)

df1 %>% mutate(n = map(data,
                       ~ summarize_at(.x,
                                      vars(contains("second")),
                                      funs(n_second = n()))))

在 dplyr 0.7.8 中,这会产生以下消息:
debugging in: map(data, ~summarize_at(.x, vars(contains("second")), funs(n_second = 4L)))

当然还有 funs(4)不会工作,因为 4不可调用,因此您会收到错误消息。

也许更有害的是,如果您尝试通过执行以下操作来修复它:
df1 %>% mutate(n = map(data,
                       ~ summarize_at(.x,
                                      vars(contains("second")),
                                      . %>% { n() }))) %>%
  unnest(n)

在 dplyr 0.7.8 中运行没有错误,但给出了错误的答案:计数为 4 而不是 6,因为它使用的是外部数据帧中的行数,而不是嵌套的行数。

幸运的是,由于此更改,所有这些都应在 dplyr 0.8.0 中修复:

#3526: hybrid all or nothing

随着这一变化,对 mutate 的调用不会取代 n() ,因为它不知道如何替换包含 n() 的完整表达式(正如我们所见,周围的表达式可以改变 n() 的含义)。

至于在以前版本的 dplyr 中工作的替代方案,在我看来,您感兴趣的计算可以通过使用 group_by 来实现而无需嵌套。 :
df %>% 
  mutate_at(vars(-one_of(c("X1", "X2", "ref"))), funs(first = . - ref)) %>% 
  mutate_at(vars(contains("first")),  funs(second = . *2 )) %>%
  group_by(X1) %>%  
  summarise_at(vars(contains("second")),
               funs(mean_second = mean(.),
                    n_second = n(),
                    se_second = sd(.)/sqrt(n()) ))

关于r - n() 在 summarise_at() 中使用时行为不一致,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45890739/

相关文章:

r - seq() 跳过序列中的一个值

r - 用 `dplyr`保存残差

r - 整理一个数据框,其中每一列包含多个变量

r - 保留该列表(在 R 中)内没有适当子集的元素(来自向量列表)

r - 您可以静默安装 Bioconductor 软件包吗?

r - r wordclouds 中的彩色类别

r - 如何使用 dplyr 为函数传递单行

r - 带有 if/else 函数的 mutate()

r - 在每个时间单位具有不同观测值的数据框中填充 "implied missing values"

R传播错误: Duplicate identifiers for rows