在R中从多个变量创建一个变量?

7

我有一个数据框中的变量序列(超过100个),我想创建一个指示变量来判断任何变量中是否存在特定的文本模式。以下是使用三个变量的示例。我已经找到了一种解决方案,即使用tidyr::unite()然后使用dplyr::mutate(),但我对不必合并变量的解决方案感兴趣。

c1<-c("T1", "X1", "T6", "R5")
c2<-c("R4", "C6", "C7", "X3")
c3<-c("C5", "C2", "X4", "T2")

df<-data.frame(c1, c2, c3)

  c1 c2 c3
1 T1 R4 C5
2 X1 C6 C2
3 T6 C7 X4
4 R5 X3 T2

code.vec<-c("T1", "T2", "T3", "T4") #Text patterns of interest
code_regex<-paste(code.vec, collapse="|")

new<-df %>% 
  unite(all_c, c1:c3, remove=FALSE) %>% 
  mutate(indicator=if_else(grepl(code_regex, all_c), 1, 0)) %>% 
  select(-(all_c))

  c1 c2 c3 indicator
1 T1 R4 C5 1
2 X1 C6 C2 0
3 T6 C7 X4 0
4 R5 X3 T2 1

以下是一个产生所期望结果的示例,但我感觉应该有一种不需要合并变量即可使用 tidyverse 完成此操作的方法。SAS 可以通过 ARRAY 语句和 DO 循环轻松处理这个问题,我希望 R 也有很好的处理方法。
真实数据框除了“c”字段之外还有许多其他变量,因此涉及到搜索每个列的解决方案将需要对数据框进行子集划分,仅包含我要搜索的变量,然后再将数据与其他变量连接起来。

你说你不想使用 unite,但值得注意的是,传递参数 remove = FALSE 会使 unite 创建一个合并变量的列,同时保留其他列。在这种情况下可能很方便。 - camille
是的,这很方便。而且它确实有效。我只是觉得可能有一种更简单的方法,不需要创建一个联合变量。 - patward5656
3个回答

6

使用基本R,我们可以使用sapplygrepl函数在每个列中查找模式,并将1分配给具有超过0个匹配项的行。

df$indicator <- as.integer(rowSums(sapply(df, grepl, pattern = code_regex)) > 0)

df
#  c1 c2 c3 indicator
#1 T1 R4 C5         1
#2 X1 C6 C2         0
#3 T6 C7 X4         0
#4 R5 X3 T2         1

如果只有几列数据并且我们只想在以"c"开头的列中应用它,我们可以使用grep进行过滤。
cols <- grep("^c", names(df))
as.integer(rowSums(sapply(df[cols], grepl, pattern = code_regex)) > 0)

通过使用dplyr,我们可以进行:

library(dplyr)

df$indicator <- as.integer(df %>%
              mutate_at(vars(c1:c3), ~grepl(code_regex, .)) %>%
              rowSums() > 0)

这是一个不错的解决方案,但在实际数据中还有其他变量,我不想进行模式搜索,因此这需要我对数据框进行索引,只包括我想要先搜索的列。我会编辑我的原始帖子,包括这些信息。 - patward5656
呼噜解决方案看起来就像我所寻找的——只需一行代码,不涉及变量合并。 - patward5656
@patward5656 我认为 purrr 的解决方案不会给您期望的输出。我改用了 mutate_at,它应该适用于一系列列。此外,您可以在 cols 中直接使用列号进行 sapply,例如列 3:51:3,以查找这些列中的模式。 - Ronak Shah

3
我们可以使用 tidyverse
library(tidyverse)
df %>%
    mutate_all(str_detect, pattern = code_regex) %>%
    reduce(`+`) %>% 
    mutate(df, indicator = .)
#  c1 c2 c3 indicator
#1 T1 R4 C5         1
#2 X1 C6 C2         0
#3 T6 C7 X4         0
#4 R5 X3 T2         1

或者使用 基础R语言

Reduce(`+`, lapply(df, grepl, pattern = code_regex))
#[1] 1 0 0 1

这个 tidyverse 的解决方案似乎只适用于搜索所有列的情况。我在真实数据集中有其他变量,当使用它进行搜索时,输出全部为 NA。这是否与 reduce 函数有关? - patward5656
@patward5656 这很容易解决。df %>% mutate_at(vars(starts_with("c")), str_detect, pattern = code_regex) %>% reduce("+") %>% mutate(df, indicator = .) - akrun
1
@patward5656 我会使用 transmute_at 而不是 mutate_atdf %>% transmute_at(vars(starts_with("c")), str_detect, pattern = code_regex) %>% reduce(+) - akrun
@patward5656 请按照我上面的评论使用 transmute_at。对我来说它很有效。df %>% transmute_at(vars(starts_with("c")), str_detect, pattern = code_regex) %>% reduce(+)# [1] 1 0 0 1 - akrun
1
谢谢。我相信 transmute_at() 完美地解决了它。 - patward5656
显示剩余2条评论

1
基于 applyR 基础。
apply(df[cols], 1, function(x) sum(grepl(code_regex, x)))
# [1] 1 0 0 1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接