如何在tidyverse中用across()按列应用自定义函数?
问题
在tidyverse中基于现有列使用自定义函数创建新列时,希望借助across()的动态重命名功能简化多变量处理,但当前代码未按预期按列执行函数:修改参数P的值时输出异常,尤其当P设为1时,函数似乎按元素而非按列执行,需要更简洁高效的实现方式。
Reprex
set.seed(123) df <- tibble(id = 1:10, rosa = runif(10, min = 20.8, max = 36.5), lila = runif(10, min = 17, max = 37), blaue = runif(10, min = 23.3, max = 32.7)) df[c(2, 5, 8), c(2:4)] <- NA
当前代码
myfun <- function(x, P = 2, na.rm = FALSE){ P ^ (min(x, na.rm = na.rm) - x) } P <- c(2, 1.5, 1.1) # 修改此参数会导致输出异常 names <- c("rosa", "lila", "blaue") df %>% select(!!names) %>% mutate(across(.cols = !!names, .fns = ~myfun(.x, P, na.rm = TRUE), .names = "{.col}_P"))
当前输出
# A tibble: 10 × 6 rosa lila blaue rosa_P lila_P blaue_P <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> 1 25.3 36.1 31.7 0.0718 0.0000526 0.00793 2 NA NA NA NA NA NA 3 27.2 30.6 29.3 0.581 0.439 0.643 4 34.7 28.5 32.6 0.000110 0.0108 0.00401 5 NA NA NA NA NA NA 6 21.5 35.0 30.0 1 0.288 0.605 7 29.1 21.9 28.4 0.00524 1 0.0753 8 NA NA NA NA NA NA 9 29.5 23.6 26.0 0.469 0.856 0.881 10 28.0 36.1 24.7 0.0114 0.0000543 1
警告信息:
1: Problem while computing `..1 = across(...)`. ℹ longer object length is not a multiple of shorter object length 2: Problem while computing `..1 = across(...)`. ℹ longer object length is not a multiple of shorter object length 3: Problem while computing `..1 = across(...)`. ℹ longer object length is not a multiple of shorter object length
预期输出
df %>% select(!!names) %>% mutate(rosa_P = 2^(min(rosa, na.rm = TRUE) - rosa)) %>% mutate(lila_P = 1.5^(min(lila, na.rm = TRUE) - lila)) %>% mutate(blaue_P = 1.1^(min(blaue, na.rm = TRUE) - blaue))
输出结果:
# A tibble: 10 × 6 rosa lila blaue rosa_P lila_P blaue_P <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> 1 25.3 36.1 31.7 0.0718 0.00314 0.514 2 NA NA NA NA NA NA 3 27.2 30.6 29.3 0.0192 0.0302 0.643 4 34.7 28.5 32.6 0.000110 0.0708 0.468 5 NA NA NA NA NA NA 6 21.5 35.0 30.0 1 0.00498 0.605 7 29.1 21.9 28.4 0.00524 1 0.701 8 NA NA NA NA NA NA 9 29.5 23.6 26.0 0.00407 0.515 0.881 10 28.0 36.1 24.7 0.0114 0.00320 1
解决方案
问题根源在于原代码把整个P向量传给了每一列的函数,R会自动循环补齐向量长度,导致计算逻辑偏离预期。我们需要让每一列对应一个专属的P值,实现方式如下:
方法一:保留自定义函数
myfun <- function(x, P = 2, na.rm = FALSE){ P ^ (min(x, na.rm = na.rm) - x) } # 将P设置为命名向量,与列名一一对应 P_vec <- c(rosa = 2, lila = 1.5, blaue = 1.1) df %>% select(all_of(names(P_vec))) %>% mutate(across(all_of(names(P_vec)), ~myfun(.x, P = P_vec[cur_column()], na.rm = TRUE), .names = "{.col}_P"))
方法二:移除自定义函数,简化代码
P_vec <- c(rosa = 2, lila = 1.5, blaue = 1.1) df %>% select(all_of(names(P_vec))) %>% mutate(across(all_of(names(P_vec)), ~P_vec[cur_column()] ^ (min(.x, na.rm = TRUE) - .x), .names = "{.col}_P"))
核心逻辑说明
- 将
P设置为命名向量,确保列名与参数值一一绑定 - 使用
cur_column()获取当前处理的列名,精准匹配对应的P值 - 彻底避免向量循环补齐导致的计算错误,保证每列用专属参数执行计算
内容的提问来源于stack exchange,提问作者doctorate
相关产品推荐
相关产品推荐

