Разбиение файла на каждые 10000 номеров (не строк)

8

У меня есть файл, который выглядит следующим образом:

chr19   61336212        +       0       0       CG      CGT    
chr19   61336213        -       0       0       CG      CGG    
chr19   61336218        +       0       0       CG      CGG    
chr19   61336219        -       0       0       CG      CGC    
chr19   61336268        +       0       0       CG      CGG    
chr19   61336269        -       0       0       CG      CGA    
chr19   61336402        +       0       0       CG      CGG    
chr19   61336403        -       0       0       CG      CGT    

Я хочу разделить этот файл на каждые 10000 интервалов 2-го поля (НЕ строки, а интервал номера). Таким образом, для этого файла я хотел бы разбить первую строку (строку с 61336212) на строку, которая имеет или до 61346211 (61336212 + 9999), затем с 61346212 до 61356211, и так далее, и так далее. Как видите, цифры во 2-м поле / столбце не «заполнены».

Есть ли способ сделать это?

agathusia
источник
В вашем примере, если следующий номер после 61346211 будет 61346220, скажем, вы ожидаете, что второй файл вывода будет охватывать диапазон, начинающийся с 61346212 или 61346220?
Джо Ли-Мойет
второй диапазон должен охватывать 61346212.
Агатусия

Ответы:

13
awk 'NR==1 {n=$2}
     {
       file = sprintf("file.%.4d", ($2-n)/10000)
       if (file != last_file) {
         close(last_file)
         last_file = file
       }
       print > file
     }'

Написали бы , чтобы file.0000, file.0001... (число того , int(($2-n)/10000)где nнаходится $2на первой линии).

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

Мы предполагаем, что эти цифры всегда растут.

Стефан Шазелас
источник
3
не могли бы вы объяснить, что происходит?
Fiximan
Не могли бы вы объяснить, что здесь происходит? Также, как в приведенном ниже комментарии, есть ли длина выходного имени файла, чтобы быть постоянной, такой как file.0000, file.0001 вместо file.1 file.2 .. file.100 .. file..2320?
Агатусия
1
@Fiximan, я не чувствую, что могу объяснить намного больше, не перефразируя код. Какую часть вы находите неясным?
Стефан Шазелас
Хорошо, я понимаю генерацию имени файла file = ..., но как работает итерация? Там нет части, которая говорит, n = n + 10000ни lower_boundary <= $2 < upper_boundaryчасти. В целом все if (file != last_file) { close(last_file) ; last_file = file }вышло из моей лиги
Fiximan
1
@Fixman, ну да, это то, что я бы назвал перефразируя if (file != last_file): если текущий файл не совпадает с предыдущим файлом, закройте предыдущий файл (поэтому открывайте только один файл за раз (нам не нужно их хранить) все открыто, как и другие решения))
Стефан Шазелас
7

Взломать однострочную версию. Возможно, больше подходит для Code Golf, чем этот форум, хотя. Это генерирует split1, split2, split3 и так далее, в качестве имен файлов.

awk '{if($2>b+9999){a++;b=$2}print >"split" a}' file.txt

Чтобы иметь выходные файлы с именами split001, split002, split003, необходимо добавить следующее sprintf:

awk '{if($2>b+9999){a++;b=$2}print >sprintf("split%03d",a)}' file.txt

Чтобы избежать проблемы замедления работы gawk, выявленной @ Stéphane Chazelas, используйте perl:

perl -ne '(undef,$a)=split(/\s+/,$_);if($a>$b+9999){$c++;$b=$a}open(D,sprintf(">>ysplit%03d",$c));print D' <file.txt
Стив
источник
1
Для этого метода, есть ли способ, чтобы имена файлов были больше .. последовательно? Это выводит split1 .... split100 ... split1000, но что-то еще в строке split00001 ... split 00100 .. split01000 ..?
Агатусия
1
Конечно, добавлена ​​дополнительная sprintfмагия.
Стив
Обратите внимание, что если на входе есть 0, 9999, 12000, 19999, 21000, 22000, то в файл1 помещается 0,9999, а в файл2 - 12000, 19999, 21000, что не соответствует требованиям.
Стефан Шазелас
1
Обратите внимание, что это достигнет предела количества одновременно открытых файлов после нескольких сотен файлов (GNU awk может обойти это ограничение, но тогда производительность быстро ухудшится).
Стефан Шазелас
1
Да. Я только что заметил проблему, которую вы упомянули.
Агатусия
4
#!/bin/bash
first=$( head -n1 file | awk -F" +" '{print $2}' )
last=$( tail -n1 file | awk -F" +" '{print $2}' )
for (( i=$first ; i<=$last ; i=i+10000 )) ; do
   awk -v start=$i -v end=$(($i+10000)) 'BEGIN { FS == " +" } { if ( $2 >= start && $2 < end ) print $0 }' file \
   >> interval_"$i"_to_"$(( $i+10000 ))"
done

Тест с интервалом в 100:

more inter*
::::::::::::::
interval_61336212_to_61346212
::::::::::::::
chr19   61336212        +       0       0       CG      CGT    
chr19   61336213        -       0       0       CG      CGG    
chr19   61336218        +       0       0       CG      CGG    
chr19   61336219        -       0       0       CG      CGC    
chr19   61336268        +       0       0       CG      CGG    
chr19   61336269        -       0       0       CG      CGA    
::::::::::::::
interval_61336312_to_61346312
::::::::::::::
chr19   61336402        +       0       0       CG      CGG    
chr19   61336403        -       0       0       CG      CGT  

Примечание: создаст пустые файлы для пустых интервалов; для удаления пустых файлов добавьте:

for file in interval* ; do
  if [ ! -s "$file" ] ; then
    rm "$file"
  fi
done

Будет выполняться файл для каждого шага в forцикле, поэтому не самый эффективный.

Fiximan
источник
3

Если вы имеете в виду только расчет, а не подсчет строк:

awk 'NR==1 || n+10000<$2{n=$2; portion++}{print > FILENAME "." portion}' file
Костас
источник
Обратите внимание, что если на входе есть 0, 9999, 12000, 19999, 21000, 22000, то в файл1 помещается 0,9999, а в файл2 - 12000, 19999, 21000, что не соответствует требованиям.
Стефан Шазелас
Обратите внимание, что это достигнет предела количества одновременно открытых файлов после нескольких сотен файлов (GNU awk может обойти это ограничение, но тогда производительность быстро ухудшится).
Стефан Шазелас
@ StéphaneChazelas Я не уверен, что ясно понимаю вас. Если вы хотите 21000 в третьем файле, используйте 9999 вместо 10000.
Костас
исходя из моего понимания вопроса, OP хочет строки с 0 до 9999 в первом файле, от 10000 до 19999 во втором файле.
Стефан Шазелас