drop_na( ) 无法在 POSIX-lt object 上工作

drop_na( ) cannot work on POSIX-lt object

根据标题,我做了一个简单的例子来测试drop_na {tidyr}

library(tidyr)
library(dplyr)

# (1.) produce a dataset with two POSIX type "ct" and "lt"

data <- data.frame(n = 1:5)
data$ct <- as.POSIXct(Sys.time() + rnorm(5) * 1000)
data$lt <- as.POSIXlt(Sys.time() + rnorm(5) * 1000)
str(data)

# $ n : int  1 2 3 4 5
# $ ct: POSIXct, format: "2018-10-07 03:02:28" ...
# $ lt: POSIXlt, format: "2018-10-07 02:37:26" ...


# (2.) assign the third values of "ct" and "lt" to NA

data[3, c("ct", "lt")] <- NA


# (3.) use different function to remove rows with NA

data %>% is.na()               # identify NAs in both "ct" and "lt"
data %>% drop_na('ct')         # drop NA from "ct"
data %>% drop_na('lt')         # NOT drop NA from "lt"
data[c(1, 2)] %>% na.omit()    # drop NA from "ct"
data[c(1, 3)] %>% na.omit()    # NOT drop NA from "lt"

从上面的结论来看,如果POSIX-lt个变量中有NA,那么只有is.na()可以用来删除有NA的行。

我大概知道POSIX"ct"和"lt"的区别。

那么有人可以解释为什么 POSIXlt 的缺失值不能被 drop_na()na.omit() 识别吗?

简答:除非你真的需要 POSIXlt

,否则请使用 POSIXct

更长的答案:

POSIXlt 是一种困难且反复无常的数据结构。参见:

> str(c(as.POSIXlt(Sys.time()), NA))
 POSIXlt[1:2], format: "2018-10-07 00:43:06" NA
> unclass(c(as.POSIXlt(Sys.time()), NA))
$sec
[1] 15.78872       NA

$min
[1] 43 NA

$hour
[1]  0 NA
# skipped a few rows

$isdst
[1]  1 -1

$zone
[1] "EEST" ""   
# skipped a few rows 

简而言之,POSIXlt 是一个向量列表,每个向量代表一个 date/time 单位:秒、分钟、小时、天等,还有时区等。没有方法na.omit 用于 POSIXlt,因此使用 na.omit.default,它不知道 POSIXlt class 的具体情况并将其视为普通列表。

> na.omit(list(NA,NA,NA))
[[1]]
[1] NA

[[2]]
[1] NA

[[3]]
[1] NA

如果你需要POSIXltna.omit方法,你可以写一个。但如果不是真的,使用 POSIXct.

更容易

推论:na.omit 也不适用于列表(即它可以使用但什么都不做)。您可以 sapplylapply na.omit 到列表,但这也会产生奇怪的结果(NA 组件将被 logical(0) 替换)。看起来 na.omit 是为了与原子向量或因子以及数据帧一起使用。 (帮助页面说,它主要用于数据框)。这意味着 na.omit 不适用于列表,包括 POSIXlt

最后,为什么要使用 POSIXlt?这个想法(据我所知)是您可以轻松地操纵日期的组成部分 - 但即使那样也会产生意想不到的结果:

> foo <- as.POSIXlt(Sys.time())
> foo
[1] "2018-10-07 01:06:22 EEST"
> foo$year
[1] 118
> foo$mon
[1] 9
> foo$mon <- 10
> foo
[1] "2018-11-07 01:06:22 EEST"
> foo$year <- 2018
> foo
[1] "3918-11-07 01:06:22 EEST"

因此,如果您需要单独操作日期的组件,使用 lubridate 不会有太大的意外。

> library(lubridate)
> year(foo)
[1] 3918
> year(foo) <- 2018
> foo
[1] "2018-11-07 01:06:22 EET"
> month(foo)
[1] 11
> month(foo)<-10
> foo
[1] "2018-10-07 01:06:22 EEST"