Ответ 1
В последнем случае это является следствием функции автоматической индексации в data.table
, так как v1.9.4+. Подробнее читайте: -).
Когда вы выполняете DT[col == .]
или DT[col %in% .]
, при первом запуске автоматически создается индекс. Индекс - это только order
указанного столбца. Вычисление индексов довольно быстро (с использованием сортировки сортировки/истинной сортировки).
Таблица составляет 120 миллионов строк и занимает примерно:
# clean session
require(data.table)
set.seed(1L)
DF = data.table(x=rep(c("a","b","c"),each=40000000), y=sample(c(1,3,6),40000000,T), v=1:9)
system.time(data.table:::forderv(DF, "y"))
# 3.923 0.736 4.712
Боковое примечание: Столбец
y
не должен быть действительно двойным (на котором порядок занимает больше времени). Если мы преобразуем его в целочисленный тип:DF[, y := as.integer(y)] system.time(data.table:::forderv(DF, "y")) # user system elapsed # 0.569 0.140 0.717
Преимущество заключается в том, что любые последующие подмножества в этом столбце с использованием ==
или %in%
будут быстро разгоняться (слайды, R script, видео презентации Мэтта). Например:
# clean session, copy/paste code from above to create DF
system.time(DF[y==6, y := 10])
# user system elapsed
# 4.750 1.121 5.932
system.time(DF[y==6, y := 10])
# user system elapsed
# 4.002 0.907 4.969
Ой, подождите... это не быстро. Но.. индексирование..?!? Мы заменяем один и тот же столбец каждый раз новым значением. Это приводит к тому, что порядок изменения столбца (таким образом, удаляется индекс). Пусть подмножество y
, но изменение v
:
# clean session
require(data.table)
set.seed(1L)
DF = data.table(x=rep(c("a","b","c"),each=40000000), y=sample(c(1,3,6),40000000,T), v=1:9)
system.time(DF[y==6, v := 10L])
# user system elapsed
# 4.653 1.071 5.765
system.time(DF[y==6, v := 10L])
# user system elapsed
# 0.685 0.213 0.910
options(datatable.verbose=TRUE)
system.time(DF[y==6, v := 10L])
# Using existing index 'y'
# Starting bmerge ...done in 0 secs
# Detected that j uses these columns: v
# Assigning to 40000059 row subset of 120000000 rows
# user system elapsed
# 0.683 0.221 0.914
Вы можете видеть, что время вычисления индексов (с использованием двоичного поиска) занимает 0 секунд. Также проверьте ?set2key()
.
Если вы не собираетесь делать повторное подмножество или, как в вашем случае, подмножество и изменение одного и того же столбца, тогда имеет смысл отключить эту функцию, выполнив options(datatable.auto.index = FALSE)
, зарегистрировав # 1264:
# clean session
require(data.table)
options(datatable.auto.index = FALSE) # disable auto indexing
set.seed(1L)
DF = data.table(x=rep(c("a","b","c"),each=40000000), y=sample(c(1,3,6),40000000,T), v=1:9)
system.time(DF[y==6, v := 10L])
# user system elapsed
# 1.067 0.274 1.367
system.time(DF[y==6, v := 10L])
# user system elapsed
# 1.100 0.314 1.443
Различия здесь не так много. Время для векторного сканирования system.time(DF$y == 6)
= 0.448s
.
Подводя итог, в вашем случае векторное сканирование имеет больше смысла. Но в целом идея состоит в том, что лучше заплатить штраф один раз и получить быстрые результаты по будущим подмножествам в этом столбце, а не по векторному сканированию каждый раз.
Функция автоматического индексирования является относительно новой и будет расширяться со временем и, вероятно, оптимизирована (возможно, есть места, на которые мы не смотрели). Отвечая на это Q, я понял, что мы не показываем время для вычисления порядка сортировки (используя
fsort()
, и я предполагаю, что время, проведенное там, может быть причиной того, что тайминги довольно близки, подано # 1265).
Что касается вашего второго дела, который медленно, не совсем уверен, почему. Я подозреваю, что это может быть из-за ненужных копий из R-части. Какую версию R вы используете? В будущем всегда отправляйте свой вывод sessionInfo()
.