Один из способов:
require(data.table)
dt <- data.table(sample_data)
# multiple seems to be a character, convert to numeric
dt[, multiple := as.numeric(multiple)]
setkey(dt, "multiple")
dt[J(rep(unique(multiple), unique(multiple))), allow.cartesian=TRUE]
Все, кроме последней строки, должно быть простым. В последней строке используется подмножество с использованием ключевого столбца с помощью J(.)
. Для каждого значения в J(.)
соответствующее значение сопоставляется с «ключевым столбцом», и возвращается совпадающее подмножество.
То есть, если вы сделаете dt[J(1)]
, вы получите подмножество, где multiple = 1
. И если вы обратите внимание, выполнение dt[J(rep(1,2)]
даст вам то же самое подмножество, но дважды. Обратите внимание, что есть разница между передачей dt[J(1,1)]
и dt[J(rep(1,2)]
. В первом случае значения (1,1) сопоставляются с первыми двумя ключевыми столбцами таблицы data.table соответственно, а во втором — путем сопоставления (1 и 2) с < em>first-key столбец таблицы data.table.
Итак, если мы передаем одно и то же значение столбца 2 раза в J(.)
, то оно дублируется дважды. Мы используем этот трюк, чтобы пройти 1 1-кратно, 2 2-кратно и т. д., и это то, что делает часть rep(.)
. rep(.)
дает 1,2,2,3,3,3,4,4,4,4.
И если в результате соединения получается больше строк, чем max(nrow(dt), nrow(i))
(i — это вектор представления, который находится внутри J(.)
), вы должны явно использовать allow.cartesian = TRUE
для выполнения этого соединения (я думаю, это новая функция из data.table 1.8.8).
Редактировать. Вот некоторые результаты сравнительного анализа, которые я провел на «относительно» больших данных. Я не вижу никакого всплеска выделения памяти в обоих методах. Но мне еще предстоит найти способ отслеживать пиковое использование памяти в функции в R. Я уверен, что видел такой пост здесь, на SO, но в данный момент он ускользает от меня. Я отпишусь снова. А пока вот тестовые данные и некоторые предварительные результаты на случай, если кому-то будет интересно или захочется испытать их на себе.
# dummy data
set.seed(45)
yr <- 1900:2013
sz <- sample(10:50, length(yr), replace = TRUE)
token <- unlist(sapply(sz, function(x) do.call(paste0, data.frame(matrix(sample(letters, x*4, replace=T), ncol=4)))))
multiple <- rep(sample(500:5000, length(yr), replace=TRUE), sz)
DF <- data.frame(yr = rep(yr, sz),
token = token,
multiple = multiple, stringsAsFactors=FALSE)
# Arun's solution
ARUN.DT <- function(dt) {
setkey(dt, "multiple")
idx <- unique(dt$multiple)
dt[J(rep(idx,idx)), allow.cartesian=TRUE]
}
# Ricardo's solution
RICARDO.DT <- function(dt) {
setkey(dt, key="yr")
newDT <- setkey(dt[, rep(NA, list(rows=length(token) * unique(multiple))), by=yr][, list(yr)], 'yr')
newDT[, tokenReps := as.character(NA)]
# Add the rep'd tokens into newDT, using recycling
newDT[, tokenReps := dt[.(y)][, token], by=list(y=yr)]
newDT
}
# create data.table
require(data.table)
DT <- data.table(DF)
# benchmark both versions
require(rbenchmark)
benchmark(res1 <- ARUN.DT(DT), res2 <- RICARDO.DT(DT), replications=10, order="elapsed")
# test replications elapsed relative user.self sys.self
# 1 res1 <- ARUN.DT(DT) 10 9.542 1.000 7.218 1.394
# 2 res2 <- RICARDO.DT(DT) 10 17.484 1.832 14.270 2.888
Но, как говорит Рикардо, может не иметь значения, если у вас закончилась память. Таким образом, в этом случае должен быть компромисс между скоростью и памятью. Что я хотел бы проверить, так это пиковую память, используемую в обоих методах, чтобы окончательно сказать, лучше ли использовать Join
.
person
Arun
schedule
24.03.2013
data.table
получила новую функциюsetDT()
, которая принимаетlist
илиdata.frame
и изменяет свой тип по ссылке наdata.table
, без какой-либо копии. Таким образом,setDT(sample_data)
вместоdata.table(sample_data)
может помочь сэкономить память. - person Uwe   schedule 03.12.2017