如何根据 R 中其他因子列的信息创建新列?

How to create a new column based on information from other factor columns in R?

提问人:larsroarf 提问时间:11/9/2023 最后编辑:larsroarf 更新时间:11/9/2023 访问量:85

问:

一段时间以来,我一直试图找到一个有效的解决方案,用于如何根据 R 中其他因子列的信息创建新列。我提出了一个可行的解决方案,但非常乏味,而一个远不那么乏味但只能部分有效的解决方案。我通常使用包含NA的调查数据,这是问题的重要组成部分。如果我在提出这个问题时做错了什么,请原谅我,因为这既是我在 SO 的第一篇文章,也是我的第一个 Reprex。这个问题与 SO 提出的其他问题类似,但我无法调整任何现有的解决方案来解决我的问题。

[由于不清楚,在第一次响应后编辑] 我想要得到的是我的 tible 中的一个新列,它仅在用于生成新列的所有列都是 NA 时返回 NA,而如果至少一列是“yes”,则返回“yes”,如果没有列包含“yes”,则返回“no”。

这里有一个 Reprex 来说明我的问题:

library(tidyverse)

# create the table
df <- tibble(
  othervar_2007 = c(NA, "yes", "no", "no", "no"),
  morv1_2007 = c(NA, "yes", "no", "no", NA),
  morv2_2007 = c(NA, "yes", NA, "no", "no"),
  morv3_2007 = c(NA, "no", "yes", "no", "no"),
  morv4_2007 = c(NA, "no", "no", "no", "no"),
  morv_othervar = c(NA, "yes", "no", "no", "no")
)

# show table
df
#> # A tibble: 5 × 6
#>   othervar_2007 morv1_2007 morv2_2007 morv3_2007 morv4_2007 morv_othervar
#>   <chr>         <chr>      <chr>      <chr>      <chr>      <chr>        
#> 1 <NA>          <NA>       <NA>       <NA>       <NA>       <NA>         
#> 2 yes           yes        yes        no         no         yes          
#> 3 no            no         <NA>       yes        no         no           
#> 4 no            no         no         no         no         no           
#> 5 no            <NA>       no         no         no         no

# alternative 1
df <- df %>% 
  mutate(newvar1 = case_when(
    morv1_2007 == "yes" | morv2_2007 == "yes" | morv3_2007 == "yes" | morv4_2007 == "yes" ~ "Yes",
    morv1_2007 == "no" | morv2_2007 == "no" | morv3_2007 == "no" | morv4_2007 == "no" ~ "No")
  )

df %>% 
  count(newvar1)
#> # A tibble: 3 × 2
#>   newvar1     n
#>   <chr>   <int>
#> 1 No          2
#> 2 Yes         2
#> 3 <NA>        1

# alternative 2
df <- df %>%
  rowwise %>%
  mutate(newvar2 = if_else(rowSums(across(c(morv1_2007, morv2_2007, morv3_2007, morv4_2007), ~ .x == "yes"))>0, "Yes", "No"))

df %>% 
  count(newvar2)
#> # A tibble: 3 × 2
#> # Rowwise: 
#>   newvar2     n
#>   <chr>   <int>
#> 1 No          1
#> 2 Yes         1
#> 3 <NA>        3
Created on 2023-11-09 with reprex v2.0.2

Tibble 包含四列,这些列应该用于改变新列,以及两个模拟列,它们应该保持原样。命名这些列也是为了说明相关列的选择问题。

标记为备选方案 1 的解决方案有效,但当要从中获取信息的列数变大时,该解决方案非常繁琐。我也无法找到选择列的好解决方案,例如,通过使用 starts_with 和 ends_with。

但是,如果至少一列为 NA,则标记为备选方案 2 的解决方案将返回 NA。当添加 na.rm = TRUE 时,它对仅包含 NA 的行返回“否”。选择问题也适用于此处。

希望在R和编码方面更有经验的人可以帮助我。

编辑后续 TIDYVERSE 解决方案

df <- df %>%
  rowwise %>%
  mutate(newvar3 = if(all(is.na(c_across(matches("^morv.*2007$"))))) {
NA_character_
} else if ("yes" %in% c_across("^morv.*2007$"))) {
"yes"
} else {
"no"})

这返回了我所追求的,但速度很慢。这里建议的代码要快得多。

R因子 突变

评论


答:

1赞 Friede 11/9/2023 #1

基于

“这是我想得到的 [...]:我脑海中的一个新专栏 仅当用于生成新列的所有列都使用时才返回 是 NA,如果至少有一个“yes”,则返回“yes”,如果没有,则返回“no” 列包含“yes”。NA

请注意,我已更改为“否”以进行测试。df[1, 1]df[1, 1] <- "no"

我建议采用以下方法:

df$newvar <- 
  apply(X = df[, grepl("morv\\d{1}", colnames(df))], 
        MARGIN = 1L, 
        FUN = \(x) ifelse(
          test = all(is.na(x)), 
          yes = NA, 
          no = ifelse(
            test = any(x == "yes", na.rm = TRUE), 
            yes = "yes", 
            no = "no")
          )
        )  

这给了

> df
# A tibble: 5 × 7
  othervar_2007 morv1_2007 morv2_2007 morv3_2007 morv4_2007 morv_othervar newvar
  <chr>         <chr>      <chr>      <chr>      <chr>      <chr>         <chr> 
1 no            NA         NA         NA         NA         NA            NA    
2 yes           yes        yes        no         no         yes           yes   
3 no            no         NA         yes        no         no            yes   
4 no            no         no         no         no         no            no    
5 no            NA         no         no         no         no            no 

评论

0赞 larsroarf 11/9/2023
感谢您的迅速回复。不幸的是,我看到我最初的帖子不清楚。我失去了写问题的第一次尝试,并且在尝试重写时错过了一些东西。这是我想要获得的内容(我也会检查我是否可以编辑原始问题):我的 tibble 中的一个新列,仅当用于生成新列的所有列都是 NA 时才返回 NA,而如果至少有一个“yes”,则返回“yes”,如果没有列包含“yes”,则返回“no”。
0赞 larsroarf 11/9/2023
非常感谢,是时候再玩这个建议了。
0赞 larsroarf 11/9/2023
发现这段代码有一个小问题,当然这又与我这边的问题描述不佳有关。第 5 行返回 NA,该行应返回“no”。我玩了你的代码,并找到了一个使用 Tidyverse 的解决方案。将在此处发布。但是您的解决方案要快得多,是否可以对其进行调整以解决我在这里的最后一个问题?
0赞 Friede 11/9/2023
好吧,我又加了一次。我们错过了.na.rm = TRUEany()