base R быстрее, чем readr, для чтения нескольких файлов CSV

Существует множество документации о том, как читать несколько CSV и связывать их в один фрейм данных. У меня есть более 5000 файлов CSV, которые мне нужно прочитать и привязать к одной структуре данных.

В частности, я следил за обсуждением здесь: Проблема при загрузке нескольких файлов .csv в один фрейм данных в R с помощью rbind

Странно то, что base R работает намного быстрее, чем любое другое решение, которое я пробовал.

Вот как выглядит мой CSV:

> head(PT)
  Line          Timestamp       Lane.01 Lane.02 Lane.03 Lane.04 Lane.05 Lane.06 Lane.07 Lane.08
1    PL1    05-Jan-16 07:17:36      NA      NA      NA      NA      NA      NA      NA      NA
2    PL1    05-Jan-16 07:22:38      NA      NA      NA      NA      NA      NA      NA      NA
3    PL1    05-Jan-16 07:27:41      NA      NA      NA      NA      NA      NA      NA      NA
4    PL1    05-Jan-16 07:32:43    9.98   10.36   10.41   10.16   10.10    9.97   10.07    9.59
5    PL1    05-Jan-16 07:37:45    9.65    8.87    9.88    9.86    8.85    8.75    9.19    8.51
6    PL1    05-Jan-16 07:42:47    9.14    8.98    9.29    9.04    9.01    9.06    9.12    9.08

Я создал три метода для чтения и привязки данных. Файлы находятся в отдельном каталоге, который я определяю как:

dataPath <- "data"
PTfiles <- list.files(path=dataPath, full.names = TRUE)

Метод 1: База R

classes <- c("factor", "character", rep("numeric",8))

# build function to load data
load_data <- function(dataPath, classes) { 
   tables <- lapply(PTfiles, read.csv, colClasses=classes, na.strings=c("NA", ""))
   do.call(rbind, tables)
}

#clock
method1 <- system.time(
   PT <- load_data(path, classes)
)

Метод 2: read_csv В этом случае я создал функцию-оболочку для read_csv, чтобы использовать

#create wrapper function for read_csv
read_csv.wrap <- function(x) { read_csv(x, skip = 1, na=c("NA", ""),
                      col_names = c("tool", "timestamp", paste("lane", 1:8, sep="")),
                      col_types = 
                         cols(
                            tool = col_character(),
                            timestamp = col_character(),
                            lane1 = col_double(),
                            lane2 = col_double(),
                            lane3 = col_double(),
                            lane4 = col_double(),
                            lane5 = col_double(),
                            lane6 = col_double(),
                            lane7 = col_double(),
                            lane8 = col_double()
                           )
                     )
}

##
# Same as method 1, just uses read_csv instead of read.csv

load_data2 <- function(dataPath) { 
   tables <- lapply(PTfiles, read_csv.wrap)
   do.call(rbind, tables)
}

#clock
method2 <- system.time(
   PT2 <- load_data2(path)
)

Метод 3: read_csv + dplyr::bind_rows

load_data3 <- function(dataPath) { 
   tables <- lapply(PTfiles, read_csv.wrap)
   dplyr::bind_rows(tables)
}

#clock
method3 <- system.time(
   PT3 <- load_data3(path)
)

Я не могу понять, почему методы read_csv и dplyr медленнее в течение прошедшего времени, тогда как они должны быть быстрее. Время ЦП уменьшилось, но почему увеличилось затраченное время (файловая система)? Что тут происходит?

Изменить - я добавил метод data.table, как было предложено в комментариях.

Метод 4 data.table

library(data.table)

load_data4 <- function(dataPath){
   tables <- lapply(PTfiles, fread)
   rbindlist(tables)
}

method4 <- system.time(
   PT4 <- load_data4(path)
)

Метод data.table - самый быстрый с точки зрения ЦП. Но все еще остается вопрос, что происходит с read_csv методами, которые делают их такими медленными.

> rbind(method1, method2, method3, method4)
        user.self sys.self elapsed
method1      0.56     0.39    1.35
method2      0.42     1.98   13.96
method3      0.36     2.25   14.69
method4      0.34     0.67    1.74

person Lloyd Christmas    schedule 17.05.2017    source источник
comment
Я думаю, data.table::fread() самый быстрый   -  person cirofdo    schedule 17.05.2017
comment
Затем следует data.table::rbindlist(), чтобы склеить их вместе.   -  person Dirk Eddelbuettel    schedule 17.05.2017
comment
Я добавил к вопросу метод data.table, и это быстро. У меня все еще остается вопрос, почему метод read_csv такой медленный.   -  person Lloyd Christmas    schedule 17.05.2017
comment
Если вы сможете закрепить это на очень конкретном воспроизводимом примере с одним файлом, которым вы готовы поделиться, вам может показаться более продуктивным поднять это как проблему на github. Если это воспроизводимо, Хэдли, вероятно, будет очень заинтересован разрыв в производительности такой величины с базой R.   -  person joran    schedule 17.05.2017
comment
Я постараюсь перефразировать более конкретно. Хотя с одним файлом может быть сложно, для этого теста я использовал каталог со 140 файлами CSV. С одним файлом время в основном будет равно нулю. Следует ли мне удалить это и опубликовать новый вопрос?   -  person Lloyd Christmas    schedule 17.05.2017
comment
Ну, я просто думаю, что ваше расследование здесь действительно только частично. Я бы потратил некоторое время на анализ ваших вариантов, чтобы увидеть, на что действительно тратится время в каждом конкретном случае. Если время действительно тратится на read_csv, вы должны иметь возможность воспроизвести разницу в скорости при чтении одного гигантского CSV без всяких прихотей и привязок.   -  person joran    schedule 17.05.2017
comment
Но без ваших файлов и т. Д. Кому-то другому будет сложно сделать это за вас или помочь, если вы понимаете, о чем я ...   -  person joran    schedule 17.05.2017
comment
Фрагмент привязки также важен, так как он может повлиять на время вычислений. Я проведу некоторое время с профилировщиком, пока вы предлагаете и переформулирую вопрос в более воспроизводимой форме.   -  person Lloyd Christmas    schedule 17.05.2017
comment
Вы пробовали указывать типы столбцов? Еще один полезный способ сделать это, который я недавно видел, - это запросить локальный Apache Drill через сержант (см. здесь). Его установка - это больше работы, чем описанное выше, но, если оно есть, это мощно.   -  person alistaire    schedule 18.05.2017
comment
Пара моментов - как говорили другие, это трудно воспроизвести без хотя бы образца файлов, которые вы читаете. - Было бы неплохо передать PT-файлы в качестве параметра в вашей функции (ах), а не читать его из глобального env - Вместо lapply взгляните на furrr, который должен хорошо работать с таким большим количеством файлов (и не зависит от того, какую функцию чтения вы в конечном итоге используете!)   -  person Moohan    schedule 12.08.2019
comment
Посмотрите на этот ответ: Сравнение скорости чтения и чтения таблицы для чтения первых 1M строк из 100M.   -  person fmassica    schedule 08.10.2019
comment
Возможно, попробуйте использовать map_dfr () с read_csv. Это кажется наиболее интуитивно понятным способом чтения и итерации с тидиверсией. например map_dfr(list.files(), read_csv)   -  person thus__    schedule 10.02.2020


Ответы (1)


Я бы сделал это в терминале (Unix). Я бы поместил все файлы в одну и ту же папку, а затем перешел в эту папку (в терминале), используя следующую команду, чтобы создать только один файл CSV:

cat *.csv > merged_csv_file.csv

Одно замечание относительно этого метода заключается в том, что заголовок каждого файла будет отображаться в середине наблюдений. Чтобы решить эту проблему, я предлагаю вам сделать:

Получить только заголовок из первого файла

head -2 file1.csv > merged_csv_file.csv

затем пропустите первые строки «X» из других файлов с помощью команды folling, где «X» - количество строк, которые нужно пропустить.

tail -n +3 -q file*.csv >> merged_csv_file.csv

-n +3 делает строки вывода хвоста от 3-го до конца, -q говорит ему не печатать заголовок с именем файла (прочтите man), >> добавляет в файл, а не перезаписывает его как>.

person Marcio Rodrigues    schedule 26.02.2020