1

我发现一些我无法解释的奇怪行为as.POSIXlt,我希望其他人可以。在调查这个问题时,我发现有时一秒的小数部分会被错误地舍入

例如,下面的数字表示自纪元开始以来的特定秒,最后 6 位数字是秒的小数部分,因此第一个数字上的秒小数应该是 0.645990。

# Generate sequence of integers to represent date/times
times <- seq( 1366039619645990 , length.out = 11 )
options(scipen=20)
times
 [1] 1366039619645990 1366039619645991 1366039619645992 1366039619645993 1366039619645994 1366039619645995
 [7] 1366039619645996 1366039619645997 1366039619645998 1366039619645999 1366039619646000

# Convert to date/time with microseconds 
options(digits.secs = 6 )
as.POSIXlt( times/1e6, tz="EST", origin="1970-01-01") + 5e-7
 [1] "2013-04-15 10:26:59.645990 EST" "2013-04-15 10:26:59.645991 EST" "2013-04-15 10:26:59.645992 EST"
 [4] "2013-04-15 10:26:59.645993 EST" "2013-04-15 10:26:59.645994 EST" "2013-04-15 10:26:59.645995 EST"
 [7] "2013-04-15 10:26:59.645996 EST" "2013-04-15 10:26:59.645997 EST" "2013-04-15 10:26:59.645998 EST"
[10] "2013-04-15 10:26:59.645999 EST" "2013-04-15 10:26:59.646000 EST"

我发现我必须添加一个小的增量,等于最小时间变化的一半,才能正确表示一秒的小数部分,否则会出现舍入错误。如果我在上面的一系列数字上运行它就可以正常工作as.POSIXlt,但是如果我尝试转换一个数字,即应该以 .645999 结尾的数字,那么将截断为 .645 的数字,我不知道为什么!

# Now just convert the date/time that should end in .645999
as.POSIXlt( times[10]/1e6, tz="EST", origin="1970-01-01") + 5e-7
[1] "2013-04-15 10:26:59.645 EST"

将返回的向量中的第 10 个元素as.POSIXlt与上面等效的单个元素进行比较。怎么了?

会话信息:

R version 2.15.2 (2012-10-26)
Platform: x86_64-apple-darwin9.8.0/x86_64 (64-bit)

locale:
[1] en_GB.UTF-8/en_GB.UTF-8/en_GB.UTF-8/C/en_GB.UTF-8/en_GB.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] raster_2.0-41 sp_1.0-5     

loaded via a namespace (and not attached):
[1] grid_2.15.2     lattice_0.20-13 tools_2.15.2
4

2 回答 2

2

这似乎是一个舍入问题,其中小数秒的有效数字被丢弃。有问题的(?) 代码在类对象的格式方法中POSIXlt,即format.POSIXltprint.POSIXlt.

如果我们使用下面的两个值作为示例,format.POSIXlt请使用我在 sapply 中包装的以下行来测试四舍五入到连续更大位数的小数秒之间的差异的绝对值。

secs <- c( 59.645998 , 59.645999 )
sapply( seq_len(np) - 1L , function(x) abs(secs - round(secs, x)) )
         [,1]     [,2]     [,3]     [,4]     [,5]     [,6]
[1,] 0.354002 0.045998 0.004002 0.000002 0.000002 0.000002
[2,] 0.354001 0.045999 0.004001 0.000001 0.000001 0.000001

正如您所看到的,当秒数为 .xxx999 时,任何四舍五入到 3 位或更多位都会给出 0.000001,从而影响打印:

# the number of digits used for the fractional seconds is gotten here
np <- getOption("digits.secs")

# and the length of digits to be printed is controlled in this loop
for (i in seq_len(np) - 1L) if (all(abs(secs - round(secs, 
                i)) < 0.000001)) {
                np <- i
                break
            }

这是因为在上述方法中实际找到的 0.000001 是:

sprintf( "%.20f" , abs(secs[2] - round(secs,5)))
[1] "0.00000099999999991773"            

# In turn this is used to control the printing of the fractional seconds            
if (np == 0L) 
            "%Y-%m-%d %H:%M:%S"
        else paste0("%Y-%m-%d %H:%M:%OS", np) 

因此,由于四舍五入中使用的测试,小数秒被截断到小数点后 3 位。我认为如果将 for 循环中的测试值设置为 5e-7,这个问题就会消失。

当返回的结果是POSIXlt对象向量时,必须调用不同的打印方法。

于 2013-04-24T10:13:45.920 回答
1

我没有得到正确的答案(继续调查),但我认为这很有趣:

times <- seq( 1366039619645990 , length.out = 11 )
# Convert to date/time wz="EST", origin="1970-01-01") + 5e-7
options(digits.secs = 6 )

test <- as.POSIXlt( times/1e6, tz="EST", origin="1970-01-01") + 5e-7

test1[1] <- NULL
for(i in 1:11)
  test1[i] <- as.POSIXlt(times[i]/1e6, tz="EST", origin="1970-01-01") + 5e-7

> identical(test, test1)
[1] TRUE

顺便说一句,在单个语句中,我得到了与您相同的结果...

> test
 [1] "2013-04-15 10:26:59.645990 EST" "2013-04-15 10:26:59.645991 EST" "2013-04-15 10:26:59.645992 EST"
 [4] "2013-04-15 10:26:59.645993 EST" "2013-04-15 10:26:59.645994 EST" "2013-04-15 10:26:59.645995 EST"
 [7] "2013-04-15 10:26:59.645996 EST" "2013-04-15 10:26:59.645997 EST" "2013-04-15 10:26:59.645998 EST"
[10] "2013-04-15 10:26:59.645999 EST" "2013-04-15 10:26:59.646000 EST"
> test[10]
[1] "2013-04-15 10:26:59.645 EST"
> as.POSIXlt( times[10]/1e6, tz="EST", origin="1970-01-01") + 5e-7
[1] "2013-04-15 10:26:59.645 EST"

查看最后两个语句,似乎这个问题主要与显示单个值而不是向量有关。但即使在这种情况下,它也可能是截断,可能是通过floor,而不是四舍五入。

于 2013-04-24T09:33:47.120 回答