#r #csv #data.table
Вопрос:
Я знаю, что есть и другие вопросы о переполнении стека о самом быстром способе чтения csv — файлов в R-и на них были даны ответы; data.table, похоже, это правильный путь. Но у меня есть дополнительные требования.
Мне нужно придумать сценарий, который устанавливает операцию различия между двумя группами векторов (чтобы найти количество значений, совпадающих в обоих векторах). Обе группы векторов должны быть извлечены из csv-файлов в двух разных каталогах, dirA и dirB. Каждый вектор в dirA будет сравниваться со всеми векторами в dirB, и будет записано количество совпадений. dirA содержит около 50 файлов, а dirB-3000 файлов различного размера (от 1 до 60 Мбайт).
Ниже приведена моя попытка сделать это с помощью R. Это не так быстро, как я ожидал бы (по сравнению с аналогичным решением, реализованным в Pandas, этот код на 30% медленнее). Один раз чтение 3000 файлов занимает более 120 секунд. Есть ли что — то, чего мне не хватает, или это лучшее, что я могу получить в R-my be, разумно используя векторизацию и многократные сравнения за один раз? Мы будем признательны за любую помощь. Спасибо.
- Я использую data.table версии 1.13.6.
- Я хочу читать все как строку (есть начальные нули и некоторые другие аномалии).
Код:
path_dirA <- "data/processed_data_dirA"
path_dirB <- "data/processed_data_dirB"
fn_dirA <- list.files(here(path_dirA), pattern="csv")
fn_dirB <- list.files(here(path_dirB), pattern="csv")
v_count_matched <- integer()
for (fn1 in fn_dirA) {
f1 <- data.table::fread(here(fn_dirA, fn1), colClasses = 'character')
for (fn2 in fn_dirB) {
f2 <- data.table::fread(here(fn_dirB, fn2), colClasses = 'character')
v_count_matched <- c(v_count_matched, length( fintersect(f1[,1],f2[,1]) ) )
}
}
}
Комментарии:
1. В некоторых случаях
vroom
это быстрее, чем data.table для приема файлов, так как он индексирует их для ленивой загрузки. Возможно, стоит попробовать. github.com/r-lib/vroom2. Вместо того, чтобы объединяться
length( fintersect(f1[,1],f2[,1]) )
дляv_count_matched
каждой итерации цикла, создайте векторv_count_matched
, который имеет длинуfn_dirA
, и заполните его какv_count_matched[fn1] = length( fintersect(f1[,1],f2[,1]) )
.3. Проверьте круг 2 в R Inferno . Проблема может быть в постоянном добавлении, а не в чтении файла.
4. Если вы сравниваете только первый столбец, вы можете прочитать только этот столбец (вместе
fread
сselect
параметром).5. Попробуйте обновить
data.table
, есть некоторые недавние улучшения производительности, которые могут повлиять на ваш случай.
Ответ №1:
В данном конкретном случае большая часть времени уходит на чтение CSV-файлов. Если бы вы могли кэшировать на диске эти CSV-файлы в другом формате с более быстрым временем чтения, вы получили бы максимальную экономию.
Например, если вам нужно ежедневно повторять сравнения, но изменился только один CSV.
Вы могли бы сохранить эти CSV-файлы (кэшированные на диске) в fst
формате. https://www.fstpackage.org/
Комментарии:
1.
vroom
пакет в этом случае бесполезен, потому что его преимуществом является медленная загрузка. Но вам действительно нужно прочитать все содержимое файлов.2. Набор файлов в папке B меняется не так часто. Преобразование их в fst очень помогает и фактически экономит больше всего времени; принятие этого ответа. Другие ответы также помогли мне лучше понять. Так Что Спасибо Вам ВСЕМ.
Ответ №2:
Одним из возможных ускорений было бы использование индексов для добавления данных, а не для объединения:
fn_dirA <- list.files(here(path_dirA), pattern="csv")
fn_dirB <- list.files(here(path_dirB), pattern="csv")
v_count_matched <- vector(NA, length(fn_dirA)*length(n_dirA))
counter = 0
for (fn1 in fn_dirA) {
f1 <- data.table::fread(here(fn_dirA, fn1), colClasses = 'character')
for (fn2 in fn_dirB) {
counter = counter 1
f2 <- data.table::fread(here(fn_dirB, fn2), colClasses = 'character')
v_count_matched[counter] <- length( fintersect(f1[,1],f2[,1]))
}
}
}
Ответ №3:
Я принял ответ, основанный на том, что работало ранее. Тем не менее, я смог значительно сократить время выполнения, просто добавив setkey. Теперь все это занимает 6 часов вместо нескольких дней!