为什么中位数会绊倒data.table(整数与双数)?

Far*_*rel 30 double integer r median data.table

我有一个名为enc.per.day的data.table,用于每天的遭遇.它有2403行,其中指定了服务日期和当天看到的患者数量.我希望看到在任何类型的工作日看到的患者中位数.

enc.per.day[,list(patient.encounters=median(n)),by=list(weekdays(DOS))]
Run Code Online (Sandbox Code Playgroud)

该行给出了错误

误差在[.data.table(enc.per.day,列表(patient.encounters =中间值(N)),:j的列不计算一致类型的每个组:导致对其组4具有第1分型"整数",但期待输入'double'

以下一切都很好

tapply(enc.per.day$n,weekdays(enc.per.day$DOS),median)
enc.per.day[,list(patient.encounters=round(median(n))),by=list(weekdays(DOS))]
enc.per.day[,list(patient.encounters=median(n)+0),by=list(weekdays(DOS))]
Run Code Online (Sandbox Code Playgroud)

到底是怎么回事?我花了很长时间才弄清楚为什么我的代码不起作用.

顺便说一句,底层向量enc.per.day $ n是一个整数

storage.mode(enc.per.day$n)
Run Code Online (Sandbox Code Playgroud)

返回"整数".此外,data.table中没有任何NA.

Jos*_*ien 43

TL; DR包装medianas.double()

median()' trip up'atata.table因为---即使只传递整数向量--- median()有时会返回一个整数值,有时会返回一个double.

## median of 1:3 is 2, of type "integer" 
typeof(median(1:3))
# [1] "integer"

## median of 1:2 is 1.5, of type "double"
typeof(median(1:2))
# [1] "double"
Run Code Online (Sandbox Code Playgroud)

使用最小示例重现错误消息:

library(data.table)
dt <- data.table(patients = c(1:3, 1:2), 
                 weekdays = c("Mon", "Mon", "Mon", "Tue", "Tue"))

dt[,median(patients), by=weekdays]
# Error in `[.data.table`(dt, , median(patients), by = weekdays) : 
#   columns of j don't evaluate to consistent types for each group: 
#   result for group 2 has column 1 type 'double' but expecting type 'integer'
Run Code Online (Sandbox Code Playgroud)

data.table抱怨因为,在检查了要处理的第一个组的值之后,它得出结论,好的,这些结果将是"整数"类型.但是,然后立即(或在第4组的情况下),它传递一个"double"类型的值,这将不适合其"整数"结果向量.


data.table可以累积结果直到分组计算结束,然后在必要时执行类型转换,但这需要一堆额外的性能降低开销; 相反,它只是报告发生了什么,并让你解决问题.在第一个组运行之后,它知道结果的类型,它会分配该类型的结果向量,只要组的数量,然后填充它.如果它后来发现某些组返回的项目超过1项,则会根据需要增长(即重新分配)该结果向量.但在大多数情况下,data.table对结果的最终大小的第一次猜测是第一次(例如,每组1行结果),因此很快.

在这种情况下,使用as.double(median(X))而不是median(X)提供合适的修复.

(顺便说一下,你的版本使用起来round()因为它总是返回"double"类型的值,你可以通过输入看到typeof(round(median(1:2))); typeof(round(median(1:3))).)