我对 R 相当陌生,但对编程本身并不陌生。我在这里使用我的代码的简化示例。我有一个包含三列(doc_id、tag_list、single_tag)的数据框,所有列都是字符。
df <- data.frame('doc_id' = c('A', 'B', 'C', 'D'),
'tag_list' = c("tagA1,tagA2,tagA3", "tagB1,tabB2", "tagC3, tagC4", "tagD1,tagD3,tagD4"),
'single_tag' = c("tagA2", NA, "tagC", NA)
)
这是我一直在做的事情: 如果 single_tag 的值为 NA,我尝试将其替换为 tag_list 中的值。
df %>% mutate(single_tag = ifelse(is.na(single_tag), tag_list, single_tag))
这将按预期工作,输出如下
doc_id tag_list single_tag
1 A tagA1,tagA2,tagA3 tagA2
2 B tagB1,tabB2 tagB1,tabB2
3 C tagC3, tagC4 tagC
4 D tagD1,tagD3,tagD4 tagD1,tagD3,tagD4
现在我想再次做同样的事情,但是这一次,如果 single_tag 为 NA,我想替换 tag_list 中的第一个值(下面的预期输出)。这是我尝试的代码。
df %>% mutate(single_tag = ifelse(is.na(single_tag), str_split(tag_list, ",")[[1]][1], single_tag))
预期输出(** 添加用于强调):
doc_id tag_list single_tag
1 A tagA1,tagA2,tagA3 tagA2
2 B tagB1,tabB2 **tagB1**
3 C tagC3, tagC4 tagC
4 D tagD1,tagD3,tagD4 **tagD1**
实际输出(** 添加用于强调):
doc_id tag_list single_tag
1 A tagA1,tagA2,tagA3 tagA2
2 B tagB1,tabB2 **tagA1**
3 C tagC3, tagC4 tagC
4 D tagD1,tagD3,tagD4 **tagA1**
我也尝试过使用modify_if
df <- df %>% mutate(single_tag = modify_if(.,is.na(single_tag), ~ str_split(tag_list, ",")[[1]][1], .else=single_tag))
我收到以下错误:
Error in `mutate()`:
ℹ In argument: `single_tag = modify_if(...)`.
Caused by error in `where_if()`:
! length(.p) == length(.x) is not TRUE
我做了一些挖掘,发现 .x 的长度是 3,谓词 .p 的长度是 4。我发现 .p 生成一个由四个逻辑值组成的向量,每个逻辑值对应 df 中的每一行。 .x 我认为只是获取一行中三列的值。
虽然我知道某种方式可以实现我所需要的,但我需要了解这两种情况发生了什么。我觉得我正在使用传统的方式来思考函数和参数如何工作,但在这种情况下它有所不同(也许是因为向量化?)。我尝试阅读文档和代码,但我被难住了。
如果重要的话,我正在使用 R 版本 4.2.3。
如有任何帮助,我们将不胜感激!
按顺序浏览示例:
library(tidyverse)
df %>% mutate(single_tag = ifelse(is.na(single_tag), str_split(tag_list, ",")[[1]][1], single_tag))
有了这个,查看 str_split(tag_list, ",") 的输出是有启发性的:
str_split(df$tag_list, ",")
[[1]]
[1] "tagA1" "tagA2" "tagA3"
[[2]]
[1] "tagB1" "tabB2"
[[3]]
[1] "tagC3" " tagC4"
[[4]]
[1] "tagD1" "tagD3" "tagD4"
如您所见,获取第一个列表的第一个元素类似于获取数据帧第一行中的第一个元素,因此得到结果。
df <- df %>% mutate(single_tag = modify_if(.,is.na(single_tag), tag_list, .else=single_tag))
这个问题是,
.x
(modify_if
的第一个输入),根据文档,应该是一个向量,但你传递一个数据帧作为第一个输入。
str_extract()
获取第一个逗号之前的所有内容(^
是开始,.
是任何字符,*
表示匹配任意次,?
确保它不贪婪(也就是说,如果不需要的话,它不会只匹配整个字符串),(?=,)
是逗号的前瞻)df |> mutate(single_tag = ifelse(is.na(single_tag), tag_list, str_extract(tag_list, "^.*?(?=,)")))
tag_list
列拆分为 actual 列表列,然后获取其中的第一个元素(使用 map()
):df |> mutate(tag_list = str_split(tag_list, ","),
single_tag = ifelse(is.na(single_tag), map_chr(tag_list, 1), single_tag))
map2()
:df |> mutate(single_tag = map2_chr(tag_list, single_tag, \(t, s) ifelse(is.na(s), str_split(t, ",")[[1]], s)))
这能给你你想要的吗?既然您使用的是
dplyr
,我认为使用 stringr
也不是问题。
df |>
mutate(
single_tag = ifelse(is.na(single_tag),
stringr::str_extract(tag_list, "[:alnum:]+(?=,)"),
single_tag)
)
str_extract
的作用是拉出与第二个参数中的正则表达式匹配的字符串的第一部分。