【问题标题】:Preserving column types when applying ifelse() across columns of different types in R在 R 中跨不同类型的列应用 ifelse() 时保留列类型
【发布时间】:2022-01-15 12:37:33
【问题描述】:

这似乎是一个相当简单的任务,但是在研究了ifelse()dplyr::if_else() 的文档以及关于 SO 上关于将ifelse() 应用于数据框中的多个列的几个类似帖子之后,我无法弄清楚.

我的目标:我有以下数据框,其中包含不同数据类型的列。在每一行上,如果列“有效”表示错误,我想将前 3 列中的值重置为 NA。

问题:我使用dplyr::across()ifelse()来改变我想要的值,但是日期列date和因子列team被强制转换为数字(如图在下面的代表中),这是不可取的。我知道dplyr::if_else() 保留了数据类型,但它也不适用于不同数据类型的列。

我知道tdf[tdf$valid == FALSE, !grepl("valid", names(tdf))] <- NA 可以实现我的目标,但我更喜欢 tidyverse 方法,我可以在我的数据清理管道中使用它。非常感谢!

library(dplyr)

tdf <- tibble(
  date = c(as.Date("2021-12-10"), as.Date("2021-12-11")),
  team = factor(1:2, labels = c("T1", "T2")),
  score = 3:4,
  valid = c(TRUE, FALSE)
)

tdf
#> # A tibble: 2 x 4
#>   date       team  score valid
#>   <date>     <fct> <int> <lgl>
#> 1 2021-12-10 T1        3 TRUE 
#> 2 2021-12-11 T2        4 FALSE

tdf %>% mutate(across(-valid, ~ ifelse(valid, ., NA)))
#> # A tibble: 2 x 4
#>    date  team score valid
#>   <dbl> <int> <int> <lgl>
#> 1 18971     1     3 TRUE 
#> 2    NA    NA    NA FALSE

reprex package (v2.0.1) 于 2021 年 12 月 10 日创建

【问题讨论】:

    标签: r if-statement dplyr across


    【解决方案1】:

    这是一个未解决的问题:

    @akrun 给出了很好的解释以及如何解决您的具体问题!

    但如果你想保留ifelse:

    Fabian Werner 在 2015 年使用自定义 safe.ifelse

    提供了在您的具体情况下唯一可行的解​​决方案(包含日期和因素)

    How to prevent ifelse() from turning Date objects into numeric objects

    safe.ifelse <- function(cond, yes, no) {
      class.y <- class(yes)
      if (class.y == "factor") {
        levels.y = levels(yes)
      }
      X <- ifelse(cond,yes,no)
      if (class.y == "factor") {
        X = as.factor(X)
        levels(X) = levels.y
      } else {
        class(X) <- class.y
      }
      return(X)
    }
    
    tdf %>% mutate(across(-valid, ~ safe.ifelse(valid, ., NA)))
    
    
      date       team  score valid
      <date>     <fct> <int> <lgl>
    1 2021-12-10 T1        3 TRUE 
    2 NA         NA       NA FALSE
    

    【讨论】:

    • 啊,我错过了那个帖子!非常感谢@TarJae 在这里分享解决方案。在不依赖其他包的情况下学习如何解决它绝对有用!
    【解决方案2】:

    使用 case_when 中的默认 (TRUE) 选项,该选项根据类型返回 NA

    library(dplyr)
    tdf %>%
        mutate(across(-valid, ~ case_when(valid ~ .)))
    

    -输出

    # A tibble: 2 × 4
      date       team  score valid
      <date>     <fct> <int> <lgl>
    1 2021-12-10 T1        3 TRUE 
    2 NA         <NA>     NA FALSE
    

    或者另一个选项是replace

    tdf %>% 
       mutate(across(-valid, ~ replace(., !valid, NA)))
    # A tibble: 2 × 4
      date       team  score valid
      <date>     <fct> <int> <lgl>
    1 2021-12-10 T1        3 TRUE 
    2 NA         <NA>     NA FALSE
    

    根据?ifelse

    结果的模式可能取决于 test 的值(见示例),结果的类属性(见 oldClass)取自 test,可能不适用于从 yes 和 no 中选择的值。

    有时最好使用诸如

    之类的结构

    (tmp

    ,可能扩展为处理测试中的缺失值。

    【讨论】:

    • 多么全面的答案!非常感谢,@akrun! case_whenreplace 的两种解决方案都运行良好且非常整洁!我需要更多地研究我每天使用的工具......?
    猜你喜欢
    • 2019-11-26
    • 2020-06-25
    • 2015-01-07
    • 1970-01-01
    • 2016-07-16
    • 2020-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多