为什么dplyr的mutate()会改变时间格式?
Why does dplyr's mutate() change the time format?
我使用readr
读入包含时间格式的日期列的数据。我可以使用 readr
的 col_types
选项正确读取它。
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))
实际上是 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))
还有 if_else
的另一个版本 @hadley in dplyr
. It correctly manage time variables. Look at this github issue。
我使用readr
读入包含时间格式的日期列的数据。我可以使用 readr
的 col_types
选项正确读取它。
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))
实际上是 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))
还有 if_else
的另一个版本 @hadley in dplyr
. It correctly manage time variables. Look at this github issue。