Я пытаюсь быстро получить доступ к подмножеству большой таблицы данных. Данные имеют три столбца, все числовые c (с плавающей запятой), все с очень небольшим повторением. Два столбца - это данные, для которых я хотел бы выполнить бинарный поиск, а третий столбец содержит числа, которые мне действительно интересны. По сути, у меня есть (x, y, z) данные, которые я хотел бы указать диапазон в x и диапазон в y и возвращают все строки в этих диапазонах.
# Generate some toy data of about the same size as the real data
DT <- data.table(x=runif(2000000), y=runif(2000000), z=runif(2000000))
head(DT)
# x y z
# 1: 0.2675023 0.5725162 0.4162230
# 2: 0.1444540 0.8114941 0.1557195
# 3: 0.3607260 0.8159502 0.9705079
# 4: 0.3370213 0.9217284 0.5269885
# 5: 0.1085204 0.6312943 0.9676716
# 6: 0.1076674 0.1623447 0.1753712
ranges <- data.frame(x_min=runif(10000, max = 0.5), x_max=runif(10000, min = 0.5),
y_min=runif(10000, max = 0.5), y_max=runif(10000, min = 0.5))
head(ranges)
# x_min x_max y_min y_max
# 1 0.43817551 0.6720366 0.28052942 0.6309755
# 2 0.07469295 0.6744950 0.23170272 0.8431767
# 3 0.29520846 0.6991277 0.01882153 0.5162244
# 4 0.10500034 0.8977652 0.04806678 0.9528880
# 5 0.20168728 0.5655350 0.34401695 0.8241058
# 6 0.44158099 0.6739211 0.05359761 0.5832320
Вот наглядный пример того, что я пытаюсь сделать; Я хочу, чтобы все точки внутри красного прямоугольника, где края прямоугольника определяются максимальным и минимальным значениями диапазонов x и y. Тем не менее, у меня есть много красных прямоугольников, над которыми я буду зацикливаться.
plot(DT$x, DT$y)
rect(xleft = ranges$x_min[1], xright = ranges$x_max[1],
ybottom = ranges$y_min[1], ytop = ranges$y_max[1], border = "red")
В настоящее время код, с которым я работаю, использует векторное сканирование, а не бинарный поиск (я думаю), но делает именно то, что мне бы хотелось.
lapply(seq_len(nrow(ranges)), function(i){
DT[x%between%c(ranges[i,]$x_min, ranges[i,]$x_max)&
y%between%c(ranges[i,]$y_min, ranges[i,]$y_max)]
})
Однако, согласно profvis
, это все еще самый медленный шаг в процессе и учитывая, что я новичок в мире data.table
, я хотел бы убедиться, что нет ничего очевидного, что я пропускаю. Насколько я могу судить, можно было бы ускорить это, используя ключи data.table для запуска двоичного поиска, а не векторного сканирования. Однако я не смог понять, как искать диапазон, а не одно значение.
Этот вопрос задает нечто очень похожее, но лучший ответ (от Мэтта) указывает на то, что это было нелегко сделать в 2014 году, когда вопрос был опубликован. Он отмечает, что проблема такого рода действительно требует реализации объединения диапазонов и ссылается на запрос Feature на странице GitHub, который был с тех пор решен (через пару месяцев после открытия).
Три года спустя, вопрос был обновлен с помощью новой функциональности %between%
, которую я уже реализовал, но я все еще не думаю, что здесь используется бинарный поиск по данным. Запрос на функцию подразумевал, что идеальное решение будет иметь форму DT[J(id,DT(from,to)),...]
, которая явно использует синтаксис J()
для использования ключей.
Использует ли синтаксис %ween% фактический двоичный поиск под капотом? Если нет, как я могу предоставить два диапазона и при этом использовать функцию быстрого бинарного поиска?
PS dplyr
* filter()
примерно в 3 раза медленнее в наборе данных, так что это не так.