Разбиение файла на каждые 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-м поле / столбце не «заполнены».

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


В вашем примере, если следующий номер после 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цикле, поэтому не самый эффективный.


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 во втором файле.
— Стефан Шазелас
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.