r - as.POSIXct 行为不一致
问题描述
这听起来像是一个重复的问题,但我经历了许多与 POSIxct 相关的错误,但没有遇到过这个问题。如果您仍然找到一个,我将非常感谢您指出这个方向。as.POSIXct 在我的情况下表现得非常尴尬。请参见下面的示例:
options(digits.secs = 3)
test_time <- "2017-01-26 23:00:00.010"
test_time <- as.POSIXct(test_time, format = "%Y-%m-%d %H:%M:%OS")
这将返回:
"2017-01-26 23:00:00.00"
现在,我尝试以下选项,它返回 NA。我不知道为什么当我需要它转换为“2017-01-26 23:00:00.010”时会出现这样的行为。
test_time <- "2017-01-26 23:00:00.010"
test_time <- as.POSIXct(test_time, format = "%Y-%m-%d %H:%M:%OS3")
现在当我这样做时它工作正常:
as.POSIXlt(strptime(test_time,format = "%Y-%m-%d %H:%M:%OS"), format = "%Y-%m-%d %H:%M:%OS")
但出于我的目的,我需要将其作为 POSIXct 对象,因为我正在使用的某些库仅采用 POSIXct 对象。再次将 POSIXlt 转换为 POSIXct 会导致与以前相同的问题。我的系统设置有问题吗?该日期也不是那些引发错误的夏令时之一。为什么它适用于一种格式而不适用于其他格式?欢迎任何线索/建议!
在 Windows 10 64 位上运行
解决方案
这里的问题与 POSIXct 可以处理的最大精度有关。它由引擎盖下的double支持,表示自 1970 年 1 月 1 日 UTC 午夜以来的秒数。小数秒表示为该双精度的小数部分,即63.02
表示1970-01-01 00:01:03.02 UTC
。
options(digits = 22, digits.secs = 3)
.POSIXct(63.02, tz = "UTC")
#> [1] "1970-01-01 00:01:03.02 UTC"
63.02
#> [1] 63.02000000000000312639
现在,当使用双精度时,它们可以精确表示的精度是有限的。您可以通过上面的示例看到这一点;在控制台中输入63.02
并不会返回完全相同的数字,而是返回接近的数字,但最后会有一些额外的位。
所以现在让我们看看你的例子。如果我们尽可能从“低级别”开始,首先要做的as.POSIXct()
是 call strptime()
,它返回一个 POSIXlt 对象。这将日期时间的每个“字段”保持为一个单独的元素(即年与月、日、秒等分开)。我们可以看到它解析正确并且我们的 sec 字段成立0.01
。
# `digits.secs` to print 3 fractional digits (has no effect on parsing)
# `digits` to print 22 fractional digits for double values
options(digits.secs = 3, digits = 22)
x <- "2017-01-26 23:00:00.010"
# looks good
lt <- strptime(x, format = "%Y-%m-%d %H:%M:%OS", tz = "America/New_York")
lt
#> [1] "2017-01-26 23:00:00.01 EST"
# This is a POSIXlt, which is a list holding fields like year,month,day,...
class(lt)
#> [1] "POSIXlt" "POSIXt"
# sure enough...
lt$sec
#> [1] 0.01000000000000000020817
但现在将其转换为 POSIXct。此时,各个字段被折叠成单个双精度字段,这可能存在精度问题。
# now convert to POSIXct (i.e. a single double holding all the info)
# looks like we lost the fractional seconds?
ct <- as.POSIXct(lt)
ct
#> [1] "2017-01-26 23:00:00.00 EST"
# no, they are still there, but the precision in the `double` data type
# isn't enough to be able to represent this exactly as `1485489600.010`
unclass(ct)
#> [1] 1485489600.009999990463
#> attr(,"tzone")
#> [1] "America/New_York"
所以ct
双精度值的小数部分接近.010
,但不能准确表示它并返回一个略小于的值.010
,当打印 POSIXct 时(我认为)四舍五入,看起来你丢失了小数秒.
因为这些问题太麻烦了,我推荐使用clock包的低级API(注意是我写的这个包)。它支持小数秒到纳秒而不损失精度(通过使用与 POSIXct 不同的数据结构)。 https://clock.r-lib.org/
library(clock)
x <- "2017-01-26 23:00:00.010"
nt <- naive_time_parse(x, format = "%Y-%m-%d %H:%M:%S", precision = "millisecond")
nt
#> <time_point<naive><millisecond>[1]>
#> [1] "2017-01-26 23:00:00.010"
# If you need it in a time zone
as_zoned_time(nt, zone = "America/New_York")
#> <zoned_time<millisecond><America/New_York>[1]>
#> [1] "2017-01-26 23:00:00.010-05:00"
推荐阅读
- java - Vert.x - 带有 DataInputStreams 的 GraphQL 订阅
- xamarin - Xamarin iOS 13 在不触发原生弹出窗口的情况下检查蓝牙权限
- swift - 尝试将 2d 触摸手动转换为 spritekit 坐标时出现问题
- php - 从 Laravel 中的数组中设置变量
- php - PHP str_replace 中的多个参数
- java - 在wildfly 11中部署的Spring Boot 2出现WFLYSRV0003错误:无法索引类module-info.class
- qemu - QEMU AARCH64“virt”机器 SMP CPU 以“running”与“halted”状态开始
- javascript - 增加进度条上的时间
- prolog - 即使定义了基本谓词,prolog 查询也会卡住
- c# - 场景正在 Unity 中加载,但无法在 Android 中加载场景