ifelse() 从时间戳向量中剥离 POSIXct 属性?
ifelse() stripping POSIXct attribute from vector of timestamps?
这很奇怪:R 的 ifelse()
似乎做了一些(不需要的)转换:
假设我有一个时间戳向量(可能是 NA),NA 值应该与现有日期区别对待,例如,只是忽略:
formatString = "%Y-%m-%d %H:%M:%OS"
timestamp = c(as.POSIXct(strptime("2000-01-01 12:00:00.000000", formatString)) + (1:3)*30, NA)
现在
timestamp
#[1] "2000-01-01 12:00:30 CET" "2000-01-01 12:01:00 CET" "2000-01-01 12:01:30 CET"
#[6] NA
如所愿,但翻译 30 秒会导致
ifelse(is.na(timestamp), NA, timestamp+30)
#[1] 946724460 946724490 946724520 NA
请注意,timestamp+30
仍然按预期工作,但假设我想用固定日期替换 NA 日期并将所有其他日期翻译 30 秒:
fixedDate = as.POSIXct(strptime("2000-01-01 12:00:00.000000", formatString))
ifelse(is.na(timestamp), fixedDate, timestamp+30)
#[1] 946724460 946724490 946724520 946724400
问题:这个解决方案有什么问题,为什么它没有按预期工作?
编辑:所需的输出是一个由 30 秒转换的时间戳向量(不是整数),NA 被替换为任何...
如果你看一下ifelse
的写法,它有一段代码是这样的:
ans <- test
ok <- !(nas <- is.na(test))
if (any(test[ok]))
ans[test & ok] <- rep(yes, length.out = length(ans))[test & ok]
请注意,答案以逻辑向量开始,与测试相同。然后将具有 test == TRUE
的元素分配给 yes
.
的值
这里的问题是将逻辑向量的一个或多个元素分配为日期 class POSIX.ct 时会发生什么。你可以看到如果你这样做会发生什么:
x <- c(TRUE, FALSE)
class(x)
# logical
x[1] <- Sys.time()
class(x)
# numeric
您可以通过以下方式解决此问题:
timestamp <- timestamp + 30
timestamp[is.na(timestamp)] <- fixedDate
你也可以这样做:
fixedDate = as.POSIXct(strptime("2000-01-01 12:00:00.000000", formatString))
unlist(ifelse(is.na(timestamp), as.list(fixedDate), as.list(timestamp+30)))
这利用了替换运算符 [<-
处理右侧列表的方式。
您也可以像这样重新添加 class 属性:
x <- ifelse(is.na(timestamp), fixedDate, timestamp+30)
class(x) <- c("POSIXct", "POSIXt")
或者如果你迫切希望像这样在一行中完成:
`class<-`(ifelse(is.na(timestamp), fixedDate, timestamp+30), c("POSIXct", "POSIXt"))
或者复制fixedDate
的属性:
x <- ifelse(is.na(timestamp), fixedDate, timestamp+30)
attributes(x) <- attributes(fixedDate)
最后一个版本还具有复制 tzone
属性的优点。
从 dplyr 0.5.0 开始,您还可以使用 dplyr::if_else
,它在输出中保留 class,并对 true 和 false 参数强制执行相同的 class。
正如 Henrik 所说,ifelse() 去除了属性,这与简单的 for 循环不同。
无忧地填充 NA 的解决方法是更简单、更清晰的函数 zoo::na.fill
那么你会做:na.fill(timestamp, fixedDate)
另请参阅 na.locf, na.approx, na.spline ...
,zoo 中其他出色的便捷功能。
这很奇怪:R 的 ifelse()
似乎做了一些(不需要的)转换:
假设我有一个时间戳向量(可能是 NA),NA 值应该与现有日期区别对待,例如,只是忽略:
formatString = "%Y-%m-%d %H:%M:%OS"
timestamp = c(as.POSIXct(strptime("2000-01-01 12:00:00.000000", formatString)) + (1:3)*30, NA)
现在
timestamp
#[1] "2000-01-01 12:00:30 CET" "2000-01-01 12:01:00 CET" "2000-01-01 12:01:30 CET"
#[6] NA
如所愿,但翻译 30 秒会导致
ifelse(is.na(timestamp), NA, timestamp+30)
#[1] 946724460 946724490 946724520 NA
请注意,timestamp+30
仍然按预期工作,但假设我想用固定日期替换 NA 日期并将所有其他日期翻译 30 秒:
fixedDate = as.POSIXct(strptime("2000-01-01 12:00:00.000000", formatString))
ifelse(is.na(timestamp), fixedDate, timestamp+30)
#[1] 946724460 946724490 946724520 946724400
问题:这个解决方案有什么问题,为什么它没有按预期工作?
编辑:所需的输出是一个由 30 秒转换的时间戳向量(不是整数),NA 被替换为任何...
如果你看一下ifelse
的写法,它有一段代码是这样的:
ans <- test
ok <- !(nas <- is.na(test))
if (any(test[ok]))
ans[test & ok] <- rep(yes, length.out = length(ans))[test & ok]
请注意,答案以逻辑向量开始,与测试相同。然后将具有 test == TRUE
的元素分配给 yes
.
这里的问题是将逻辑向量的一个或多个元素分配为日期 class POSIX.ct 时会发生什么。你可以看到如果你这样做会发生什么:
x <- c(TRUE, FALSE)
class(x)
# logical
x[1] <- Sys.time()
class(x)
# numeric
您可以通过以下方式解决此问题:
timestamp <- timestamp + 30
timestamp[is.na(timestamp)] <- fixedDate
你也可以这样做:
fixedDate = as.POSIXct(strptime("2000-01-01 12:00:00.000000", formatString))
unlist(ifelse(is.na(timestamp), as.list(fixedDate), as.list(timestamp+30)))
这利用了替换运算符 [<-
处理右侧列表的方式。
您也可以像这样重新添加 class 属性:
x <- ifelse(is.na(timestamp), fixedDate, timestamp+30)
class(x) <- c("POSIXct", "POSIXt")
或者如果你迫切希望像这样在一行中完成:
`class<-`(ifelse(is.na(timestamp), fixedDate, timestamp+30), c("POSIXct", "POSIXt"))
或者复制fixedDate
的属性:
x <- ifelse(is.na(timestamp), fixedDate, timestamp+30)
attributes(x) <- attributes(fixedDate)
最后一个版本还具有复制 tzone
属性的优点。
从 dplyr 0.5.0 开始,您还可以使用 dplyr::if_else
,它在输出中保留 class,并对 true 和 false 参数强制执行相同的 class。
正如 Henrik 所说,ifelse() 去除了属性,这与简单的 for 循环不同。
无忧地填充 NA 的解决方法是更简单、更清晰的函数 zoo::na.fill
那么你会做:na.fill(timestamp, fixedDate)
另请参阅 na.locf, na.approx, na.spline ...
,zoo 中其他出色的便捷功能。