Лучший способ быстро работать с большими объемами данных CSV - PullRequest
11 голосов
/ 05 апреля 2011

У меня есть большие наборы данных CSV (10M + строк), которые необходимо обработать. У меня есть два других файла, на которые нужно ссылаться для вывода - они содержат данные, которые усиливают то, что мы знаем о миллионах строк в файле CSV. Цель состоит в том, чтобы вывести новый файл CSV, в котором каждая запись объединена с дополнительной информацией из других файлов.

Представьте себе, что в большом файле CSV есть транзакции, но информация о клиенте и платежная информация записаны в двух других файлах, и мы хотим вывести новый CSV, в котором каждая транзакция связана с идентификатором клиента и идентификатором счета и т. Д.

У коллеги есть функциональная программа, написанная на Java для этого, но она очень медленная. Причина в том, что CSV-файл с миллионами строк, по-видимому, должен быть пройден много-много-много раз.

Мой вопрос - да, я к нему подхожу - как мне подойти к этому в Ruby? Цель состоит в том, чтобы он был быстрее (18+ часов прямо сейчас с очень небольшой нагрузкой на процессор)

Могу ли я загрузить столько записей в память? Если да, то как мне это сделать?

Я знаю, это немного расплывчато. Просто ищу идеи, потому что это немного ново для меня.

Ответы [ 6 ]

30 голосов
/ 29 октября 2011

Вот некоторый код ruby, который я написал для обработки больших csv-файлов (в моем случае ~ 180 МБ).

https://gist.github.com/1323865

Стандартный FasterCSV.parse, тянущий все это в память, занималбольше часа.Это заняло около 10 минут.

Соответствующая часть такова:

lines = []
IO.foreach('/tmp/zendesk_tickets.csv') do |line|
  lines << line
  if lines.size >= 1000
    lines = FasterCSV.parse(lines.join) rescue next
    store lines
    lines = []
  end
end
store lines

IO.foreach не загружает весь файл в память и просто просматривает его с помощью буфера,Когда он достигает 1000 строк, он пытается проанализировать CSV и вставить только эти строки.Одна сложная часть - это «спасение следующего».Если ваш CSV имеет несколько полей, которые занимают несколько строк, вам может понадобиться взять еще несколько строк, чтобы получить правильную разбираемую строку csv.В противном случае строка, на которой вы находитесь, может быть посередине поля.

В сущности вы можете увидеть еще одну приятную оптимизацию, которая использует обновление MySQL ON DUPLICATE KEY.Это позволяет вам выполнять массовую вставку, и, если обнаружен дубликат ключа, он просто перезаписывает значения в этой строке вместо вставки новой строки.Вы можете думать об этом как о создании / обновлении в одном запросе.Чтобы это работало, вам нужно установить уникальный индекс хотя бы для одного столбца.

2 голосов
/ 05 апреля 2011

Два достаточно быстрых варианта:

  1. Поместите ваши данные в базу данных sqlite. Тогда это простой запрос с парой join, который будет работать намного быстрее, чем все, что вы могли бы написать самостоятельно - SQL очень хорош для таких задач.

  2. Предполагая, что ваши дополнительные CSV-файлы достаточно малы, чтобы поместиться в ОЗУ, вы можете прочитать все в хеш, используя идентификатор клиента в качестве ключа, а затем посмотреть этот хеш при обработке основного файла с 10 + M записями. Обратите внимание, что данные о поиске необходимо помещать только в ОЗУ, основной список можно обрабатывать небольшими ветвями.

2 голосов
/ 05 апреля 2011

10M + строк на самом деле не похоже на , что много. Если вы можете предварительно загрузить содержимое файлов и сопоставить данные в памяти с приличными структурами данных (вам понадобятся карты в какой-то момент), вам не нужно будет снова и снова просматривать файлы CSV. Доступ к файлу SLOW .

2 голосов
/ 05 апреля 2011

как насчет использования базы данных.

сжимает записи в таблицы, а затем запрашивает их с помощью объединений.

импорт может занять некоторое время, но механизм БД будет оптимизирован дляобъединение и поиск части ...

1 голос
/ 05 апреля 2011

Мой опыт показывает, что с Ruby подготовьтесь к тому, чтобы использовать в 10 раз больше полезной нагрузки.Конечно, при текущих объемах ОЗУ, если процесс загружает только один файл за раз, 10 МБ практически ничтожно, даже если умножить на десять:)

Если вы можете читать по одной строке за раз (что легкос экземплярами файлов), вы также можете использовать FasterCSV и писать по одной строке за раз.Это сделало бы потребление памяти O(1) вместо O(n).Но с 10-мегабайтными файлами вы, вероятно, можете сохранить этот файл в памяти и записать его в CSV за один проход, учитывая только несколько процессов в любой момент времени.

0 голосов
/ 05 апреля 2011

Если у вас написана Java-программа, убедитесь, что вы используете библиотеки NIO.Они намного быстрее, чем по умолчанию.Ранее я обрабатывал текстовые файлы с 500 000 строк, используя библиотеки NIO.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...