Обрезка огромного (3,5 ГБ) файла csv для чтения в R


87

Итак, у меня есть файл данных (разделенный точкой с запятой), в котором много деталей и неполные строки (ведущие к подавлению Access и SQL). Это набор данных на уровне округа, разбитый на сегменты, подсегменты и подсегменты (всего ~ 200 факторов) за 40 лет. Короче говоря, он огромен, и он не уместится в памяти, если я попытаюсь просто прочитать его.

Итак, мой вопрос заключается в следующем, учитывая, что мне нужны все округа, но только один год (и только самый высокий уровень сегмента ... что в итоге приводит к примерно 100000 строкам), что было бы наилучшим способом получить это сворачивание в R?

В настоящее время я пытаюсь вырезать ненужные годы с Python, обойдя ограничение на размер файла, читая и работая по одной строке за раз, но я бы предпочел решение только для R (пакеты CRAN в порядке). Есть ли аналогичный способ чтения файлов по частям в R?

Любые идеи очень приветствуются.

Обновить:

  • Ограничения
    • Требуется использовать мою машину, поэтому экземпляров EC2 нет
    • Как можно больше R-only. Скорость и ресурсы в данном случае не важны ... при условии, что моя машина не взорвется ...
    • Как вы можете видеть ниже, данные содержат смешанные типы, с которыми мне нужно работать позже.
  • Данные
    • Размер данных составляет 3,5 ГБ, около 8,5 миллионов строк и 17 столбцов.
    • Пара тысяч строк (~ 2k) искажены, только один столбец вместо 17
      • Это совершенно неважно, и их можно отбросить.
    • Мне нужно всего ~ 100 000 строк из этого файла (см. Ниже)

Пример данных:

County; State; Year; Quarter; Segment; Sub-Segment; Sub-Sub-Segment; GDP; ...
Ada County;NC;2009;4;FIRE;Financial;Banks;80.1; ...
Ada County;NC;2010;1;FIRE;Financial;Banks;82.5; ...
NC  [Malformed row]
[8.5 Mill rows]

Я хочу вырезать несколько столбцов и выбрать два из 40 доступных лет (2009-2010 с 1980-2020), чтобы данные могли поместиться в R:

County; State; Year; Quarter; Segment; GDP; ...
Ada County;NC;2009;4;FIRE;80.1; ...
Ada County;NC;2010;1;FIRE;82.5; ...
[~200,000 rows]

Полученные результаты:

Поработав со всеми внесенными предложениями, я решил, что readLines, предложенные JD и Marek, будут работать лучше всего. Я дал Мареку проверку, потому что он дал образец реализации.

Я воспроизвел слегка адаптированную версию реализации Марека для своего окончательного ответа здесь, используя strsplit и cat, чтобы сохранить только нужные мне столбцы.

Следует также отметить, что это НАМНОГО менее эффективно, чем Python ... например, Python перебирает файл размером 3,5 ГБ за 5 минут, в то время как R занимает около 60 ... но если все, что у вас есть, это R, то это билет.

## Open a connection separately to hold the cursor position
file.in <- file('bad_data.txt', 'rt')
file.out <- file('chopped_data.txt', 'wt')
line <- readLines(file.in, n=1)
line.split <- strsplit(line, ';')
# Stitching together only the columns we want
cat(line.split[[1]][1:5], line.split[[1]][8], sep = ';', file = file.out, fill = TRUE)
## Use a loop to read in the rest of the lines
line <- readLines(file.in, n=1)
while (length(line)) {
  line.split <- strsplit(line, ';')
  if (length(line.split[[1]]) > 1) {
    if (line.split[[1]][3] == '2009') {
        cat(line.split[[1]][1:5], line.split[[1]][8], sep = ';', file = file.out, fill = TRUE)
    }
  }
  line<- readLines(file.in, n=1)
}
close(file.in)
close(file.out)

Неудачи по подходу:

  • sqldf
    • Это определенно то, что я буду использовать для решения этой проблемы в будущем, если данные будут правильно сформированы. Однако, если это не так, SQLite задыхается.
  • Уменьшение карты
    • Если честно, документация меня немного напугала, так что я не успел попробовать. Похоже, что для этого также требуется, чтобы объект был в памяти, что привело бы к поражению точки, если бы это было так.
  • bigmemory
    • Этот подход полностью связан с данными, но одновременно может обрабатывать только один тип. В результате все мои векторы символов упали при помещении в big.table. Если мне нужно разрабатывать большие наборы данных на будущее, я бы подумал об использовании только чисел, чтобы эта опция оставалась в живых.
  • сканировать
    • Сканирование, похоже, имело такие же проблемы с типами, как и большая память, но со всей механикой readLines. Короче говоря, на этот раз это просто не соответствовало требованиям.

3
Если ваши критерии достаточно просты, вы, вероятно, сможете обойтись без использования sedи / или awkсоздания урезанной версии CSV, которую вы можете прочитать напрямую. Поскольку это скорее обходной путь, чем ответ, я оставлю его как комментарий.
Hank Gay,

Я согласен с Хэнком - вы должны использовать правильный инструмент для работы, и если это простая очистка / удаление нерелевантных строк / столбцов, инструменты потока командной строки, такие как sort / sed / awk, великолепны и будут намного менее ресурсоемкими, чем R или python - если вы дадите образец формата ваших файлов, мы, вероятно, могли бы привести пример
Аарон Стэтхэм

Отлично. Сообщите нам, что вы обнаружите.
Шейн

@Hank & Aaron: Обычно я полностью за то, чтобы использовать правильный инструмент для работы, но, учитывая, что это работает на компьютере с Windows, и я изучаю R по ходу работы, я подумал, что было бы хорошим упражнением отказаться от лучших практик и попробуйте это как R-only, если возможно.
FTWynn

2
Для дальнейшего использования ознакомьтесь с пакетом data.table R. freadФункция гораздо быстрее read.table. Используйте что-нибудь вроде, x = fread(file_path_here, data.table=FALSE)чтобы загрузить его как data.frameобъект.
paleo13

Ответы:


39

Моя попытка с readLines. Этот фрагмент кода создается csvс выбранными годами.

file_in <- file("in.csv","r")
file_out <- file("out.csv","a")
x <- readLines(file_in, n=1)
writeLines(x, file_out) # copy headers

B <- 300000 # depends how large is one pack
while(length(x)) {
    ind <- grep("^[^;]*;[^;]*; 20(09|10)", x)
    if (length(ind)) writeLines(x[ind], file_out)
    x <- readLines(file_in, n=B)
}
close(file_in)
close(file_out)

Это почти в точности то, что я только что писал. Я чувствую, что это также будет лучший ответ, учитывая ограничения памяти, смешанные типы и искаженные строки.
FTWynn

10

Я не являюсь экспертом в этом вопросе , но вы можете попробовать MapReduce , что в основном означает использование подхода «разделяй и властвуй». В R есть несколько вариантов для этого, в том числе:

  1. mapReduce (чистый R)
  2. RHIPE (который использует Hadoop ); см. пример 6.2.2 в документации для примера разделения файлов

В качестве альтернативы R предоставляет несколько пакетов для работы с большими данными, которые выходят за пределы памяти (на диск). Вероятно, вы могли бы загрузить весь набор данных в bigmemoryобъект и полностью выполнить сокращение в R. См. Http://www.bigmemory.org/ для набора инструментов, чтобы справиться с этим.


Хорошее предложение, но у меня нет большого опыта работы с MapReduce и ему подобными. Я должен это прочитать.
FTWynn

bigmemoryВ этом случае вам может быть проще попробовать сначала.
Шейн

10

Есть ли аналогичный способ чтения файлов по частям в R?

Да. Функция readChar () будет читать блок символов, не предполагая, что они оканчиваются нулем. Если вы хотите читать данные построчно, вы можете использовать readLines () . Если вы читаете блок или строку, выполняете операцию, а затем записываете данные, вы можете избежать проблемы с памятью. Хотя, если вы хотите запустить большой экземпляр памяти на Amazon EC2, вы можете получить до 64 ГБ ОЗУ. Это должно содержать ваш файл и много места для манипулирования данными.

Если вам нужно больше скорости, то рекомендация Шейна использовать Map Reduce очень хорошая. Однако, если вы выберете путь использования большого экземпляра памяти на EC2, вам следует взглянуть на многоядерный пакет для использования всех ядер на машине.

Если вы обнаружите, что хотите прочитать много гигабайт данных с разделителями в R, вам следует хотя бы изучить пакет sqldf, который позволяет импортировать непосредственно в sqldf из R, а затем работать с данными из R. Я обнаружил, что sqldf является одним из них. из самых быстрых способов импорта гигабайт данных в R, как упоминалось в предыдущем вопросе .


Я буду иметь в виду экземпляр EC2, но на данный момент я должен придерживаться своего рабочего стола, а это 2 ГБ ОЗУ. sqldf определенно похоже на то, что я имел в виду. Однако он также подавляется неверно сформированными строками (должно быть 17 столбцов, но в нескольких тысячах строк есть только один). Это требует какого-то другого метода предварительной обработки или есть вариант, который мне не хватает?
FTWynn

6

Есть совершенно новый пакет под названием colbycol, который позволяет вам читать только те переменные, которые вам нужны, из огромных текстовых файлов:

http://colbycol.r-forge.r-project.org/

Он передает любые аргументы в read.table, поэтому комбинация должна позволить вам довольно точно определить подмножество.


6

ffПакет представляет собой прозрачный способ иметь дело с большими файлами.

Вы можете увидеть сайт пакета и / или презентацию о нем.

надеюсь, это поможет


5

Вы можете импортировать данные в базу данных SQLite, а затем использовать RSQLite для выбора подмножеств.


Хороший план, но поскольку это, по сути, то, что sqldf делает за кулисами, я бы предпочел это. Разве нет лучшего способа обработки искаженных строк, если вы используете прямой RSQLite?
FTWynn

5

А как насчет использования readrи read_*_chunkedсемьи?

Итак, в вашем случае:

testfile.csv

County; State; Year; Quarter; Segment; Sub-Segment; Sub-Sub-Segment; GDP
Ada County;NC;2009;4;FIRE;Financial;Banks;80.1
Ada County;NC;2010;1;FIRE;Financial;Banks;82.5
lol
Ada County;NC;2013;1;FIRE;Financial;Banks;82.5

Актуальный код

require(readr)
f <- function(x, pos) subset(x, Year %in% c(2009, 2010))
read_csv2_chunked("testfile.csv", DataFrameCallback$new(f), chunk_size = 1)

Это относится fк каждому блоку, запоминая имена столбцов и комбинируя отфильтрованные результаты в конце. Посмотрите, ?callbackчто является источником этого примера.

Это приводит к:

# A tibble: 2 × 8
      County State  Year Quarter Segment `Sub-Segment` `Sub-Sub-Segment`   GDP
*      <chr> <chr> <int>   <int>   <chr>         <chr>             <chr> <dbl>
1 Ada County    NC  2009       4    FIRE     Financial             Banks   801
2 Ada County    NC  2010       1    FIRE     Financial             Banks   825

Вы даже можете увеличить, chunk_sizeно в этом примере всего 4 строки.



3

Возможно, вы сможете перейти на MySQL или PostgreSQL, чтобы избежать ограничений MS Access.

Подключить R к этим системам довольно просто с помощью коннектора базы данных на основе DBI (доступного в CRAN).


Туше для использования лучших инструментов базы данных, но поскольку это потребует административных хлопот (нужно любить эти административные правила в крупных компаниях), я стараюсь придерживаться того, что у меня есть. Кроме того, я стремлюсь к как можно меньшему количеству преобразований между получаемым текстовым файлом.
FTWynn

3

scan () имеет как аргумент nlines, так и аргумент пропуска. Есть ли какая-то причина, по которой вы можете просто использовать это, чтобы читать фрагмент строк за раз, проверяя дату, чтобы увидеть, подходит ли она? Если входной файл упорядочен по дате, вы можете сохранить индекс, который сообщает вам, какими должны быть ваши пропуски и строки, что ускорит процесс в будущем.


Я проверю, но в файле нет ничего полезного, например даты. Провайдеры, кажется, думают, что более важно отсортировать по региону, в котором находится данное графство. / Вздох ...
FTWynn

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

1

В наши дни 3,5 ГБ просто не так уж много, я могу получить доступ к машине с 244 ГБ ОЗУ (r3,8xlarge) в облаке Amazon за 2,80 доллара в час. Сколько часов у вас уйдет на то, чтобы понять, как решить проблему с помощью решений типа больших данных? Сколько стоит ваше время? Да, вам понадобится час или два, чтобы понять, как использовать AWS, но вы можете изучить основы на бесплатном уровне, загрузить данные и прочитать первые 10 тысяч строк в R, чтобы проверить, что он работает, а затем вы можете запустить большой экземпляр памяти, такой как r3.8xlarge, и прочтите все это! Просто мой 2с.


0

Теперь, в 2017 году, я бы посоветовал перейти на Spark и SparkR.

  • синтаксис может быть написан простым, довольно похожим на dplyr способом

  • вполне подходит для небольшой памяти (небольшой в смысле 2017 года)

Однако начало работы может быть пугающим ...


-3

Я бы выбрал БД, а затем сделал несколько запросов для извлечения необходимых вам образцов через DBI.

Избегайте импорта CSV-файла размером 3,5 ГБ в SQLite. Или, по крайней мере, дважды проверьте, что ваш ОГРОМНЫЙ db соответствует ограничениям SQLite, http://www.sqlite.org/limits.html

У вас чертовски большая БД. Я бы выбрал MySQL, если вам нужна скорость. Но будьте готовы ждать много часов, пока завершится импорт. Если у вас нет нестандартного оборудования или вы пишете из будущего ...

Amazon EC2 может быть хорошим решением также для создания экземпляра сервера с R и MySQL.

мои две скромные копейки стоят.


18
Каков размер 3,5 ГБ для sqlite? Пока вы используете подходящую файловую систему, проблем быть не должно (я регулярно использую базы данных sqlite> 30 ГБ для однопользовательских приложений)
Аарон Стэтхэм,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.