data.table无法通过两列正确进行汇总

7

最近我遇到了一个让我头痛不已的 data.table 问题。看起来像是一个bug,但也许我错过了什么显而易见的东西。

我有以下数据框:

# First some data
data <- data.table(structure(list(
  month = structure(c(1356998400, 1356998400, 1356998400, 
                      1359676800, 1354320000, 1359676800, 1359676800, 1356998400, 1356998400, 
                      1354320000, 1354320000, 1354320000, 1359676800, 1359676800, 1359676800, 
                      1356998400, 1359676800, 1359676800, 1356998400, 1359676800, 1359676800, 
                      1359676800, 1359676800, 1354320000, 1354320000), class = c("POSIXct", 
                                                                                 "POSIXt"), tzone = "UTC"), 
  portal = c(TRUE, TRUE, FALSE, TRUE, 
             TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, 
             TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE
  ), 
  satisfaction = c(10L, 10L, 10L, 9L, 10L, 10L, 9L, 10L, 10L, 
                   9L, 2L, 8L, 10L, 9L, 10L, 10L, 9L, 10L, 10L, 10L, 9L, 10L, 9L, 
                   10L, 10L)), 
                  .Names = c("month", "portal", "satisfaction"), 
                  row.names = c(NA, -25L), class = "data.frame"))

我想通过portalmonth来进行总结。使用经典的tapply进行总结,结果如预期-我得到了一个3x2的矩阵,其中包含了2012年12月和2013年1-2月的结果:

> tapply(data$satisfaction, list(data$month, data$portal), mean)
           FALSE      TRUE
2012-12-01   8.5  8.000000
2013-01-01  10.0 10.000000
2013-02-01   9.0  9.545455

data.tableby 参数不支持总结功能:

> data[, mean(satisfaction), by = 'month,portal']
   month      portal        V1
1: 2013-01-01  FALSE 10.000000
2: 2013-02-01   TRUE  9.000000
3: 2013-01-01   TRUE 10.000000
4: 2012-12-01  FALSE  8.500000
5: 2012-12-01   TRUE  7.333333
6: 2013-02-01   TRUE  9.666667
7: 2013-02-01  FALSE  9.000000
8: 2012-12-01   TRUE 10.000000

正如您所看到的,它返回了一个数据表,其中有8个值,而不是预期的6个值;例如,portal == TRUEmonth == 2012-02-01的值是重复的。

有趣的是,如果我仅限于2013年的数据,一切都按预期工作:

> data[month >= ymd(20130101), mean(satisfaction), by = 'month,portal']
        month portal        V1
1: 2013-01-01   TRUE 10.000000
2: 2013-01-01  FALSE 10.000000
3: 2013-02-01   TRUE  9.545455
4: 2013-02-01  FALSE  9.000000

我感到非常困惑 :). 请有人帮助我吗?

使用 by=list(month, portal) - Andrie
首先,data[, mean(satisfaction), by = list(month, portal)] 产生了相同(不正确)的结果。其次,根据 data.table 的帮助文档,两种语法都被支持:“by - 单个未引用的列名、列名表达式的列表、包含逗号分隔列名的单个字符字符串或列名的字符向量。” - Victor K.
3
将您的 POSIX 列转换为日期格式(as.Date)似乎可以解决问题。我认为 data.table 在某些日期是否“相等”方面进行了一些纠结(正确?错误?)。 - joran
谢谢@joran。这真的很奇怪——例如,当我执行table(c(1356998400, 1356998400, 1356998400, 1359676800, 1354320000, 1359676800, 1359676800, 1356998400, 1356998400, 1354320000, 1354320000, 1354320000, 1359676800, 1359676800, 1359676800, 1356998400, 1359676800, 1359676800, 1356998400, 1359676800, 1359676800, 1359676800, 1359676800, 1354320000, 1354320000))时,我得到了完全不同的三个值。 - Victor K.
也不清楚为什么当给出同一数据的较小子集时,它会停止分割细微差别。正是这种第二种行为让我花费了很长时间最终调试出来。 - Victor K.
2个回答

8

这是一个已知问题,已在data.table 1.8.7中解决(截至撰写本文尚未在CRAN上发布)。

来自data.table的 NEWS

BUG FIXES

    <...>

o   setkey could sort 'double' columns (such as POSIXct) incorrectly when not the
    last column of the key, #2484. In data.table's C code :
        x[a] > x[b]-tol
    should have been :
        x[a]-x[b] > -tol  [or  x[b]-x[a] < tol ]
    The difference may have been machine/compiler dependent. Many thanks to statquant
    for the short reproducible example. Test added.

使用 install.packages("data.table", repos="http://R-Forge.R-project.org") 升级到 1.8.7 后,一切都符合预期。


5
问题似乎出在排序上。当我加载data并执行setkey时:
setkey(data, "month", "portal")

# > data
#          month portal satisfaction
#  1: 2012-12-01   TRUE           10
#  2: 2012-12-01  FALSE            9
#  3: 2012-12-01  FALSE            8
#  4: 2012-12-01   TRUE            2
#  5: 2012-12-01   TRUE           10
#  6: 2012-12-01   TRUE           10
#  7: 2013-01-01   TRUE           10
#  8: 2013-01-01   TRUE           10
#  9: 2013-01-01   TRUE           10
# 10: 2013-01-01   TRUE           10
# 11: 2013-01-01   TRUE           10
# 12: 2013-01-01   TRUE           10
# 13: 2013-01-01  FALSE           10
# 14: 2013-02-01   TRUE            9
# 15: 2013-02-01   TRUE            9
# 16: 2013-02-01  FALSE            9
# 17: 2013-02-01   TRUE           10
# 18: 2013-02-01   TRUE           10
# 19: 2013-02-01   TRUE           10
# 20: 2013-02-01   TRUE           10
# 21: 2013-02-01   TRUE           10
# 22: 2013-02-01   TRUE            9
# 23: 2013-02-01   TRUE           10
# 24: 2013-02-01   TRUE            9
# 25: 2013-02-01   TRUE            9
#          month portal satisfaction

您可以看到portal列没有正确排序。当我再次使用 setkey 命令时,问题得以解决。

setkey(data, "month", "portal")

# I get this warning message:
Warning message:
In setkeyv(x, cols, verbose = verbose) :
  Already keyed by this key but had invalid row order, key rebuilt. 
  If you didn't go under the hood please let datatable-help know so 
  the root cause can be fixed.

现在,data列似乎已经根据键列正确排序:
# > data
#          month portal satisfaction
#  1: 2012-12-01  FALSE            9
#  2: 2012-12-01  FALSE            8
#  3: 2012-12-01   TRUE           10
#  4: 2012-12-01   TRUE            2
#  5: 2012-12-01   TRUE           10
#  6: 2012-12-01   TRUE           10
#  7: 2013-01-01  FALSE           10
#  8: 2013-01-01   TRUE           10
#  9: 2013-01-01   TRUE           10
# 10: 2013-01-01   TRUE           10
# 11: 2013-01-01   TRUE           10
# 12: 2013-01-01   TRUE           10
# 13: 2013-01-01   TRUE           10
# 14: 2013-02-01  FALSE            9
# 15: 2013-02-01   TRUE            9
# 16: 2013-02-01   TRUE            9
# 17: 2013-02-01   TRUE           10
# 18: 2013-02-01   TRUE           10
# 19: 2013-02-01   TRUE           10
# 20: 2013-02-01   TRUE           10
# 21: 2013-02-01   TRUE           10
# 22: 2013-02-01   TRUE            9
# 23: 2013-02-01   TRUE           10
# 24: 2013-02-01   TRUE            9
# 25: 2013-02-01   TRUE            9
#          month portal satisfaction

所以,似乎这是一个关于对POSIXct + logical类型进行排序的问题?

data[, mean(satisfaction), by=list(month, portal)]

#         month portal        V1
# 1: 2012-12-01  FALSE  8.500000
# 2: 2012-12-01   TRUE  8.000000
# 3: 2013-01-01  FALSE 10.000000
# 4: 2013-01-01   TRUE 10.000000
# 5: 2013-02-01  FALSE  9.000000
# 6: 2013-02-01   TRUE  9.545455

我认为这里存在一个bug。

2
是的,我正要留言说如果@MatthewDowle在一两天内没有发表评论,那么OP应该将这个问题发布到data.table邮件列表上。 - joran
1
啊哈!谢谢Arun,这似乎确实是这种情况。我将向邮件列表报告它作为一个错误。在接受你的答案之前,我们会等待Matthew回复。 - Victor K.
可能与这个 bug 有关,巧合或不巧合的是,它今天刚刚被开启:https://r-forge.r-project.org/tracker/index.php?func=detail&aid=2552&group_id=240&atid=975 - Victor K.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接