这似乎是一项相当简单的任务,但在研究了ifelse()
、dplyr::if_else()
的文档以及SO上关于将ifelse()
应用于数据帧中多列的几篇类似文章后,我无法理解。
我的目标:我有以下数据框架,其中包含不同数据类型的列。在每一行上,我想将前3列中的值重置为NA,如果Column";有效的";表示错误。
问题:我使用dplyr::across()
和ifelse()
根据需要更改值,但日期列date
和因子列team
被强制为数字(如下面的reprex所示(,这是不可取的。我知道dplyr::if_else()
保留了数据类型,但它也不能跨不同数据类型的列工作。
我知道tdf[tdf$valid == FALSE, !grepl("valid", names(tdf))] <- NA
可以实现我的目标,但我更喜欢一种不同的方法,可以在我的数据清理管道中使用。非常感谢!
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
创建于2021-12-10由reprex包(v2.0.1(
使用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
结果的模式可能取决于测试的值(参见示例(,结果的类属性(参见oldClass(取自测试,可能不适合从是和否中选择的值。
有时最好使用之类的构造
(tmp<-yes;tmp[!test]<-no[!test];tmp(
,可能扩展为处理测试中丢失的值。
这是一个尚未解决的问题:
@akrun给出了一个很好的解释,以及如何解决您的具体问题!
但如果你想保留ifelse
:
Fabian Werner在2015年使用自定义的safe.ifelse
为您的特定案例提供了唯一有效的解决方案(带日期和因素(
如何防止ifelse((将Date对象转换为数字对象
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