if_else 与 haven_labelled 列因错误 class 而失败

if_else with haven_labelled column fails because of wrong class

我有以下数据:

dat <- structure(list(value = structure(c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
                                        label = "value: This is my label",
                                        labels = c(`No` = 0, `Yes` = 1),
                                        class = "haven_labelled"),
                      group = structure(c(1, 2, 1, 1, 2, 3, 3, 1, 3, 1, 3, 3, 1, 2, 3, 2, 1, 3, 3, 1),
                                        label = "my group",
                                        labels = c(first = 1, second = 2, third = 3),
                                        class = "haven_labelled")),
                 row.names = c(NA, -20L),
                 class = c("tbl_df", "tbl", "data.frame"),
                 label = "test.sav")

如您所见,数据使用了来自 tidyverse 的 haven 包的特殊 class,即所谓的 labelled 列。

现在我想重新编码我的初始 value 变量,这样:

if group equals 1, value should stay the same, otherwise it should be missing

我正在尝试以下操作,但出现错误:

dat_new <- dat %>%
  mutate(value = if_else(group != 1, NA, value))
# Error: `false` must be a logical vector, not a `haven_labelled` object

我了解到 dplyr 的 if_else 要求 if_else 命令中的真假检查相同 class 并且因为没有 NA 等价物对于 class 标记(例如类似于 NA_real_ 的双打),代码可能会失败,对吧?

那么,如何重新编码我的初始变量并保留标签

我知道我可以更改上面的代码并将 if_else 替换为 R 的基本版本 ifelse。但是,这会删除所有标签并将值列强制转换为数字。

您可以使用以下难看的代码在 haven_labelled class 中创建一个 NA 值:

haven::labelled(NA_real_, labels = attr(dat$value, "labels"))

我建议为此编写一个函数,例如

labelled_NA <- function(value) 
  haven::labelled(NA_real_, labels = attr(value, "labels"))

然后你的 mutate 代码就没那么丑了:

dat_new <- dat %>%
  mutate(value = if_else(group != labelled_NA(value), value)) 

然后你得到

> dat_new[1:5,]
# A tibble: 5 x 2
      value      group
  <dbl+lbl>  <dbl+lbl>
1   NA      1 [first] 
2   NA      2 [second]
3    0 [No] 1 [first] 
4    0 [No] 1 [first] 
5   NA      2 [second]

对于 group == 1 的情况,您可以尝试 dplyr::case_when。如果没有匹配的案例,则返回 NA

dat %>% mutate(value = case_when(group == 1 ~ value))