在 R 中跨不同类型的列应用 ifelse() 时保留列类型
Preserving column types when applying ifelse() across columns of different types in R
这似乎是一个相当简单的任务,但在研究了 ifelse()
、dplyr::if_else()
的文档以及关于应用 ifelse()
到数据框中的多列。
我的目标: 我有以下数据框,其中包含不同数据类型的列。在每一行上,如果列“有效”指示错误,我想将前 3 列中的值重置为 NA。
问题: 我使用 dplyr::across()
和 ifelse()
来更改我想要的值,但是日期列 date
和因子列 team
被强制转换为数字(如下面的 reprex 所示),这是不可取的。我知道 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 创建
使用 case_when
中的默认 (TRUE
) 选项,其中 returns 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
The mode of the result may depend on the value of test (see the examples), and the class attribute (see oldClass) of the result is taken from test and may be inappropriate for the values selected from yes and no.
Sometimes it is better to use a construction such as
(tmp <- yes; tmp[!test] <- no[!test]; tmp)
, possibly extended to handle missing values in test.
这是一个未解决的问题:
@akrun 给出了很好的解释以及如何解决您的具体问题!
但万一你想保留 ifelse
:
Fabian Werner 在 2015 年使用自定义 safe.ifelse
[为您的具体情况提供了唯一可行的解决方案 (带日期和因素) =27=]
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
这似乎是一个相当简单的任务,但在研究了 ifelse()
、dplyr::if_else()
的文档以及关于应用 ifelse()
到数据框中的多列。
我的目标: 我有以下数据框,其中包含不同数据类型的列。在每一行上,如果列“有效”指示错误,我想将前 3 列中的值重置为 NA。
问题: 我使用 dplyr::across()
和 ifelse()
来更改我想要的值,但是日期列 date
和因子列 team
被强制转换为数字(如下面的 reprex 所示),这是不可取的。我知道 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 创建使用 case_when
中的默认 (TRUE
) 选项,其中 returns 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
The mode of the result may depend on the value of test (see the examples), and the class attribute (see oldClass) of the result is taken from test and may be inappropriate for the values selected from yes and no.
Sometimes it is better to use a construction such as
(tmp <- yes; tmp[!test] <- no[!test]; tmp)
, possibly extended to handle missing values in test.
这是一个未解决的问题:
@akrun 给出了很好的解释以及如何解决您的具体问题!
但万一你想保留 ifelse
:
Fabian Werner 在 2015 年使用自定义 safe.ifelse
[为您的具体情况提供了唯一可行的解决方案 (带日期和因素) =27=]
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