【问题标题】:Creating one variable from a list of variables in R?从R中的变量列表中创建一个变量?
【发布时间】:2019-09-11 17:40:25
【问题描述】:

我在数据框中有一系列变量(超过 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 创建了一列联合变量,而其他变量保持不变。在这种情况下可能会很方便。
  • 是的,很方便。它确实有效。我只是觉得我可能缺少一种更简单的方法,不需要创建统一变量。

标签: r tidyverse dplyr


【解决方案1】:

使用基数 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)

【讨论】:

  • 这是一个很好的解决方案,但是在实际数据中还有其他变量我不想进行模式搜索,所以这需要我索引数据框以仅包含我想要搜索的列第一的。将编辑我的原始帖子以包含此信息。
  • purr 解决方案看起来就像我正在寻找的那样——一行代码不涉及合并变量。
  • @patward5656 我认为purrr 解决方案不会给你预期的输出。我将其更改为使用mutate_at,它应该适用于列范围。此外,您可以直接在 cols 中使用列号来表示 sapply .,例如列 3:51:3 以在这些列中查找模式。
【解决方案2】:

我们可以使用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

或使用base R

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

【讨论】:

  • 这个tidyverse 解决方案似乎只适用于所有列都被搜索的情况。我的真实数据集中还有其他变量,当使用它时,输出全部为 NA。这和reduce函数有关系吗?
  • @patward5656 这很容易解决。 df %&gt;% mutate_at(vars(starts_with("c")), str_detect, pattern = code_regex) %&gt;% reduce("+") %&gt;% mutate(df, indicator = .)
  • c1&lt;-c("T1", "X1", "T6", "R5") c2&lt;-c("R4", "C6", "C7", "X3") c3&lt;-c("C5", "C2", "X4", "T2") z1&lt;-c("C5", "C2", "X4", "T2") df&lt;-data.frame(c1, c2, c3, z1) df %&gt;% mutate_at(vars(starts_with("c")), str_detect, pattern = code_regex) %&gt;% reduce(+) %&gt;% mutate(df, indicator = .) c1 c2 c3 z1 indicator 1 T1 R4 C5 C5 NA 2 X1 C6 C2 C2 NA 3 T6 C7 X4 X4 NA 4 R5 X3 T2 T2 NA Warning message: In Ops.factor(.x, .y) : ‘+’ not meaningful for factors 这似乎产生了 NA。
  • @patward5656 我会使用transmute_at 而不是mutate_at df %&gt;% transmute_at(vars(starts_with("c")), str_detect, pattern = code_regex) %&gt;% reduce(+)
  • 谢谢。我相信transmute_at() 可以完美解决。
【解决方案3】:

基于Rapply

apply(df[cols], 1, function(x) sum(grepl(code_regex, x)))
# [1] 1 0 0 1

【讨论】:

    猜你喜欢
    • 2019-11-21
    • 2016-12-29
    • 2021-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-28
    • 2011-01-04
    相关资源
    最近更新 更多