【问题标题】:R issue with rounding millisecondsR 问题与舍入毫秒
【发布时间】:2015-10-16 08:59:57
【问题描述】:

鉴于以下 R 下舍入毫秒的问题。我该如何解决它以使时间正确?

> options(digits.secs=3)
> as.POSIXlt("13:29:56.061", format='%H:%M:%OS', tz='UTC')
[1] "2012-06-07 13:29:56.060 UTC"
> as.POSIXlt("13:29:56.062", format='%H:%M:%OS', tz='UTC')
[1] "2012-06-07 13:29:56.061 UTC"
> as.POSIXlt("13:29:56.063", format='%H:%M:%OS', tz='UTC')
[1] "2012-06-07 13:29:56.063 UTC"

我注意到此 URL 提供了背景信息,但没有解决我的问题: Milliseconds puzzle when calling strptime in R.

此 URL 也涉及该问题但未解决该问题:R xts: .001 millisecond in index

在这些情况下,我确实看到了以下内容:

> x <- as.POSIXlt("13:29:56.061", format='%H:%M:%OS', tz='UTC')
> print(as.numeric(x), digits=20)
[1] 1339075796.0610001087

该 URL 似乎也表明这只是一个显示问题,但我注意到使用诸如 "%OS3" 之类的语句而没有选项行似乎无法获取正确的位数。

我使用的版本是 Windows 下的 32 位 2.15.0 但在 R 的其他情况下似乎存在。

请注意,我的原始数据是 CSV 文件中的这些日期时间字符串,我必须找到一种方法将它们从字符串转换为正确的毫秒时间。

【问题讨论】:

  • 这里使用 format() 是不必要的和分散注意力的。 . .
  • 是的,但我们确实需要format = '%H:%M:%OS'
  • print(56.061, digits=20) #[1] 56.060999999999999943;您可以将 0.0001 秒添加到所有时间并获得更正的截断。浮点数的精度在 32 位和 64 位机器上应该是一样的。

标签: r time rounding milliseconds rounding-error


【解决方案1】:

我没看到:

> options(digits.secs = 4)
> as.POSIXlt("13:29:56.061", format = '%H:%M:%OS', tz='UTC')
[1] "2012-06-07 13:29:56.061 UTC"
> as.POSIXlt("13:29:56.062", format = '%H:%M:%OS', tz='UTC')
[1] "2012-06-07 13:29:56.062 UTC"
> as.POSIXlt("13:29:56.063", format = '%H:%M:%OS', tz='UTC')
[1] "2012-06-07 13:29:56.063 UTC"
> options(digits.secs = 3)
> as.POSIXlt("13:29:56.061", format = '%H:%M:%OS', tz='UTC')
[1] "2012-06-07 13:29:56.061 UTC"
> as.POSIXlt("13:29:56.062", format = '%H:%M:%OS', tz='UTC')
[1] "2012-06-07 13:29:56.062 UTC"
> as.POSIXlt("13:29:56.063", format = '%H:%M:%OS', tz='UTC')
[1] "2012-06-07 13:29:56.063 UTC"

> sessionInfo()
R version 2.15.0 Patched (2012-04-14 r59019)
Platform: x86_64-unknown-linux-gnu (64-bit)

locale:
 [1] LC_CTYPE=en_GB.utf8       LC_NUMERIC=C             
 [3] LC_TIME=en_GB.utf8        LC_COLLATE=en_GB.utf8    
 [5] LC_MONETARY=en_GB.utf8    LC_MESSAGES=en_GB.utf8   
 [7] LC_PAPER=C                LC_NAME=C                
 [9] LC_ADDRESS=C              LC_TELEPHONE=C           
[11] LC_MEASUREMENT=en_GB.utf8 LC_IDENTIFICATION=C      

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

使用"%OSn" 格式字符串,可以强制截断。如果小数秒不能用浮点数精确表示,那么截断很可能会走错路。如果您发现事情出错了,您也可以明确地四舍五入到您想要的单位或添加您希望操作的分数的一半(在0.0005所示的情况下):

> t1 <- as.POSIXlt("13:29:56.061", format = '%H:%M:%OS', tz='UTC')
> t1
[1] "2012-06-07 13:29:56.061 UTC"
> t1 + 0.0005
[1] "2012-06-07 13:29:56.061 UTC"

(但我说过,我没有看到这里的问题。)

后一点是Simon Urbanek on the R-Devel mailing list on 30-May-2012提出的。

【讨论】:

  • 试用 32 位版本。
  • @AndrewStern 我不能我没有 32 位系统来试用它。我已经更新了我的答案。尝试将一小部分(在您的情况下为 0.0005)添加到您的时间之后您将它们作为"POSIXlt" 对象,看看这是否会改善情况。关注 R-Devel 线程了解更多详情。
  • 我可以重现 - 我在 Win7 64 位系统上安装了 32 位和 64 位 R。看来问题是 32bit R 特有的。
  • @Fhnuzoag 好的,这是一个浮点问题。 Andrew 应该尝试将一小部分添加到被截断的时间中。
  • 在打印出来之前向我的向量添加 0.0005 似乎已经成功了。
【解决方案2】:

这和Milliseconds puzzle when calling strptime in R是一样的问题。

你的例子:

> x <- as.POSIXlt("13:29:56.061", format='%H:%M:%OS', tz='UTC')
> print(as.numeric(x), digits=20)
[1] 1339075796.0610001087

不能代表问题。 as.numeric(x) 在转换为数字之前将您的 POSIXlt 对象转换为 POSIXct,因此您会得到不同的浮点精度舍入误差。

print.POSIXlt(调用format.POSIXlt)不是这样工作的。 format.POSIXlt 分别格式化 POSIXlt 列表结构的每个元素,因此您需要查看:

print(x$sec, digits=20)
[1] 56.060999999999999943

这个数字在小数点后第三位被截断,所以你看到56.060。你可以直接调用format看到这个:

> format(x, "%H:%M:%OS6")
[1] "13:29:56.060999"

【讨论】:

    【解决方案3】:

    在测试中,我注意到这个问题在 32 位 R 3.01 中仍然存在,这是由于 POSIXlt 日期时间的打印、格式和 as.character 运算符的 32 位实现所特有的浮点数据截断。

    基础数据没有存储在导致截断的另一种类型(32 位)而不是另一种(64 位)中,而是“打印”、“格式”和“as.character” POSIXlt 类型的函数,专门用于将 POSIXlt 数据显示为可显示的字符串。

    虽然记录的行为是这些函数截断(忽略)额外的数字(如@Gavin Simpson 所述),但对于 32 位和 64 位版本,情况并非如此。展示;我们将生成 1000 个不同的时间并执行一些比较操作:

    > options(digits.sec=3)
    > x = as.POSIXlt("13:29:56.061", format='%H:%M:%OS', tz='UTC')
    
    > for (i in 0:999) {
    >     x[i+1] = as.POSIXlt(paste0("13:29:56.",sprintf("%03d",i)),format='%H:%M:%OS',tz='UTC')
    > }
    
    > sum(x[2:1000]>x[1:999])
    [1] 999
    

    在 32 位和 64 位下,比较运算符是一致的,但是在 32 位下我看到了:

    > x[1:6]
    [1] "2015-10-16 13:29:56.000 UTC" "2015-10-16 13:29:56.000 UTC"
    [3] "2015-10-16 13:29:56.002 UTC" "2015-10-16 13:29:56.003 UTC"
    [5] "2015-10-16 13:29:56.003 UTC" "2015-10-16 13:29:56.005 UTC"
    

    所以这显然是一个显示问题。查看 POSIXlt 数据类型中的实际数字,尤其是我们可以看到发生了什么的秒数:

    > y = (x[1:6]$sec) 
    > y
    [1] 56.000 56.001 56.002 56.003 56.004 56.005
    > trunc(y*1000)/1000
    [1] 56.000 56.001 56.002 56.003 56.004 56.005
    > trunc((y-floor(y))*1000)/1000
    [1] 0.000 0.000 0.002 0.003 0.003 0.005
    

    我建议这是一个应该在底层基础库中修复的错误,但作为临时修复,您可以覆盖“print”、“as.character”和“format”函数以将输出更改为您想要的输出,例如

    format.POSIXlt = function(posix) {
        return(paste0(posix$year+1900,"-",sprintf("%02d",posix$mon+1),"-",sprintf("%02d",posix$mday)," ",
            sprintf("%02d",posix$hour),":",sprintf("%02d",posix$min),":",sprintf("%002.003f",posix$sec)))
        }
    
    print.POSIXlt = function(posix) {
        print(paste0(posix$year+1900,"-",sprintf("%02d",posix$mon+1),"-",sprintf("%02d",posix$mday)," ",
            sprintf("%02d",posix$hour),":",sprintf("%02d",posix$min),":",sprintf("%002.003f",posix$sec)))
        }
    
    as.character.POSIXlt = function(posix) {
        return(paste0(posix$year+1900,"-",sprintf("%02d",posix$mon+1),"-",sprintf("%02d",posix$mday)," ",
            sprintf("%02d",posix$hour),":",sprintf("%02d",posix$min),":",sprintf("%002.003f",posix$sec)))
        }
    

    【讨论】:

      【解决方案4】:

      毫秒数:

       unclass(as.POSIXlt("13:29:56.061", '%H:%M:%OS', tz='UTC'))
       $sec
       [1] 56.061
       ...
      

      (这里不需要调用格式,它是参数的名称,而不是来自其他函数的必需输入)。

      否则,我无法重现(在 Windows 64 位 R 2.15.0 上):

      options(digits.secs = 3)
      as.POSIXlt("13:29:56.061", '%H:%M:%OS', tz='UTC')
      [1] "2012-06-07 13:29:56.061 UTC"
      
      sessionInfo()
      R version 2.15.0 Patched (2012-05-05 r59321)
      Platform: x86_64-pc-mingw32/x64 (64-bit)
      ...
      

      【讨论】:

      • 当我使用以下命令取消分类时,它似乎是正确的: unclass(as.POSIXlt("13:29:56.061", "%H:%M:%OS", tz=' UTC')) 但使用 : as.POSIXlt("13:29:56.061", "%H:%M:%OS", tz='UTC') 显示时屏幕仍然显示不正确的毫秒数。请注意,我使用的是 32 位版本,而 64 位版本可能更准确,因为寄存器更大。
      猜你喜欢
      • 2018-07-19
      • 1970-01-01
      • 1970-01-01
      • 2011-06-08
      • 1970-01-01
      • 2011-02-21
      • 1970-01-01
      • 1970-01-01
      • 2018-09-17
      相关资源
      最近更新 更多