我正在尝试通过data.table调用替换SQL生成的笛卡尔积.我拥有丰富的资产和价值历史,我需要所有组合的子集.假设我有一个表格,其中T = [date,contract,value].在SQL中它看起来像
SELECT a.date, a.contract, a.value, b.contract. b.value
FROM T a, T b
WHERE a.date = b.date AND a.contract <> b.contract AND a.value + b.value < 4
Run Code Online (Sandbox Code Playgroud)
在RI现在有以下内容
library(data.table)
n <- 1500
dt <- data.table(date = rep(seq(Sys.Date() - n+1, Sys.Date(), by = "1 day"), 3),
contract = c(rep("a", n), rep("b", n), rep("c", n)),
value = c(rep(1, n), rep(2, n), rep(3, n)))
setkey(dt, date)
dt[dt, allow.cartesian = TRUE][(contract != i.contract) & (value + i.value < 4)]
Run Code Online (Sandbox Code Playgroud)
我相信我的解决方案首先创建所有组合(在这种情况下为13,500行),然后过滤(到3000).然而SQL(我可能错了)加入子集,更重要的是不要将所有组合加载到RAM中.任何想法如何使用data.table更有效?
使用= .EACHI功能.在data.table 连接和子集中有非常紧密的联系; 即,连接只是另一个子集 - 使用data.table- 而不是通常的整数/逻辑/行名称.它们是以这种方式设计的,并考虑到这些情况.
基于子集的连接允许在加入时将j表达式和分组操作合并在一起.
require(data.table)
dt[dt, .SD[contract != i.contract & value + i.value < 4L], by = .EACHI, allow = TRUE]
Run Code Online (Sandbox Code Playgroud)
这是惯用的方式(如果您i.*只想使用cols作为条件,但也不返回它们),但是,.SD尚未进行优化,并且评估每个组的j-expression .SD是昂贵的.
system.time(dt[dt, .SD[contract != i.contract & value + i.value < 4L], by = .EACHI, allow = TRUE])
# user system elapsed
# 2.874 0.020 2.983
Run Code Online (Sandbox Code Playgroud)
一些使用的案例.SD已经过优化.在处理这些情况之前,您可以通过以下方式解决此问题:
dt[dt, {
idx = contract != i.contract & value + i.value < 4L
list(contract = contract[idx],
value = value[idx],
i.contract = i.contract[any(idx)],
i.value = i.value[any(idx)]
)
}, by = .EACHI, allow = TRUE]
Run Code Online (Sandbox Code Playgroud)
这需要0.045秒,而不是你方法的0.005秒.但每次都要by = .EACHI评估j-expression(因此记忆效率).这是你必须接受的权衡.