Эффективный способ разбить большой файл на более мелкие на основе значения столбца - PullRequest
0 голосов
/ 28 ноября 2018

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

У меня есть код, чтобы сделать это, но для файла в 1 гигабайт это занимает около 15-20 минут.Есть ли эффективный способ сделать это?

Это то, что у меня сейчас есть:

awk -F, '{if(NR > 1) {print >> $2"_backfill_tmp.csv"; close($2"_backfill_tmp.csv")}}' $input_file
counter=0
for file in *"_backfill_tmp.csv"
do
  file_name=${input_file%.*}"_backfill2_part_"$PART_NUMBER".csv"
  cat "$file" >> "$file_name"
  rm "$file"
  (( counter++ ))
  if (( $counter % 50 == 0 )) ; then
    (( PART_NUMBER++ ))
  fi
done

Команда awk записывает каждую строку в отдельный файл на основе значения столбца 2 (игнорируяпервая строка, которая является заголовком), так что каждая из строк с одинаковым значением ID попадает в один и тот же файл.Я закрываю файлы каждый раз, потому что сталкиваюсь с Too many files open error и не могу установить ulimit на машине.Тем не менее, этот процесс занимает всего около 15 секунд, так что это не проблема.

Затем я перебираю все временно созданные файлы и записываю их в отдельный файл, пока $counter не достигнет 50 (т.е. объединение50 файлов вместе).Теперь это где это занимает много времени.Я предполагаю, что поскольку существует много отдельных файлов идентификаторов, их открытие по одному и их объединение занимает много времени.

Я новичок в awk, поэтому я почти уверен, что мой код не 'т эффективный.Могу ли я сделать весь процесс быстрее?

Ответы [ 2 ]

0 голосов
/ 29 ноября 2018

вы должны отсортировать файл для лучшей производительности

$ sort -t, -k2,2 file | awk '!($2 in a){c++; a[$1]; 
                                        if(c==50) {ix++; c=0}} 
                                       {print > "file_"(ix+1)}'

считает уникальные ключи и увеличивает счетчик файлов после 50;напечатайте каждую строку в индексированном файле.

вам может потребоваться закрыть файлы, если они все еще превышают пороговое значение вашей системы.

0 голосов
/ 28 ноября 2018

Скрипт для несортированного ввода

Вы можете использовать следующий скрипт.Я не использовал close, поскольку теперь число открытых файлов составляет только #uniqueIDs / 50 вместо # uniqueIDs.

awk -F, 'NR > 1 {
  if (!($2 in mapIdToPart)) {
    if (uniqueIds % 50 == 0) {
      maxPart++;
    }
    mapIdToPart[$2] = maxPart;
    uniqueIds++;
  }
  print >> "part"mapIdToPart[$2]".csv";
}' input.csv

Это создает файлы part#.csv, где # - номер текущегочасть.Входной файл не должен быть отсортирован.Строки с одинаковыми идентификаторами перейдут в одну и ту же часть.Порядок строк в каждой части соответствует порядку строк во входном файле.Каждая часть имеет 50 (или меньше, для последней части) уникальных идентификаторов.

Скрипт для отсортированного ввода

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

Порядок может быть алфавитным, числовым, ..., это не имеет значения.Здесь я предположил, что отсортированный файл больше не имеет заголовка.Если заголовок все еще существует, добавьте NR > 1 в начале сценария awk.

awk -F, '{
  if ($2 != lastId) {
    lastId = $2;
    if (uniqueIds % 50 == 0) {
      close("part"maxPart".csv");
      maxPart++;
    }
    uniqueIds++;
  }
  print >> "part"maxPart".csv";
}' sorted.csv

Benchmark

Чтобы протестировать сценарий, я сгенерировал примеры данных, используя

n=98""000""000; paste -d,
    <(shuf -i 10""000-99""000 -r -n "$n") \
    <(shuf -i 0-9""999 -r -n "$n") \
| cat <(echo data,id) - > input.csv

Образцы данных содержали два столбца и 98 миллионов строк с числами в них.Там где 10 тысяч уникальных идентификаторов.Измеренные времена, когда

  • 3m 54s для запуска первого сценария на несортированном вводе.
  • 1m 19s для сортировки ввода с использованием tail -n +2 input.csv | LC_ALL=C sort -t, -k2 > sorted.csv.Часть tail удаляет заголовок.
  • 1 м 48 с для запуска второго сценария на отсортированном вводе.
  • 3 м 07 с Для сортировки и запуска второго сценария.

Вывод: Даже если ваш ввод не отсортирован, может быть быстрее выполнить сортировку и запустить второй сценарий.

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