You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用magrittr管道在tibble多列运行lm时为何报错?

问题背景

我发现以下两种R代码可以正常运行:

第一种:结合with()与magrittr管道的代码

with(mtcars,
     lm(mpg ~ qsec) %>% broom::tidy() %>% .[2,"p.value",T])

第二种:使用summarise()与across()的代码

mtcars %>% 
  summarise(across(one_of("mpg", "cyl", "disp", "hp"),
                   ~ broom::tidy(lm(. ~ qsec))[2,"p.value",T]
  ))

但当我把lm()的结果通过magrittr管道传递给broom::tidy()再提取p值时,代码报错:

出错代码

mtcars %>% 
  summarise(across(one_of("mpg", "cyl", "disp", "hp"),
                     ~ lm(. ~ qsec) %>% broom::tidy() %>% .[2,"p.value",T]
))

报错信息

16: .handleSimpleError(function (cnd) 
    {
        msg <- glue("Can't compute column `{quo_data$name_auto}`.")
        abort(msg, call = call("across"), parent = cnd)
    }, "invalid 'x' type in 'x && y'", base::quote(drop && length(xo) == 
        1L))
15: `[.tbl_df`(., mpg, 2, "p.value", T)
14: .[mpg, 2, "p.value", T]
13: lm(mpg ~ qsec) %>% broom::tidy() %>% mpg[2, "p.value", T]
12: .Call(dplyr_mask_eval_all_summarise, quo, private)
11: eval()
10: mask$eval_all_summarise(quo)
9: withCallingHandlers(mask$eval_all_summarise(quo), error = function(cnd) {
       msg <- glue("Can't compute column `{quo_data$name_auto}`.")
       abort(msg, call = call("across"), parent = cnd)
   })
8: FUN(X[[i]], ...)
7: lapply(.x, .f, ...)
6: map(quosures, summarise_eval_one, mask = mask)
5: withCallingHandlers({
       for (i in seq_along(dots)) {
           poke_error_context(dots, i, mask = mask)
           context_poke("column", old_current_column)
           dot <- dots[[i]]
           dot <- expand_pick(dot, mask)
           quosures <- expand_across(dot)
           quosures_results <- map(quosures, summarise_eval_one, 
               mask = mask)
           for (k in seq_along(quosures)) {
               quo <- quosures[[k]]
               quo_data <- attr(quo, "dplyr:::data")
               quo_result <- quosures_results[[k]]
               if (is.null(quo_result)) {
                   next
               }
               types_k <- quo_result$types
               chunks_k <- quo_result$chunks
               results_k <- quo_result$results
               if (!quo_data$is_named && is.data.frame(types_k)) {
    ...
4: summarise_cols(.data, dplyr_quosures(...), by, "summarise")
3: summarise.data.frame(., across(one_of("mpg", "cyl", "disp", "hp"), 
       ~lm(. ~ qsec) %>% broom::tidy() %>% .[2, "p.value", T]))
2: summarise(., across(one_of("mpg", "cyl", "disp", "hp"), ~lm(. ~ 
       qsec) %>% broom::tidy() %>% .[2, "p.value", T]))
1: mtcars %>% summarise(across(one_of("mpg", "cyl", "disp", "hp"), 
       ~lm(. ~ qsec) %>% broom::tidy() %>% .[2, "p.value", T]))

原因解析

核心问题是magrittr管道中的.在dplyr的mask环境里被重新绑定了。

在across()的lambda函数(~开头的表达式)中,dplyr会创建特殊的mask环境,把当前遍历的列名(比如mpg)绑定为环境变量。而magrittr的.在这个环境里,会优先指向当前列的向量值,而非管道前一步的输出结果。

从报错信息第13行能看出:lm(mpg ~ qsec) %>% broom::tidy() %>% mpg[2, "p.value", T],这里的.被解析成了mpg列的数值向量,而非broom::tidy()返回的结果数据框,执行mpg[2,"p.value",T]自然报错——数值向量无法用数据框的索引方式取值。

而第二种正常运行的代码是直接嵌套调用,没有用管道,.在这里是lambda函数的参数(代表当前列),不会和管道的.混淆;第一种代码用with(),环境是mtcars数据框,管道的.正常指向前一步输出,因此都没问题。

修复方案

可以用两种方式解决:

  1. 用括号包裹管道里的.表达式,明确指定.是管道前的输出:
mtcars %>% 
  summarise(across(one_of("mpg", "cyl", "disp", "hp"),
                   ~ lm(. ~ qsec) %>% broom::tidy() %>% (.)[2,"p.value",T]
))
  1. 改用magrittr::extract()函数代替[索引,避免.的歧义:
mtcars %>% 
  summarise(across(one_of("mpg", "cyl", "disp", "hp"),
                   ~ lm(. ~ qsec) %>% broom::tidy() %>% magrittr::extract(2, "p.value")
))

内容的提问来源于stack exchange,提问作者Dima Lituiev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 16:04:53