【问题标题】:why aggregate on Date returns numeric - R为什么聚合日期返回数字 - R
【发布时间】:2020-03-03 16:27:16
【问题描述】:

我有几百万行的 data.table 并尝试在日期字段上进行聚合。返回的结果是数字(而不是日期),我不知道为什么。我一直在尝试重现该示例,但无济于事。 这是我的代码示例:

DT <- data.table(ID = c(1,1,2,1,2,3,2,3,1), col1 = c('Y', 'N', 'Y', 'Y', 'N', 'Y',' Y', 'N', 'N'), col2 = as.Date(c('2001-01-01', '2002-01-01', '2003-01-01', '2004-01-01','2005-01-01', NA, '2007-01-01', '2008-01-01', NA), '%Y-%m-%d'))

> DT
   ID col1       col2
1:  1    Y 2001-01-01
2:  1    N 2002-01-01
3:  2    Y 2003-01-01
4:  1    Y 2004-01-01
5:  2    N 2005-01-01
6:  3    Y       <NA>
7:  2    Y 2007-01-01
8:  3    N 2008-01-01
9:  1    N       <NA>

DT_agg = DT[, .(max_date = if(!any(col1 == "Y")) NA_real_ else max(col2[which(col1 == "Y")], na.rm = TRUE) ) , by = .(ID)]
Warning message:
In max.default(NA_real_, na.rm = TRUE) :
  no non-missing arguments to max; returning -Inf



 # this works on the sample data.table. 
 # and it's desired output. But it doesn't work on my 
 # real data.table. max_date has numeric values instead (e.g. 11124, 22354, etc.)
> DT_agg
   ID   max_date
1:  1 2004-01-01
2:  2 2003-01-01
3:  3       <NA>

# this is what i want for my results. but in my real table max_date becomes numeric and unusable
> class(DT_agg$max_date)
[1] "Date"

在上面的示例代码中DT_agg$max_date 是一个日期类。但由于某种原因,在我的表中它是数字的。我在另一个日期字段上运行相同的聚合,这很好。

【问题讨论】:

  • 您的意思是这适用于您提供的样本数据,但不适用于您的真实数据?
  • 是的。这是正确的。我将编辑我的问题以使其更清楚。这适用于提供的示例,但不适用于我的大型真实 data.table
  • 我认为 col2[which(col1 == "Y")] 在某些情况下是 length(0) 在删除导致该警告的 NA 之后。您可以使用此数据集进行复制:DT &lt;- data.table(ID = c(1,1,2,1,2,3,2,3,1), col1 = rep("Y", 9L), col2 = as.Date(rep(NA, 9L), '%Y-%m-%d'))
  • !any(col1 == "Y") 可以简化为all(col1 != "Y")
  • 关于您的问题。用as.Date(NA_real_, origin = "2000-01-01") 之类的东西替换NA_real_ 会起作用吗?

标签: r date data.table aggregate


【解决方案1】:

TL;DR

ifelse 是事情发生的地方。 Yes 和 No 子句必须是同一类。

我的解决方案:

首先,按两列聚合

> DT[,max(col2),by=list(ID,col1)]
   ID col1         V1
1:  1    Y 2004-01-01
2:  1    N       <NA>
3:  2    Y 2003-01-01
4:  2    N 2005-01-01
5:  3    Y       <NA>
6:  2    Y 2007-01-01
7:  3    N 2008-01-01

然后就拿 Y 了。

> DT[,max(col2),by=list(ID,col1)][col1=='Y']
   ID col1         V1
1:  1    Y 2004-01-01
2:  2    Y 2003-01-01
3:  3    Y       <NA>

现在删除不需要的第二列,(或分配它

> DT[,max(col2),by=list(ID,col1)][col1=='Y'][ , .(ID,V1)]
   ID         V1
1:  1 2004-01-01
2:  2 2003-01-01
3:  3       <NA>

另外,第二步和第三步可以合并:

 DT[,max(col2),by=list(ID,col1)][col1=='Y', .(ID,max_date=V1)]

更新:当我尝试使用ifelse 时,我发现你的转化发生在此处

 k=as.Date('2001-01-01')
> k
[1] "2001-01-01"
> as.integer(k)
[1] 11323
> max(k,NA)
[1] NA
> min(k,NA)
[1] NA
> ifelse(Y=='N',k,NA)
Error in ifelse(Y == "N", k, NA) : object 'Y' not found
> Y='N'
> ifelse(Y=='N',k,NA)
[1] 11323

更新 2

如果您尝试执行ifelse的快速数据表版本,您可以看到问题所在。转换发生在普通的ifelse 中,因为“是”和“否”子句不是同一个类,并且 R 必须花时间来转换它们,这也给你的性能带来了影响。请记住:ifelse 必须是可矢量化的。为此,YesNo 子句_必须属于同一类。

# the double colon is not really necessary.  I'm just
# doing it to emphasize the provenance of `fifelse`

> data.table::fifelse(Y=='N',k,23)
Error in data.table::fifelse(Y == "N", k, 23) : 
  'yes' has different class than 'no'. Please make sure that both arguments have the same class.

【讨论】:

  • 这可行,但我对这个解决方案的唯一问题是我在不同的列上有多个这些聚合。这意味着对于每个日期列,我必须进行单独的聚合,然后将它们组合在一起。就是有点丑。 :) 我想如果它不能以任何其他方式工作,这就是我会做的。我只是想知道为什么日期上的聚合操作不稳定。不知道为什么会这样真的是个混蛋!
  • @Ankhnesmerira :另外,只是想到了这一点-如果您正在执行的聚合数量是固定的(不依赖于您的数据),为什么不先对所有类别进行主聚合,以及然后努力削减它?
  • 这很有帮助!!太感谢了。谜团解开了。
猜你喜欢
  • 1970-01-01
  • 2023-03-17
  • 2019-09-03
  • 2020-04-16
  • 1970-01-01
  • 1970-01-01
  • 2018-02-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多