Мне нравится этот материал data.table, в равной степени за его скорость выполнения и за экономичный способ написания сценариев.
Я использую его даже на маленьких столах.
Я регулярно подмножаю таблицы таким образом: DT[, .(id1, id5)]
а не так: DT[, c("id1", "id5")]
Сегодня я измерил скорость обоих и был поражен разницей в скорости на маленьких столах. Экономный метод намного медленнее.
Является ли эта разница чем-то преднамеренным?
Есть ли стремление сделать экономичный способ сходящимся по скорости исполнения к другому?
(Это считается, когда мне приходится повторяющимся образом подмножать несколько небольших таблиц.)
Убунту 18.04
R версия 3.5.3 (11 марта 2019 г.)
таблица данных 1.12.0
Оперативная память 32 ГБ
Процессор Intel® Core™ i7-8565U с тактовой частотой 1,80 ГГц × 8
library(data.table)
library(microbenchmark)
N <- 2e8
K <- 100
set.seed(1)
DT <- data.table(
id1 = sample(sprintf("id%03d", 1:K), N, TRUE), # large groups (char)
id2 = sample(sprintf("id%03d", 1:K), N, TRUE), # large groups (char)
id3 = sample(sprintf("id%010d", 1:(N/K)), N, TRUE), # small groups (char)
id4 = sample(K, N, TRUE), # large groups (int)
id5 = sample(K, N, TRUE), # large groups (int)
id6 = sample(N/K, N, TRUE), # small groups (int)
v1 = sample(5, N, TRUE), # int in range [1,5]
v2 = sample(5, N, TRUE), # int in range [1,5]
v3 = sample(round(runif(100, max = 100), 4), N, TRUE) # numeric e.g. 23.5749
)
microbenchmark(
DT[, .(id1, id5)],
DT[, c("id1", "id5")]
)
Unit: seconds
expr min lq mean median uq max neval
DT[, .(id1, id5)] 1.588367 1.614645 1.929348 1.626847 1.659698 12.33872 100
DT[, c("id1", "id5")] 1.592154 1.613800 1.937548 1.628082 2.184456 11.74581 100
N <- 2e5
DT2 <- data.table(
id1 = sample(sprintf("id%03d", 1:K), N, TRUE), # large groups (char)
id2 = sample(sprintf("id%03d", 1:K), N, TRUE), # large groups (char)
id3 = sample(sprintf("id%010d", 1:(N/K)), N, TRUE), # small groups (char)
id4 = sample(K, N, TRUE), # large groups (int)
id5 = sample(K, N, TRUE), # large groups (int)
id6 = sample(N/K, N, TRUE), # small groups (int)
v1 = sample(5, N, TRUE), # int in range [1,5]
v2 = sample(5, N, TRUE), # int in range [1,5]
v3 = sample(round(runif(100, max = 100), 4), N, TRUE) # numeric e.g. 23.5749
)
microbenchmark(
DT2[, .(id1, id5)],
DT2[, c("id1", "id5")]
)
Unit: microseconds
expr min lq mean median uq max neval
DT2[, .(id1, id5)] 1405.042 1461.561 1525.5314 1491.7885 1527.8955 2220.860 100
DT2[, c("id1", "id5")] 614.624 640.617 666.2426 659.0175 676.9355 906.966 100
Вы можете исправить форматирование вашего сообщения, используя одну строку из трех обратных кавычек до и после фрагмента кода:
```
code
```
Это считается, когда мне приходится повторяющимся образом подмножать несколько небольших таблиц.
Я предполагаю, что повторный выбор столбцов из небольших таблиц - это то, чего следует и в большинстве случаев можно избежать...? Поскольку j
в DT[i, j, by]
поддерживает и оптимизирует такое большое разнообразие входных данных, я думаю, что некоторые накладные расходы на его синтаксический анализ являются естественными.
Что касается других способов решения вашей проблемы (и, возможно, это лучше подходит для переполнения стека, если вы хотите поговорить об этом подробнее)... В зависимости от того, что еще вы хотите сделать с таблицей, вы можете просто удалить другие столбцы , DT[, setdiff(names(DT), cols) := NULL]
и продолжайте использовать DT напрямую.
Если вы все же предпочитаете использовать подмножество, захват указателей столбцов выполняется намного быстрее, чем любой вариант, который вы рассматривали здесь, хотя таким образом изменения результата повлияют на исходную таблицу:
library(data.table)
library(microbenchmark)
N <- 2e8
K <- 100
set.seed(1)
DT <- data.table(
id1 = sprintf("id%03d", 1:K), # large groups (char)
id2 = sprintf("id%03d", 1:K), # large groups (char)
id3 = sprintf("id%010d", 1:(N/K)), # small groups (char)
id4 = sample(K), # large groups (int)
id5 = sample(K), # large groups (int)
id6 = sample(N/K), # small groups (int)
v1 = sample(5), # int in range [1,5]
v2 = sample(5), # int in range [1,5]
v3 = round(runif(100, max = 100), 4), # numeric e.g. 23.5749
row = seq_len(N)
)
cols = c("id1", "id5")
microbenchmark(times = 3,
expression = DT[, .(id1, id5)],
index = DT[, c("id1", "id5")],
dotdot = DT[, ..cols],
oddball = setDT(lapply(setNames(cols, cols), function(x) DT[[x]]))[],
oddball2 = setDT(unclass(DT)[cols])[]
)
Unit: microseconds
expr min lq mean median uq max neval
expression 1249753.580 1304355.3415 1417166.9297 1358957.103 1500873.6045 1642790.106 3
index 1184056.302 1191334.4835 1396372.3483 1198612.665 1502530.3715 1806448.078 3
dotdot 1084521.234 1240062.2370 1439680.6980 1395603.240 1617260.4300 1838917.620 3
oddball 92.659 171.8635 568.5317 251.068 806.4680 1361.868 3
oddball2 66.582 125.9505 150.7337 185.319 192.8095 200.300 3
(Я убрал рандомизацию из вашего примера и уменьшил тест в # раз, потому что был нетерпелив.)
Я так и не нашел способа напрямую вызвать подмножество списка R (которое используется после unclass
выше).
Относительно «редактирования результата будет изменена исходная таблица», я имею в виду:
myDT = data.table(a = 1:2, b = 3:4)
# standard way
res <- myDT[, "a"]
res[, a := 0]
myDT
# a b
# 1: 1 3
# 2: 2 4
# oddball, grabbing pointers
res2 <- setDT(unclass(myDT)["a"])
res2[, a := 0]
myDT
# a b
# 1: 0 3
# 2: 0 4
Хорошо, сегодня я узнал что-то новое и быстрое (чудаки), и я обратил внимание на то, что существует компромисс между скоростью и экономным кодированием. Итак, стакан наполовину полон! Спасибо!
Я думаю, что # 852 связано
Самый полезный комментарий
Вы можете исправить форматирование вашего сообщения, используя одну строку из трех обратных кавычек до и после фрагмента кода:
Я предполагаю, что повторный выбор столбцов из небольших таблиц - это то, чего следует и в большинстве случаев можно избежать...? Поскольку
j
вDT[i, j, by]
поддерживает и оптимизирует такое большое разнообразие входных данных, я думаю, что некоторые накладные расходы на его синтаксический анализ являются естественными.Что касается других способов решения вашей проблемы (и, возможно, это лучше подходит для переполнения стека, если вы хотите поговорить об этом подробнее)... В зависимости от того, что еще вы хотите сделать с таблицей, вы можете просто удалить другие столбцы ,
DT[, setdiff(names(DT), cols) := NULL]
и продолжайте использовать DT напрямую.Если вы все же предпочитаете использовать подмножество, захват указателей столбцов выполняется намного быстрее, чем любой вариант, который вы рассматривали здесь, хотя таким образом изменения результата повлияют на исходную таблицу:
(Я убрал рандомизацию из вашего примера и уменьшил тест в # раз, потому что был нетерпелив.)
Я так и не нашел способа напрямую вызвать подмножество списка R (которое используется после
unclass
выше).Относительно «редактирования результата будет изменена исходная таблица», я имею в виду: