Обрезка огромного (3,5 ГБ) CSV-файла для чтения в R
Итак, у меня есть файл данных (разделенный точкой с запятой), который содержит много деталей и неполных строк (что приводит к засорению Access и SQL). Это набор данных на уровне округов, разбитый на сегменты, подсегменты и подсегменты (всего ~200 факторов) в течение 40 лет. Короче говоря, он огромен и не уместится в памяти, если я попытаюсь просто прочитать его.
Итак, мой вопрос заключается в следующем: учитывая, что я хочу, чтобы все округа, но только один год (и только самый высокий уровень сегмента... приводили к концу около 100000 строк), каков был бы наилучший способ получить это накопить в R?
В настоящее время я пытаюсь отказаться от ненужных лет работы с Python, чтобы обойти ограничение размера файла, читая и работая по одной строке за раз, но я бы предпочел решение только для R (пакеты CRAN в порядке). Есть ли подобный способ прочитать в файлах кусок за раз в R?
Любые идеи очень приветствуются.
Обновить:
- Ограничения
- Нужно использовать мою машину, поэтому нет экземпляров EC2
- Как R-только, насколько это возможно. Скорость и ресурсы не имеют значения в этом случае... при условии, что моя машина не взорвется...
- Как вы можете видеть ниже, данные содержат смешанные типы, с которыми мне нужно работать позже
- Данные
- Данные имеют размер 3,5 ГБ, около 8,5 миллионов строк и 17 столбцов.
- Пару тысяч строк (~2k) искажены, только один столбец вместо 17
- Это совершенно неважно и может быть отброшено
- Мне нужно только ~100000 строк из этого файла (см. Ниже)
Пример данных:
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. Короче говоря, на этот раз это не отвечало всем требованиям.
13 ответов
Моя попытка с 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)
Есть ли подобный способ прочитать в файлах кусок за раз в R?
Да. Функция readChar() будет читать блок символов, не предполагая, что они заканчиваются нулем. Если вы хотите читать данные в строке за раз, вы можете использовать readLines (). Если вы читаете блок или строку, делаете операцию, а затем записываете данные, вы можете избежать проблемы с памятью. Хотя, если вы хотите запустить большой экземпляр памяти на Amazon EC2, вы можете получить до 64 ГБ ОЗУ. Это должно держать ваш файл плюс много места для манипулирования данными.
Если вам нужна большая скорость, то рекомендация Шейна использовать Map Reduce очень хорошая. Однако, если вы идете по пути использования большого экземпляра памяти в EC2, вы должны взглянуть на многоядерный пакет для использования всех ядер на машине.
Если вам захочется прочитать много гигабайт данных с разделителями в R, вам следует, по крайней мере, изучить пакет sqldf, который позволяет вам импортировать напрямую в sqldf из R, а затем работать с данными из R. Я нашел, что sqldf один из самых быстрых способов импортировать концерты данных в R, как упоминалось в предыдущем вопросе.
Я не эксперт в этом, но вы можете попробовать MapReduce, что в основном означает использование подхода "разделяй и властвуй". R имеет несколько вариантов для этого, в том числе:
- mapReduce (чистый R)
- RHIPE (который использует Hadoop); см. пример 6.2.2 в документации для примера поднабора файлов
Кроме того, R предоставляет несколько пакетов для работы с большими данными, которые выходят за пределы памяти (на диск). Вы могли бы загрузить весь набор данных в bigmemory
возьмите объект и сделайте сокращение полностью в R. Смотрите http://www.bigmemory.org/ для набора инструментов, чтобы справиться с этим.
Существует совершенно новый пакет под названием colbycol, который позволяет вам читать только те переменные, которые вы хотите, из огромных текстовых файлов:
http://colbycol.r-forge.r-project.org/
Он передает любые аргументы в read.table, поэтому комбинация должна позволять вам довольно тесно использовать подмножество.
ff
Пакет - это прозрачный способ работы с огромными файлами.
Вы можете увидеть сайт пакета и / или презентацию о нем.
надеюсь, это поможет
Как насчет использования 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
к каждому куску, запоминая имена col и объединяя отфильтрованные результаты в конце. Увидеть ?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 строки.
Вы можете импортировать данные в базу данных SQLite, а затем использовать RSQLite для выбора подмножеств.
Возможно, вы можете перейти на MySQL или PostgreSQL, чтобы уберечь себя от ограничений MS Access.
К этим системам довольно легко подключить R с помощью коннектора базы данных на основе DBI (доступно на CRAN).
scan() имеет аргумент nlines и аргумент пропуска. Есть ли какая-то причина, по которой вы можете просто использовать это, чтобы читать по частям время, проверяя дату, чтобы увидеть, подходит ли она? Если входной файл упорядочен по дате, вы можете сохранить индекс, который скажет вам, каким должен быть ваш пропуск и nlines, чтобы ускорить процесс в будущем.
В наши дни 3,5 ГБ не так уж и велики, я могу получить доступ к машине с 244 ГБ ОЗУ (r3,8xlarge) в облаке Amazon за 2,80 долл. / Час. Сколько часов вам понадобится, чтобы понять, как решить проблему, используя решения типа больших данных? Сколько стоит ваше время? Да, вам понадобится час или два, чтобы понять, как использовать AWS, но вы можете изучить основы бесплатного уровня, загрузить данные и прочитать первые 10k строк в R, чтобы проверить, как это работает, а затем вы можете запустить большой экземпляр памяти типа r3.8xlarge и читать все это в! Просто мой 2с.
Теперь, 2017, я бы предложил перейти на spark и sparkR.
синтаксис может быть написан простым, довольно похожим на dplyr способом
это хорошо вписывается в маленькую память (маленький в смысле 2017 года)
Тем не менее, это может быть пугающим опытом, чтобы начать...
Я бы пошел на БД, а затем сделать несколько запросов, чтобы извлечь необходимые образцы через DBI
Пожалуйста, избегайте импорта CSV-файла размером 3,5 ГБ в SQLite. Или, по крайней мере, дважды проверьте, что ваша огромная база данных соответствует ограничениям SQLite, http://www.sqlite.org/limits.html
Это чертовски большая БД у вас есть. Я бы пошел на MySQL, если вам нужна скорость. Но будьте готовы подождать много часов до завершения импорта. Если у вас нет нестандартного оборудования или вы пишете из будущего...
EC2 от Amazon может быть хорошим решением и для создания экземпляра сервера под управлением R и MySQL.
мои две скромные копейки стоит.