Как присоединяться к таблице данных по условию
Я хочу добавить новый столбец в мою таблицу данных. Этот столбец должен содержать сумму другого столбца всех строк, удовлетворяющих определенному условию. Пример: таблица data.table выглядит следующим образом:
require(data.table)
DT <- data.table(n=c("a", "a", "a", "a", "a", "a", "b", "b", "b"),
t=c(10, 20, 33, 40, 50, 22, 25, 34, 11),
v=c(20, 15, 16, 17, 11, 12, 20, 22, 10)
)
DT
n t v
1: a 10 20
2: a 20 15
3: a 33 16
4: a 40 17
5: a 50 11
6: a 22 12
7: b 25 20
8: b 34 22
9: b 11 10
Для каждой строки x и каждой строки i, где abs (t [i] - t [x]) <= 10, я хочу рассчитать
foo = sum( v[i] * abs(t[i] - t[x]) )
В SQL я бы решил это, используя самосоединение. В R я смог сделать это, используя цикл for:
for (i in 1:nrow(DT))
DT[i, foo:=DT[n==DT[i]$n & abs(t-DT[i]$t)<=10, sum(v * abs(t-DT[i]$t) )]]
DT
n t v foo
1: a 10 20 150
2: a 20 15 224
3: a 33 16 119
4: a 40 17 222
5: a 50 11 170
6: a 22 12 30
7: b 25 20 198
8: b 34 22 180
9: b 11 10 0
К сожалению, я должен делать это довольно часто, а таблица, с которой я работаю, намного больше. Подход за петлю работает, но слишком медленный. Я играл с пакетом sqldf без реального прорыва. Я хотел бы сделать это, используя некоторую магию data.table и там мне нужна ваша помощь:-). Я думаю, что необходимо какое-то самосоединение при условии, что разница значений t меньше порога.
Последующие действия:
У меня есть следующий вопрос: в моем приложении это соединение выполняется снова и снова. Изменение v, но t и n всегда одно и то же. Поэтому я думаю о том, чтобы каким-то образом сохранить, какие строки принадлежат друг другу. Любые идеи, как это сделать умным способом?
Ответы
Ответ 1
Попробуйте следующее:
unique(merge(DT, DT, by="n")[abs(t.x - t.y) <= 10, list(n, sum(v.x * abs(t.x - t.y))), by=list(t.x, v.x)])
Разбивка для указанной строки:
Вы можете объединить таблицу с собой, выход также будет data.table. Обратите внимание, что именам столбцов присваивается суффикс .x
и .y
merge(DT, DT, by="n")
... вы можете просто фильтровать и вычислять как с любым DT
# this will give you your desired rows
[abs(t.x - t.y), ]
# this is the expression you outlined
[ ... , sum(v.x * abs(t.x - t.y)) ]
# summing by t.x and v.x
[ ... , ... , by=list(t.x, v.x)]) ]
Затем, наконец, завершим все это в unique
, чтобы удалить любые дублированные строки.
UPDATE: это должен быть комментарий, но слишком длинный
Ниже приведена строка, соответствующая вашему результату. Единственное различие между этим и тем, что находится в верхней части этого ответа, - это термин v.y
в sum(v.y * ...)
, однако оператор by
все еще использует v.x
. Это намеренно?
unique(merge(DT, DT, by="n")[abs(t.x - t.y) <= 10, list(n, sum(v.y * abs(t.x - t.y))), by=list(t.x, v.x)])
Ответ 2
Отличный вопрос. Этот ответ - всего лишь дегустатор, на самом деле отвечающий Рикардо.
В идеале мы хотим избежать большой декартовой самоподготовки для повышения эффективности. К сожалению, объединения диапазонов (FR # 203) еще не реализованы. Тем временем, используя самый последний v1.8.7 (untested):
setkey(DT,n,t)
DT[,from:=DT[.(n,t-10),which=TRUE,roll=-Inf,rollends=TRUE]]
DT[,to:=DT[.(n,t+10),which=TRUE,roll=+Inf,rollends=TRUE]]
DT[,foo:=0L]
for (i in 1:nrow(DT)) {
s = seq.int(DT$from[i],DT$to[i])
set(DT, i, "foo", DT[,sum(v[s]*abs(t[s]-t[i]))] )
}
Как только FR # 203 будет выполнен, логика выше будет встроена, и она должна стать одним лайнером:
setkey(DT,n,t)
DT[.(n,.(t-10,t+10),t), foo:=sum(v*abs(t-i.t))]
Во втором столбце таблицы i
есть столбец с двумя столбцами (указывающий на соединение). Это должно быть быстрым, потому что, как обычно, j
будет оцениваться для каждой строки i
, не создавая огромную декартовую таблицу самосоединения.
Это текущее мышление, во всяком случае.