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

如何向量化dplyr::case_when右侧表达式?解决ID生成异常问题

问题描述

现有数据框:

> data <- data.frame(x = c(1,1,2,2,3,4,5,6), y = c(1,2,3,4,5,6,7,8))
> data
  x y
1 1 1
2 1 2
3 2 3
4 2 4
5 3 5
6 4 6
7 5 7
8 6 8

需求:使用mutate和case_when创建新的id变量,要求:

  • 相同x值的行对应相同id
  • 缺失x的行各拥有唯一id

自定义生成id的函数:

id_function <- function(x, n){
  set.seed(x)
  res <- character(n)
  for(i in seq(n)){
    res[i] <- paste0(sample(c(letters, LETTERS, 0:9), 32), collapse="")
  }
  res
}

id_function(1, 1)
[1] "4dMaHwQnrYGu0PTjgioXKOyW75NRZtcf"

尝试代码:

data %>%
  mutate(my_id = id_function(1234, nrow(.)),
         my_id = dplyr::case_when(!is.na(x) ~ id_function(x, 1),
                                  TRUE ~ my_id))

得到错误结果(非缺失x的行id全部相同):

x y                            my_id
1  1 1 4dMaHwQnrYGu0PTjgioXKOyW75NRZtcf
2  1 2 4dMaHwQnrYGu0PTjgioXKOyW75NRZtcf
3  2 3 4dMaHwQnrYGu0PTjgioXKOyW75NRZtcf
4  2 4 4dMaHwQnrYGu0PTjgioXKOyW75NRZtcf
5 NA 5 0vnws5giVNIzp86BHKuOZ9ch4dtL3Fqy
6 NA 6 IbKU6DjvW9ypitl7qc25Lr4sOwEfghdk
7 NA 7 8oqQMPx6IrkGhXv4KlUtYfcJ5Z1RCaDy
8 NA 8 BRsjumlCEGS6v4ANrw1bxLynOKkF90ao
错误原因

case_when的右侧表达式若不是向量化函数,只会执行一次而非逐行执行。你的id_function并非向量化函数:当传入向量形式的x(比如c(1,1,2,2,...))时,set.seed(x)只会用向量的第一个元素设置种子,后续生成的id都基于这个种子,最终导致所有非缺失x的行得到相同id。

解决方法

方法1:向量化自定义函数

重写函数使其能处理向量输入,为每个唯一x值生成对应id后映射回原数据:

id_function_vec <- function(x) {
  # 提取非缺失的唯一x值
  unique_x <- unique(na.omit(x))
  # 为每个唯一x生成对应id
  id_map <- sapply(unique_x, function(val) {
    set.seed(val)
    paste0(sample(c(letters, LETTERS, 0:9), 32), collapse="")
  })
  # 将id映射到原x向量,NA位置暂留空
  res <- unname(id_map[as.character(x)])
  # 为NA行生成唯一id
  na_indices <- is.na(res)
  if(any(na_indices)) {
    set.seed(1234) # 可选:设置种子保证结果可复现
    res[na_indices] <- replicate(sum(na_indices), 
                                 paste0(sample(c(letters, LETTERS, 0:9), 32), collapse=""))
  }
  res
}

# 使用函数生成id
data %>% mutate(my_id = id_function_vec(x))

方法2:结合分组操作

利用group_by按x分组,每组内生成同一个id,NA行单独处理:

data %>%
  group_by(x) %>%
  mutate(my_id = ifelse(is.na(x), 
                        paste0(sample(c(letters, LETTERS, 0:9), 32), collapse=""),
                        {set.seed(x); paste0(sample(c(letters, LETTERS, 0:9), 32), collapse="")})) %>%
  ungroup()

分组后每个x组内仅执行一次id生成逻辑,同x行的id自然相同;NA行每组仅一行,因此生成唯一id。

方法3:用purrr::map优化case_when写法

若坚持使用case_when,可结合purrr::map_chr实现逐行调用函数:

library(purrr)

data %>%
  mutate(my_id = id_function(1234, nrow(.)),
         my_id = case_when(
           !is.na(x) ~ map_chr(x, ~id_function(.x, 1)),
           TRUE ~ my_id
         ))

map_chr会遍历x的每个元素,单独调用id_function,确保每个x值生成对应的唯一id。

内容的提问来源于stack exchange,提问作者James Martherus

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 07:01:19