【问题标题】:Using case_when() within mutate_at() to recode several columns with different types of NA在 mutate_at() 中使用 case_when() 重新编码具有不同类型 NA 的多个列
【发布时间】:2020-06-12 18:51:31
【问题描述】:

给定数据:

df <- structure(list(cola = structure(c(5L, 9L, 6L, 2L, 7L, 10L, 3L, 
8L, 1L, 4L), .Label = c("a", "b", "d", "g", "q", "r", "t", "w", 
"x", "z"), class = "factor"), colb = c(156L, 8L, 6L, 100L, 49L, 
31L, 189L, 77L, 154L, 171L), colc = c(0.207140279468149, 0.51990159181878, 
0.402017514919862, 0.382948065642267, 0.488511856179684, 0.263168515404686, 
0.38591041485779, 0.774066215148196, 0.763264901703224, 0.474355421960354
), cold = structure(c(1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L), .Label = c("a", 
"b"), class = "factor")), class = "data.frame", row.names = c(NA, 
-10L))

df
#    cola colb      colc cold
# 1     q  156 0.2071403    a
# 2     x    8 0.5199016    b
# 3     r    6 0.4020175    a
# 4     b  100 0.3829481    b
# 5     t   49 0.4885119    a
# 6     z   31 0.2631685    b
# 7     d  189 0.3859104    a
# 8     w   77 0.7740662    b
# 9     a  154 0.7632649    a
# 10    g  171 0.4743554    b

如果特定行中colc 中的值是&gt;= 0.5,我想用NA 替换该行中所有其他单元格的内容,但该行的cold 的内容除外(即我想保持原样)。

我用dplyr::mutate_at()base::ifelse() 尝试过这个,效果很好:

df %>% mutate_at(vars(-c(cold)), funs(ifelse(colc >= 0.5, NA, .)))

#    cola colb      colc cold
# 1     5  156 0.2071403    a
# 2    NA   NA        NA    b
# 3     6    6 0.4020175    a
# 4     2  100 0.3829481    b
# 5     7   49 0.4885119    a
# 6    10   31 0.2631685    b
# 7     3  189 0.3859104    a
# 8    NA   NA        NA    b
# 9    NA   NA        NA    a
# 10    4  171 0.4743554    b

但我想用dplyr::case_when() 来做这件事,因为我可能需要满足多个替换条件(例如,如果colc &lt; 0.5 &amp; colc &gt;= 0.3 则替换为"foo"。但是case_when() 似乎表现不佳:

df %>% mutate_at(vars(-c(cold)), funs(case_when(colc >= 0.5 ~ NA, TRUE ~ .)))

错误:必须是逻辑向量,而不是因子对象

为什么会发生这种情况,我可以做些什么来解决它?我认为这是因为我试图将具有不同数据类型的多个列转换为 NA。我试图在网上寻找解决方案,但我找不到。

编辑:具体来说,我想保留各个列的数据类型。

【问题讨论】:

  • 为什么要再次拨打colc?为什么不使用匿名,因为colc 已经在mutate_at 调用中?
  • 这行得通,但不漂亮恕我直言:mutate_at(vars(-cold), ~case_when(colc &gt;= 0.5 ~ NA_integer_, TRUE ~ as.integer(.x)))
  • @NelsonGon 你能解释一下你的第一条评论是什么意思吗?
  • 其实并不重要。我最初认为检查colc 本身是重复的,但后来意识到colc 是引用,因此anon 不起作用。

标签: r dplyr conditional-statements typeerror na


【解决方案1】:
library(dplyr)

df %>%
  mutate_at(vars(-c(cold)), ~ case_when(colc >= 0.5 ~ `is.na<-`(., TRUE), TRUE ~ .))

#    cola colb      colc cold
# 1     q  156 0.2071403    a
# 2  <NA>   NA        NA    b
# 3     r    6 0.4020175    a
# 4     b  100 0.3829481    b
# 5     t   49 0.4885119    a
# 6     z   31 0.2631685    b
# 7     d  189 0.3859104    a
# 8  <NA>   NA        NA    b
# 9  <NA>   NA        NA    a
# 10    g  171 0.4743554    b

说明

使用case_when分配NA时,需要指定NA的类型,即NA_integer_NA_real_NA_complex_NA_character_。但是,mutate_at 同时转换多个列,并且这些列具有不同的类型,因此您不能对所有列应用一个语句。理想情况下,可能存在NA_guess 之类的东西来识别类型,但到目前为止我还没有找到。这个方法有点棘手。我使用is.na()将输入向量转换为NA,这些NA将与输入向量的类型相同。例如:

x <- 1:5
is.na(x) <- TRUE ; x
# [1] NA NA NA NA NA
class(x)
# [1] "integer"

y <- letters[1:5]
is.na(y) <- TRUE ; y
# [1] NA NA NA NA NA
class(y)
# [1] "character"

【讨论】:

  • 我基本上是按照这些思路思考的,我希望有人会提到NA_guess 或类似的东西确实存在。真的很喜欢这里使用is.na()
【解决方案2】:

类似于@NelsonGon 的工作:

library(dplyr)

df %>%
        mutate_all(as.character) %>% 
        mutate_at(vars(-c(cold)), 
                  ~case_when(colc >= 0.5 ~ NA_character_, # ifelse(is.numeric(.), NA_real_, NA_character_), 
                             TRUE ~ .
                  )
        ) %>% 
        mutate(colb = as.numeric(colb),
               colc = as.numeric(colc)
        )

#>    cola colb      colc cold
#> 1     q  156 0.2071403    a
#> 2  <NA> <NA>        NA    b
#> 3     r    6 0.4020175    a
#> 4     b  100 0.3829481    b
#> 5     t   49 0.4885119    a
#> 6     z   31 0.2631685    b
#> 7     d  189 0.3859104    a
#> 8  <NA> <NA>        NA    b
#> 9  <NA> <NA>        NA    a
#> 10    g  171 0.4743554    b

【讨论】:

  • 此解决方案不需要将所有列的数据类型更改回其原始类型吗?
  • 好点猜测 colb 是来自 &lt;NA&gt; 错字的类型字符。我已经相应地更新了我的答案,谢谢。
  • 这个方法很棒。但是如果存在很多列并且每个列都有不同的类型,那就进退两难了。最终的 mutate 命令将变得庞大。
猜你喜欢
  • 1970-01-01
  • 2022-11-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-07
  • 1970-01-01
  • 2021-04-28
相关资源
最近更新 更多