【问题标题】:Aggregate data.table based on condition in another row根据另一行中的条件聚合 data.table
【发布时间】:2016-05-15 14:44:29
【问题描述】:

我想根据两个条件聚合一个data.table,其中一个附加到另一行。这是我的问题和一个可重现的例子:

我有一对起点-终点。 对于每个起点,我想对给定condition1 的目的地中的积分求和。但是,有两个棘手的问题。

  1. 每个起点-终点对中的点只能求和一次
  2. 只有当condition2反向通量 中得到满足时,才能对分数进行总结。也就是说,A-B 中的点只能在 condition1==T 和如果有 B-A 对其中 condition2==T 时相加

可重现的例子:

library(data.table)
dt <-  data.table( origin = c("A", "A", "A", "A", "A", "A", "B", "B", "A", "A", "C", "C", "B", "B", "B", "B", "B", "C", "C", "B", "A", "C", "C", "C", "C", "C", "A", "A", "C", "C", "B", "B"),
                   destination = c("A", "A", "A", "A", "B", "B", "A", "A", "C", "C", "A", "A", "B", "B", "B", "C", "C", "B", "B", "A", "B", "C", "C", "C", "A", "A", "C", "C", "B", "B", "C", "C"),
                   points_in_dest = c(5, 5, 5, 5, 4, 4, 5, 5, 3, 3, 5, 5, 4, 4, 4, 3, 3, 4, 4, 5, 4, 3, 3, 3, 5,5, 3, 3, 4, 4, 3, 3),
                   depart_time = c(7, 8, 16, 18, 7, 8, 16, 18, 7, 8, 16, 18, 7, 8, 16, 7, 8, 16, 18, 8, 16, 7, 8, 18, 7, 8, 16, 18, 7, 8, 16, 18),   
                   travel_time = c(0, 0, 0, 0, 70, 10, 70, 10, 10, 10, 70, 70, 0, 0, 0, 70, 10, 10, 70, 70, 10, 0, 0, 0, 10, 70, 10, 70, 10, 70, 70, 10) )

 dt[ depart_time<=8  & travel_time < 60, condition1 := T] # condition 1 - trips must be in the morning and shorter than 60 min
 dt[ depart_time>=16 & travel_time < 60, condition2 := T] # condition 2 - trips must be in the afternoon and shorter than 60 min

如果我只考虑condition1 来总结积分,这就是我得到的。注意这个查询没有处理两个问题:(1)当有多个满足condition1的起点-目的地对时,它是重复计算点,(2)当condition2不满足时,它不排除点

dt[ condition1==T, .(poits = sum(points_in_dest)), by=.(origin)]

>    origin poits
> 1:      A    20
> 2:      B    11
> 3:      C    15

期望的输出

>    origin poits
> 1:      A     9
> 2:      B     7
> 3:      C    12

我的真实数据框约为 8000 万行,因此我希望有一个有效的解决方案,可能基于 data.table。我意识到这是一个棘手的问题,我将不胜感激。提前致谢

背景

这是具有时空限制的可访问性时间地理中的常见问题。问题是,例如,考虑到您的时空限制以及您住在 A 区,您可以选择多少工作机会。 A区有5个工作,B区有4个工作,C区有3个工作,你有资格在所有这些工作。但是,只有在早上可以到办公室 (condition1) 并且可以在下午 4 点之后回到家 (condition2) 的情况下,您才能在工作岗位上工作。

【问题讨论】:

  • 那么depart_time &lt;= 8 条件从何而来?我没有看到它在任何地方指定。还是只是为了说明?即,在您的示例中,您只想要前两行?
  • @DavidArenburg,你说得对,我应该提到depart_time 条件,我只想对第一行求和。但请记住,我的真实数据框有更多的起点-目的地和出发时间对,为了简单起见,这里的示例只是部分子集
  • 会在您的数据集上使用以下内容吗? res &lt;- dt[condition2 == 1L, dt[condition1 == 1L &amp; depart_time &lt; 8][.SD, on = c(destination = "origin", origin = "destination")]] ; res[, .(points = sum(points_in_dest, na.rm = TRUE)), by = origin]。这应该很快,但我不确定我是否完全理解你的问题。或者这个dt[dt[condition2 == 1L], on = c(destination = "origin", origin = "destination"), sum(points_in_dest[depart_time &lt; 8]), by = origin]
  • @DavidArenburg,感谢您的回答。我认为它与我想要的非常接近,但它在我的数据中不起作用,因为它具有更多条件 1 和 2 的组合,因此我更新了问题中提供的示例数据以涵盖所有可能的组合。跨度>

标签: r dataframe data.table aggregate


【解决方案1】:

由于您只想计算每个组合一次,我建议对 unique 子集进行相反的连接(destinationoriginorigindestination)这两个条件,然后像你已经做的那样简单地按原点求和。

我在解决这个问题时遇到了data.table 中的一个错误,因此出现了setattr(res, "sorted", NULL) 行(这将删除密钥)。此解决方法不会影响性能。 I've filled a bug report.

setkey(dt, origin, destination) ## doing this so the `unique` function will work faster
res <- unique(dt[(condition1)])[unique(dt[(condition2)]), 
                                on = c(destination = "origin", origin = "destination"), 
                                nomatch = 0L]
setattr(res, "sorted", NULL) ### Fixing the bug
res[, .(points = sum(points_in_dest)), keyby = origin]
#    origin points
# 1:      A      9
# 2:      B      7
# 3:      C     12

【讨论】:

  • 非常感谢您的回答。它的运行速度非常快,在我的 80M 数据集中用时不到 8 秒!但是,很难说出如此大量的观察结果的期望输出是什么。这就是为什么我试图提供一个包含多种可能性的样本数据,并且您的答案通过了测试(以优异成绩)。让我们看看你的答案在其他人的数据中表现如何,希望他们能在这篇文章中发表评论并提供一些反馈。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-09
  • 1970-01-01
  • 2020-07-01
  • 2021-07-06
  • 2016-05-30
相关资源
最近更新 更多