Наиболее эффективный способ фильтрации data.table на основе агрегированного значения - PullRequest
1 голос
/ 23 апреля 2020

Каков рекомендуемый / «лучший» способ (по производительности) для фильтрации data.table на основе некоторых критериев, рассчитанных для агрегированной формы этой самой таблицы.

A reprex говорит более 1000 слова:

library(data.table)
DT <- data.table(grp = rep(LETTERS[1:3], each = 3), x = 1:9)
setkey(DT, "grp")
DT[DT[, .(nok = any(x == 4)), by = grp][nok == FALSE]]
DT[DT[, .GRP[all(x != 4)], by = grp]]

Я мог бы сразу подумать об этих 2 решениях, и мое внутреннее чувство подсказывает мне, что вторая форма должна быть «лучше» (меньшие промежуточные таблицы хранятся, и мне не нужно связывать результаты), но мне было интересно, есть ли каноническая форма выполнения этого?

Может быть, мне не нужно использовать объединение в первую очередь и могу использовать сгруппированный фильтр для аргумента i?

Это, очевидно, не работает должным образом (by, очевидно, оказывает влияние только на j):

DT[all(x != 4), by = grp]

В то время как этот SO ответ показывает еще один способ делать то же самое, моя главная забота о производительности. Таким образом, я хотел бы знать, какая из этих опций будет хорошо масштабироваться для больших таблиц, если я хочу продолжить работу с отфильтрованной таблицей data.table (то есть использовать другое выражение j для отфильтрованного результата)

В моем реальном случае у меня есть около 16 строк Mio, около 40 000 уникальных ключей и 14 столбцов.

Таким образом, набор эталонных данных может выглядеть следующим образом:

bench <- data.table(keys = rep(paste0("k", 1:40000), 400))
bench[, paste0("cols", 1:13) := replicate(13, sample(40000 * 400, TRUE), 
                                          simplify = FALSE)]

Пока я ищу обобщенный c ответ (если возможно) независимо от выбранного окончательного фильтра, фактический фильтр будет выяснить, какие группы содержат любое значение NA.

Ответы [ 2 ]

2 голосов
/ 23 апреля 2020

Я узнал от этого поста

Вы можете сделать это.

DT[DT[,.I[all(x!=4)],by=.(grp)]$V1,]
1 голос
/ 24 апреля 2020

Я не думаю, что есть универсальный способ c, который применим ко всем ситуациям. Это зависит от характеристик набора данных, а также критериев фильтрации. Набор данных может иметь несколько небольших групп или целочисленных ключей, в то время как фильтрация может быть реализована с использованием более быстрого низкоуровневого подхода к программированию.

Вот несколько вариантов, касающихся вашей реальной проблемы (т. Е. Фильтрация по группам с NA в одном из столбцов):

DT_keys <- copy(DT)
system.time(setkey(DT_keys, keys))
#   user  system elapsed 
#   1.50    0.67    1.32 

DT_cols1 <- copy(DT)
system.time(setkey(DT_cols1, cols1))
#   user  system elapsed 
#   4.21    0.21    1.30 

microbenchmark::microbenchmark(times=1L,
    m0 = DT_keys[, keys[is.na(cols1)], keys]$keys,
    m1 = DT_keys[, if (anyNA(cols1)) keys, keys]$keys,
    m2 = DT_cols1[.(NA_integer_)]$keys
)

время для 16 миллионов строк фиктивных данных:

Unit: milliseconds
 expr       min        lq      mean    median        uq       max neval
   m0 90.675005 90.675005 90.675005 90.675005 90.675005 90.675005     1
   m1 56.548620 56.548620 56.548620 56.548620 56.548620 56.548620     1
   m2  4.010301  4.010301  4.010301  4.010301  4.010301  4.010301     1

Время очень быстрое для размера вашего фактического набора данных. Не так много значительного времени будет сэкономлено, если вы не запустите фильтрацию сотни раз. Может быть, вы захотите сохранить некоторые другие типы времени помимо времени выполнения.

данные:

library(data.table)
set.seed(0L)
nk <- 4e4L
nn <- 400L
DT <- data.table(keys = rep(paste0("k", 1L:nk), nn))
DT[, paste0("cols", 1L:13L) := 
    replicate(13L, sample(c(NA_integer_, 1L:nk), nk * nn, TRUE), simplify = FALSE)]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...