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

如何在R函数中评估字符向量元素以动态创建数据变量

生成水果虚拟变量的问题解决

问题背景

给定如下R数据集:

library(tibble)
df <- data.frame(
  basket = c("A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8"),
  fruits = c("orange, apple", "apple", "pineapple, banana", "strawberry, apple, watermelon", "blueberry, pear", "apple, papaya", "apple", "strawberry")
)

其中basket为篮子名称,fruits是每个篮子的水果列表(逗号分隔)。需要为指定水果(apple、strawberry、pineapple)生成虚拟变量,标记每个篮子是否包含对应水果(1=包含,0=不包含)。

用户尝试编写自定义函数批量生成变量,但遇到错误:

fruits_list <- c("apple", "strawberry", "papaya")

create_ind_vars <- function(df, num) {
  df %>% 
    mutate("ind_{{num}}" := if_else(
      charges == !! rlang::sym(paste0(num))
      | str_starts(charges, !! rlang::sym(paste0(num, ",")))
      | str_detect(charges, !! rlang::sym(paste0(",", num, ",")))
      | str_ends(charges, !! rlang::sym(paste0(",", num))),
      1, 0
    ))
}

testdat <- create_ind_vars(df, fruits_list)

报错信息:

Error in `rlang::sym()`:
! Can't convert a character vector to a symbol.
Run `rlang::last_trace()` to see where the error occurred.

错误原因

  1. 变量名误写:函数中charges应为数据集里的fruits列;
  2. 语法误用:rlang::sym()用于转换单个字符为符号,但传入的是字符向量,且无需用该函数——直接字符串匹配即可;
  3. 函数逻辑缺陷:设计为处理单个水果,但直接传入向量导致批量处理失败;
  4. 变量命名错误:"ind_{{num}}"的写法不符合动态命名语法,需结合glue风格语法与:=使用。

解决方案

方法1:使用dplyr::across(推荐,简洁高效)

利用across批量处理指定水果,结合stringr::str_detect精准匹配字符串,同时用glue语法生成变量名:

library(dplyr)
library(stringr)

target_fruits <- c("apple", "strawberry", "pineapple")

df_result <- df %>%
  mutate(
    across(
      all_of(target_fruits),
      ~as.integer(str_detect(fruits, regex(paste0("\\b", .x, "\\b"), ignore_case = FALSE))),
      .names = "{.col}"
    )
  )

print(df_result)

说明:

  • \\b是单词边界,避免匹配pineapple时误判包含apple;
  • as.integer将逻辑值(TRUE/FALSE)转换为1/0;
  • .names = "{.col}"直接用水果名作为新变量名,符合需求格式。

方法2:修正自定义函数+循环迭代

若坚持使用自定义函数,调整为处理单个水果,再循环遍历目标列表:

library(dplyr)
library(stringr)

create_ind_var <- function(df, fruit) {
  df %>%
    mutate(
      !!fruit := as.integer(
        str_detect(fruits, regex(paste0("\\b", fruit, "\\b")))
      )
    )
}

target_fruits <- c("apple", "strawberry", "pineapple")
df_result <- df
for (fruit in target_fruits) {
  df_result <- create_ind_var(df_result, fruit)
}

print(df_result)

也可使用purrr::reduce简化循环:

library(purrr)
df_result <- reduce(target_fruits, create_ind_var, .init = df)

方法3:用tidyr拆分后计数

先把水果拆分成单独行,统计每个篮子的水果出现情况,再合并回原数据:

library(tidyr)
library(dplyr)

target_fruits <- c("apple", "strawberry", "pineapple")

df_result <- df %>%
  left_join(
    df %>%
      separate_rows(fruits, sep = ",\\s*") %>%
      filter(fruits %in% target_fruits) %>%
      mutate(value = 1) %>%
      pivot_wider(
        id_cols = basket,
        names_from = fruits,
        values_from = value,
        values_fill = 0
      ),
    by = "basket"
  ) %>%
  mutate(across(all_of(target_fruits), ~replace_na(.x, 0)))

print(df_result)

说明:

  • separate_rows将逗号分隔的水果拆分为单独行;
  • pivot_wider将行转列为虚拟变量;
  • replace_na把未匹配到目标水果的篮子填充为0。

内容的提问来源于stack exchange,提问作者Andrew Skelton

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 13:54:55