我有一个Public Class Location
{
private int piecesA;
private int piecesB;
private int pieces = piecesA + piecesB;
//methods etc.
}
,它为不同的公交路线(data.table
)提供了位置(origin
和destination
)之间的联系。
route_id
出于我想要做的目的,如果有library(data.table)
library(magrittr)
# data for reproducible example
dt <- data.table( origin = c('A','B','C', 'F', 'G', 'H'),
destination = c('B','C','D', 'G', 'H', 'I'),
freq = c(2,2,2,10,10,10),
route_id = c(1,1,1,2,2,2), stringsAsFactors=FALSE )
# > dt
# origin destination freq route_id
# 1: A B 2 1
# 2: B C 2 1
# 3: C D 2 1
# 4: F G 10 2
# 5: G H 10 2
# 6: H I 10 2
提供连接route_id
和连接A-B
,那么我想添加到数据连接B-C
用于相同的A-C
,依此类推。
问题:到目前为止,我已经创建了一个简单的代码来完成这项工作,但是:
route_id
(我的真实数据有数十万次观察)for loop
连接,但输出中应该没有B-C
。
C-B
# loop
# a) get a data subset corresponding to each route_id
# b) get all combinations of origin-destination pairs
# c) row bind the new pairs to original data
for (i in unique(dt$route_id)) {
temp <- dt[ route_id== i,]
subset_of_pairs <- expand.grid(temp$origin, temp$destination) %>% setDT()
setnames(subset_of_pairs, c("origin", "destination"))
dt <- rbind(dt, subset_of_pairs, fill=T)
}
# assign route_id and freq to new pairs
dt[, route_id := route_id[1L], by=origin]
dt[, freq := freq[1L], by=route_id]
# Keepe only different pairs that are unique
dt[, origin := as.character(origin) ][, destination := as.character(destination) ]
dt <- dt[ origin != destination, ][order(route_id, origin, destination)]
dt <- unique(dt)
答案 0 :(得分:4)
一种方式:
res = dt[, {
stops = c(origin, last(destination))
pairs = combn(.N + 1L, 2L)
.(o = stops[pairs[1,]], d = stops[pairs[2,]])
}, by=route_id]
route_id o d
1: 1 A B
2: 1 A C
3: 1 A D
4: 1 B C
5: 1 B D
6: 1 C D
7: 2 F G
8: 2 F H
9: 2 F I
10: 2 G H
11: 2 G I
12: 2 H I
这假设c(origin, last(destination))
是按顺序排列的完整列表。如果dt
没有足够的信息来构建完整的订单,则任务变得更加困难。
如果需要来自dt
的变种,则res[dt, on=.(route_id), freq := i.freq]
等更新加入有效。
这样的任务总是有内存不足的风险。在这种情况下,OP有多达一百万行,包含最多341个停止的组,因此最终结果可能与1e6/341*choose(341,2)
= 1.7亿行一样大。这是可以管理的,但总的来说这种分析不会扩展。
工作原理
通常,data.table语法可以像处理组的循环一样对待:
DT[, {
...
}, by=g]
这比循环有一些优点:
...
正文中创建的任何内容都不会污染工作区。.N
,.SD
,.GRP
和.BY
以及.()
的{{1}}。在上面的代码中,list()
找到从1开始的索引对。#stop(= .N + 1,其中.N是与给定route_id关联的数据子集中的行数) 。它是一个矩阵,第一行对应一对的第一个元素;和第二行与第二行。 pairs
应评估为列列表;此处...
缩写为list()
。
进一步改进
我猜这段时间主要用于多次计算.()
。如果多个路由具有相同的#stops,则可以通过预先计算来解决此问题:
combn
然后在主代码中抓取Ns = dt[,.N, by=route_id][, unique(N)]
cb = lapply(setNames(,Ns), combn, 2)
。或者,定义一个使用memoization的pairs = cb[[as.character(.N)]]
函数,以避免重新计算。