【问题标题】:Replace content in one column with another column R将一列中的内容替换为另一列 R
【发布时间】:2022-01-27 11:12:10
【问题描述】:

我正在处理一些调查数据,我想用另一个调查项目替换一个调查项目/列的内容,同时保留原始单元格内容。 Ex - 如果 Q2_1.x 缺失(缺失编码为 \"-99\" 或编码为 character_NA),则将 Q2_1.x 替换为 Q2_1.y。

这是我的数据示例:

ibrary(dplyr)
library(magrittr)
ibrary(readr)

org_dat <- read_table(\'ID   Q2_1.x  Q2_2.x  Q2_1.y  Q2_2.y  Q14_1.x Q14_1.y Q15
    1   Yes NA  NA  NA  Sometimes   NA  NA
    2   -99 NA  No  NA  NA  Always  Yes
    3   Yes NA  Yes NA  NA  NA  NA
    4   -99 NA  NA  No  NA  Yes No 
    5   NA  -99 NA  NA  NA  Always  NA
    6   -99 NA  NA  No  NA  NA  NA\') %>% mutate_all(as.character)

这是我想要的输出:

dat_out <- read_table(\'ID   Q2_1    Q2_2    Q14_1   Q15
1   Yes NA  Sometimes   NA
2   No  NA  Always  Yes
3   Yes NA  NA  NA
4   -99 No  Yes No
5   NA  -99 Always  NA
6   -99 No  NA  NA\')

当前解决方案我知道我可以单独替换这些列中的每一个,但是我有很多列要处理,我想使用一种智能的 dplyr/grepl 方法来解决这个问题!有任何想法吗?我总是用 Q*.y 替换 Q*.x。

org_dat %>% mutate(Q2_1.x = case_when(!is.na(Q2_1.y) &
                                        Q2_1.x == \'-99\'| is.na(Q2_1.x) ~ Q2_1.y,
                                      TRUE ~ Q2_1.x)) %>%
mutate(Q2_2.x = case_when(!is.na(Q2_2.y) &
                            Q2_2.x == \'-99\'| is.na(Q2_2.x) ~ Q2_2.y,
                          TRUE ~ Q2_2.x)) %>% 
  
  mutate(Q14_1.x = case_when(!is.na(Q14_1.y) &
                              Q14_1.x == \'-99\'| is.na(Q14_1.x) ~ Q14_1.y,
                            TRUE ~ Q14_1.x)) %>%
  rename(Q2_1 = Q2_1.x,
         Q2_2 = Q2_2.x,
         Q14_1 = Q14_1.x) %>%
  select(-matches(\"x|y\"))
  • 我无法重现您的数据。请使用 dput(org_data) 而不是 read_table
  • @GuedesBF 我将库添加到我的代码中,现在应该可以工作了!

标签: r regex dplyr grepl


【解决方案1】:

更短的方法。只需从变量名称中去除所有".x" 后缀,然后转换名称不是"ID" 或不以".y" 结尾的变量。对于这些变量中的每一个,get0 与带有".y" 后缀的对应项进行如下替换。请注意,如果没有带有 ".y" 后缀的对应项,get0 将返回 NULL 并且 idx 因此折叠为 integer(0)。结果,变量按原样返回。

library(dplyr)

ord_dat %>% 
  rename_with(~sub("\\.x", "", .), ends_with(".x")) %>% 
  transmute(ID, across(!ID & !ends_with(".y"), ~{
    .y <- get0(paste0(cur_column(), ".y"))
    idx <- which(.x %in% c("-99", NA) & !is.na(.y))
    replace(.x, idx, .y[idx])
  }))

这是一个 tidyverse 方法。我们会

  1. pivot_longer 将数据集分为四列:IDQ(uestion)、xy
  2. x 显示NA"-99"y 显示非NA 值的位置替换x 中的任何值。
  3. 删除名称来自Q 和值来自x 的数据集列ypivot_wider

    这些步骤并不复杂,但它们确实需要对功能进行精细控制。这使得代码有点长:

    library(dplyr)
    library(tidyr)
    
    ord_dat %>% 
      pivot_longer(
        -ID, 
        names_to = c("Q", ".value"), 
        names_pattern = "([^\\.]*)\\.?([^\\.]*)", 
        names_transform = list(.value = ~replace(., . == "", "x"))
      ) %>% 
      mutate(x = {
        idx <- which(x %in% c("-99", NA) & !is.na(y))
        replace(x, idx, y[idx])
      }, y = NULL) %>% 
      pivot_wider(names_from = Q, values_from = x)
    

    输出

    # A tibble: 6 x 5
      ID    Q2_1  Q2_2  Q14_1     Q15  
      <chr> <chr> <chr> <chr>     <chr>
    1 1     Yes   NA    Sometimes NA   
    2 2     No    NA    Always    Yes  
    3 3     Yes   NA    NA        NA   
    4 4     -99   No    Yes       No   
    5 5     NA    -99   Always    NA   
    6 6     -99   No    NA        NA   
    

【讨论】:

  • 正如我的回答一样,我相信使用 coalesce 会使这个答案更清晰:org_dat %&gt;% transmute(ID, across(!ID &amp; !ends_with(".y"), ~coalesce(.x, get0(sub('\\.x', '.y', cur_column()))))) %&gt;% rename_with(~sub("\\.x", "", .), ends_with(".x"))
  • 顺便说一句,很好地使用了get0。不知道。
  • @GuedesBF 我考虑过coalesce,但这会导致.x 中的NAs 变成.y 中的"-99"。我认为这与 OP 共享的所需输出不匹配。
  • @ekoam 谢谢你!您能否再解释一下大括号内发生的事情?什么是 ".y" ".x" ?粘贴有什么作用?在这种情况下替换如何工作?我太依赖于tidyverse,我想牢牢掌握你的方法。
  • 1) .y 只是我选择的一个变量名。在~ lambda 表达式中使用的.x 表示传递给函数的第一个参数。在这种情况下,这将是一个既不是ID 也不是以".y" 结尾的列。 2)例如paste0(c("A", "B"), "1")给你一个字符向量c("A1", "B1")。请注意,cur_column() 为您提供当前正在评估的列名。 3)replace(c(1,2,3), c(1,2), c(100,1000)) 给出c(100,1000,3)。它替换给定位置的值。 @NewBee
【解决方案2】:

这里答案的关键是首先将用户定义的 NA 转换为带有 na_if 的真实 nas,然后是带有成对列的 coalesce

library(dplyr)
library(stringr)
org_dat %>%
    na_if(-99) %>%
    mutate(across(ends_with('.x'),
                  ~coalesce(.x, get(deparse(substitute(.x)) %>%
                                        str_replace('\\.x', '.y'))))) %>%
    select(-ends_with('.y')) %>%
    rename_with(~str_remove(.x, '\\..$'))


# A tibble: 6 × 5
  ID    Q2_1  Q2_2  Q14_1     Q15  
  <chr> <chr> <chr> <chr>     <chr>
1 1     Yes   NA    Sometimes NA   
2 2     No    NA    Always    Yes  
3 3     Yes   NA    NA        NA   
4 4     NA    No    Yes       No   
5 5     NA    NA    Always    NA   
6 6     NA    No    NA        NA   

编辑

原始答案没有提供实际所需的输出,因为它用 NA 替换了所有用户定义的 NA(-99)。

如果 OP 想要保留这些用户定义的 NA,我们可以执行以下操作: 首先,将所有列更改为字符。 其次,将 data.frame 拆分为由前缀“Q{number}_{number}”和split.default 配对的数据帧。 最后,modify 包含两列('x' 和 'y' 对)的所有列表元素,modify_ifcoalesce

library(dplyr)
library(purrr)

org_dat %>%
    mutate(across(everything(), as.character)) %>%
        split.default(sub('\\..$', '', names(org_dat))) %>%
        modify_if(.p=~ncol(.x)==2, .f = ~coalesce(.x[[1]], .x[[2]])) %>%
        bind_cols() %>%
        select(ID, Q2_1, Q2_2, Q14_1, Q15)

# A tibble: 6 × 5
  ID    Q2_1  Q2_2  Q14_1     Q15  
  <chr> <chr> <chr> <chr>     <chr>
1 1     Yes   NA    Sometimes NA   
2 2     -99   NA    Always    Yes  
3 3     Yes   NA    NA        NA   
4 4     -99   No    Yes       No   
5 5     NA    -99   Always    NA   
6 6     -99   No    NA        NA   

【讨论】:

  • 谢谢,这很有帮助!
猜你喜欢
  • 1970-01-01
  • 2020-03-11
  • 2015-11-21
  • 1970-01-01
  • 2019-03-28
  • 1970-01-01
  • 2020-08-13
  • 1970-01-01
相关资源
最近更新 更多