эффективное объединение> 100 файлов - PullRequest
0 голосов
/ 03 января 2019

У меня есть список, содержащий> 100 файлов с разделителями табуляции, содержащий 5-8 миллионов строк и 16 столбцов (всегда в одном и том же точном порядке).Из каждого файла мне нужно извлечь 5 конкретных столбцов, в том числе один идентификатор-столбец.Мой окончательный вывод (с использованием 3 входных файлов в качестве примера) должен состоять из 4 файлов, содержащих следующие столбцы:

  • output1: ID, VAR1
  • output2: VAR2.1, VAR2.2, VAR2.3
  • выход3: VAR3.1, VAR3.2, VAR3.3
  • выход4: VAR4.1, VAR4.2, VAR4.3

, где ".1", ".2" и ".3" указывают, что столбец происходит из первого, второго и третьего входных файлов соответственно.

Моя проблема заключается в том, что входные файлы содержат частичноперекрывающиеся идентификаторы, и мне нужно извлечь объединение этих строк (т.е. все идентификаторы, которые встречаются хотя бы один раз в одном из входных файлов).Точнее, output1 должен содержать объединения столбцов «ID» и «VAR1» всех входных файлов.Порядок строк в остальных выходных файлах должен совпадать с выходным1.Наконец, строки, отсутствующие в каком-либо данном входном файле, должны дополняться «NA» в output2, output3 и output4.

Я использую комбинацию цикла while, awk и join, чтобы выполнить работу, но это занимает довольно много времени.Я хотел бы знать, есть ли более быстрый способ сделать это, потому что мне приходится снова и снова запускать один и тот же сценарий с различными входными файлами.

Пока мой сценарий:

ID=1
VAR1=6
VAR2=9
VAR3=12
VAR4=16
while read FILE;do
    sort -k${ID},${ID} < ${FILE} | awk -v ID=${ID} -v VAR1=${VAR1} -v VAR2=${VAR2} -v VAR3=${VAR3} -v VAR4=${VAR4} 'BEGIN{OFS="\t"};{print $ID,$VAR1 > "tmp1";print ${ID},$VAR2 > "tmp2";print ${ID},$VAR3 > "tmp3";print ${ID},$VAR4 > "tmp4"}'
    awk 'FNR==NR{a[$1]=$1;next};{if(($1 in a)==0){print $0 > "tmp5"}}' output1 tmp1
    cat output1 tmp5 > foo && mv foo output1
    join -e "NA" -a1 -a2 -t $'\t' -1 1 -2 1 output2 -o auto tmp2 > bar2 && mv bar2 output2
    join -e "NA" -a1 -a2 -t $'\t' -1 1 -2 1 output3 -o auto tmp3 > bar3 && mv bar2 output3
    join -e "NA" -a1 -a2 -t $'\t' -1 1 -2 1 output4 -o auto tmp4 > bar4 && mv bar2 output4
    rm tmp?
done < files.list
sort -k1,1 output1 > foo && mv foo output1

Последнее замечание: я использую cat для output1, потому что все значения в VAR1 для одного и того же идентификатора одинаковы для всех входных файлов (я убедился в этом, когда я предварительно обработал свои файлы).Поэтому я могу просто добавить строки, которые еще не включены в конец output1, и отсортировать окончательный выходной файл

1 Ответ

0 голосов
/ 03 января 2019

Сначала вы должны выяснить, где теряется большая часть времени. Вы можете 'повторить "бегущий X"; время. / X` и убедитесь, что вы не пытаетесь оптимизировать самую быструю часть сценария.

Вы можете просто запустить три соединения в фоновом режиме параллельно (cmd args ) &, а затем wait, чтобы все они завершили. Если это занимает 1 секунду, а часть awk до этого занимает 10 минут, это не сильно поможет.

Вы также можете поставить wait перед cat output 1 tmp5... и перед последней sort -k1... строкой. Чтобы это работало, вам нужно будет по-разному называть временные файлы и переименовывать их непосредственно перед join s. Идея состоит в том, чтобы сгенерировать вход для трех параллельных объединений для первого файла в фоновом режиме, wait, затем переименовать файлы, запустить join s в фоновом режиме и сгенерировать следующие входные данные. После завершения цикла просто дождитесь окончания последних join с. Это поможет, если часть awk потребляет сравнимое с процессором время join s.

HTH, вы можете создавать еще более сложные сценарии параллельного выполнения.

...