Почему функция mutate () dplyr меняет формат времени?

Я использую readr для чтения данных, которые состоят из столбца даты в формате времени. Я могу правильно прочитать это, используя col_types параметр readr.

library(dplyr)
library(readr)

sample <- "time,id
2015-03-05 02:28:11,1674
2015-03-03 13:10:59,36749
2015-03-05 07:55:48,NA
2015-03-05 06:13:19,NA
"

mydf <- read_csv(sample, col_types="Ti")
mydf
                 time    id
1 2015-03-05 02:28:11  1674
2 2015-03-03 13:10:59 36749
3 2015-03-05 07:55:48    NA
4 2015-03-05 06:13:19    NA

Это приятно. Однако, если я хочу манипулировать этим столбцом с помощью dplyr, столбец времени теряет свой формат.

mydf %>% mutate(time = ifelse(is.na(id), NA, time))
        time    id
1 1425522491  1674
2 1425388259 36749
3         NA    NA
4         NA    NA

Почему это происходит?

Я знаю, что могу обойти эту проблему, преобразовав ее в символ раньше, но было бы удобнее без преобразования вперед и назад.

mydf %>% mutate(time = as.character(time)) %>% 
    mutate(time = ifelse(is.na(id), NA, time))

person janosdivenyi    schedule 01.09.2015    source источник


Ответы (2)


На самом деле причиной этой проблемы является ifelse(), а не dplyr::mutate(). Пример проблемы удаления атрибутов показан в help(ifelse) -

## ifelse() strips attributes
## This is important when working with Dates and factors
x <- seq(as.Date("2000-02-29"), as.Date("2004-10-04"), by = "1 month")
## has many "yyyy-mm-29", but a few "yyyy-03-01" in the non-leap years
y <- ifelse(as.POSIXlt(x)$mday == 29, x, NA)
head(y) # not what you expected ... ==> need restore the class attribute:
class(y) <- class(x)

Вот и все. Если вы хотите использовать ifelse(), это немного лишняя работа. Вот два возможных метода, которые приведут вас к желаемому результату без ifelse(). Первый действительно простой и использует is.na<-.

## mark 'time' as NA if 'id' is NA
is.na(mydf$time) <- is.na(mydf$id)

## resulting in
mydf
#                  time    id
# 1 2015-03-05 02:28:11  1674
# 2 2015-03-03 13:10:59 36749
# 3                <NA>    NA
# 4                <NA>    NA

Если вы не хотите выбирать этот маршрут и хотите продолжить использование метода dplyr, вы можете использовать replace() вместо ifelse().

mydf %>% mutate(time = replace(time, is.na(id), NA))
#                  time    id
# 1 2015-03-05 02:28:11  1674
# 2 2015-03-03 13:10:59 36749
# 3                <NA>    NA
# 4                <NA>    NA

Данные:

mydf <- structure(list(time = structure(c(1425551291, 1425417059, 1425570948, 
1425564799), class = c("POSIXct", "POSIXt"), tzone = ""), id = c(1674L, 
36749L, NA, NA)), .Names = c("time", "id"), class = "data.frame", row.names = c(NA, 
-4L))
person Rich Scriven    schedule 01.09.2015

Есть еще одна версия if_else от @hadley в dplyr. Он правильно управляет временными переменными. Также обратите внимание на эту проблему с github.

person Alexander    schedule 28.06.2018