如果在创建对象和评估对象之间环境发生变化,R 中的惰性评估会导致意外结果

Lazy evaluation in R leads to unexpected results if the environment changes between the time the object was created and the time it is evaluated

openxlsx 在其当前版本 4.2.4 中将数字精度从默认值 7 覆盖为 22。如果您尝试使用 writeData.data.frame 编写惰性评估 data.frame,这将导致意外行为。

library(openxlsx)
library(magrittr)
library(dplyr)

wb <- createWorkbook()
sheet <- addWorksheet(wb,"Sheet")
data.frame(a=55.7) %>% mutate(a=a%>%format(nsmall=1)) %>% writeData(wb,sheet,.)
saveWorkbook(wb,"test.xlsx")

您可能会看到,工作簿中包含的不是预期的字符串“55.7”,而是更多的小数。这是因为在全局环境中 format 使用 7 位数字,因此 format(55.7)=="55.7",但在 writeData 中它的计算结果为

格式(55.7)==“55.700000000000003”

这不是浮点数的问题,而是环境变化的问题。格式使用默认值“digits=getOption("digits")”。默认为 7,在 writeData 中为 22。仅此而已,没什么特别的。

我想知道,为什么 format(55.7) 没有在全局环境的上下文中得到评估。

我想这是 openxlsx:

的一个特性(或错误)
  • R 对象是相同的
  • 这不是显示或格式的问题。我查看了这些文件的 XML,数字 55.70000000000000284217 和 57.7 是由 R 函数写入的
  • writexl 替换 openxlsx 消除了这种差异

使用当前环境中的数字选项延迟计算表达式。 openxlsx::writeData 某处使用 17 位数字,而默认值为 7。

library(magrittr)

f <- function(x) {
  rlang::scoped_options(digits=17)
  x$a
}
data.frame(a=format(1234.1)) %>% f()

> 1234.0999999999999

df <- data.frame(a=format(1234.1))
df %>% f()

> 1234.1

编辑:来自 openxlsx 的开发人员确认了该行为。他们通过在更改 fix.

中的任何环境变量之前强制评估其输入 data.frame 来解决此问题

这里是 link to my next question,它显示了完全相同的意外行为,但没有使用数字。