首页 > 解决方案 > 按名称将 tibble 列映射到另一列

问题描述

我有一个字符列,field_name其中包含其他列的名称。我想创建另一列,field_value,它将包含基于field_name每一行的值。如果field_name是 NA,field_value也应该是 NA。我对使用不感兴趣rowwise()(出于性能原因)。

# A tibble: 3 × 6
  category stage is_solid saturation is_vaccinated field_name
  <chr>    <chr> <lgl>         <dbl> <lgl>         <chr>
1 oncology I     TRUE             NA TRUE          stage
2 covid19  NA    NA               95 FALSE         saturation
3 other    NA    NA               NA TRUE          NA

预期结果:

# A tibble: 3 × 7
  category stage is_solid saturation is_vaccinated field_name field_value
  <chr>    <chr> <lgl>         <dbl> <lgl>         <chr>      <chr>
1 oncology I     TRUE             NA TRUE          stage      I
2 covid19  NA    NA               95 FALSE         saturation 95
3 other    NA    NA               NA TRUE          NA         NA
df <- 
    tibble(
      stringsAsFactors = FALSE,
              category = c("oncology", "covid19", "other"),
                 stage = c("I", NA, NA),
              is_solid = c(TRUE, NA, NA),
            saturation = c(NA, 95, NA),
         is_vaccinated = c(TRUE, FALSE, TRUE),
            field_name = c("stage", "saturation", NA)
    )

更新:当我们在阶段有其他值时,@deschen 的答案不起作用。例如,

df <- 
  tibble(
    stringsAsFactors = FALSE,
    category = c("oncology", "covid19", "other"),
    stage = c("I", "II", "III"),
    is_solid = c(TRUE, NA, NA),
    saturation = c(NA, 95, NA),
    is_vaccinated = c(TRUE, FALSE, TRUE),
    field_name = c("stage", "saturation", NA)
  )

标签: rtidyverse

解决方案


您可以将field_name值作为字符向量传递across,将其转换为字符值,然后使用coalesce将几个新生成的列合并为一个。注意,由于coalese还不能正常工作across,我们必须使用do.call. 这可能会在某个时候改变。

另请注意,您可能希望确保对临时列使用真正唯一的命名模式,以免在此过程中干扰任何潜在的现有列。

尽管您排除了rowwise解决方案,但我会对它的外观非常感兴趣。我猜下面的解决方案在数量级上不会比rowwise.

library(tidyverse)
df %>%
  mutate(across(all_of(na.omit(df$field_name)), ~as.character(.), .names = "{.col}_temp"),
         field_value = do.call(coalesce, across(ends_with("_temp")))) %>%
  select(-ends_with("_temp"))

这使:

# A tibble: 3 x 7
  category stage is_solid saturation is_vaccinated field_name field_value
  <chr>    <chr> <lgl>         <dbl> <lgl>         <chr>      <chr>      
1 oncology I     TRUE             NA TRUE          stage      I          
2 covid19  <NA>  NA               95 FALSE         saturation 95         
3 other    <NA>  NA               NA TRUE          <NA>       <NA>

根据 TO 的新数据示例进行更新。现在涉及更多,可以通过pivot_longer/wider往返来完成。

df %>%
  mutate(across(all_of(na.omit(df$field_name)), ~as.character(.), .names = "{.col}_temp")) %>%
  pivot_longer(cols = ends_with("_temp")) %>%
  filter(str_detect(name, field_name) | (is.na(field_name) & is.na(value))) %>%
  pivot_wider() %>%
  mutate(field_value = do.call(coalesce, across(ends_with("_temp")))) %>%
  select(-ends_with("_temp"))

这使:

# A tibble: 3 x 7
  category stage is_solid saturation is_vaccinated field_name field_value
  <chr>    <chr> <lgl>         <dbl> <lgl>         <chr>      <chr>      
1 oncology I     TRUE             NA TRUE          stage      I          
2 covid19  II    NA               95 FALSE         saturation 95         
3 other    III   NA               NA TRUE          NA         NA  

推荐阅读