Dividindo arquivo para cada 10000 números (não linhas)

8

Eu tenho um arquivo que se parece com o seguinte:

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    

Eu quero dividir esse arquivo para cada intervalo de 10000 do 2º campo (NÃO linhas, mas intervalo de números). Então, para este arquivo eu gostaria de dividir a partir da primeira linha (a linha com 61336212) para a linha que tem ou até 61346211 (61336212 + 9999), depois de 61346212 para 61356211, e assim por diante. Como você pode ver, os números no 2º campo / coluna não estão preenchidos.

Existe uma maneira de fazer isso?

    
por agathusia 17.08.2015 / 17:54

4 respostas

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

Escrevia para file.0000 , file.0001 ... (o número sendo int(($2-n)/10000) , em que n é $2 para a primeira linha).

Note que fechamos os arquivos depois que paramos de escrever para eles, caso contrário, você alcançaria o limite do número de arquivos simultaneamente abertos após algumas centenas de arquivos (o GNU awk pode contornar esse limite, mas as performances degradam rapidamente).

Estamos assumindo que esses números estão sempre aumentando.

    
por 17.08.2015 / 18:29
8

Corte a versão de uma linha. Talvez mais adequado para Code Golf do que este fórum. Isso gera split1, split2, split3 e assim por diante, como nomes de arquivos.

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

Para ter arquivos de saída chamados split001, split002, split003, envolve este sprintf :

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

Para evitar o problema de desaceleração do Gawk identificado por @ Stéphane Chazelas, use perl:

perl -ne '(undef,$a)=split(/\s+/,$_);if($a>$b+9999){$c++;$b=$a}open(D,sprintf(">>ysplit%03d",$c));print D' <file.txt
    
por 17.08.2015 / 18:35
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

Teste com intervalo definido para 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  

Nota: produzirá arquivos vazios para intervalos vazios; para remover arquivos vazios, adicione:

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

Será executado o arquivo para cada etapa no loop for , portanto, não é o mais eficiente.

    
por 17.08.2015 / 18:28
3

Se você quer dizer apenas cálculo e não contagem de linhas:

awk 'NR==1 || n+10000<$2{n=$2; portion++}{print > FILENAME "." portion}' file
    
por 17.08.2015 / 18:55