Divisione del file per ogni 10000 numeri (non righe)


8

Ho un file simile al seguente:

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    

Voglio dividere questo file per ogni intervallo di 10000 del secondo campo (NON righe, ma intervallo di numeri). Quindi per questo file vorrei dividere dalla prima riga (la riga con 61336212) alla riga che ha o fino a 61346211 (61336212 + 9999), quindi da 61346212 a 61356211 e così via. Come puoi vedere, i numeri nel 2 ° campo / colonna non sono "riempiti".

C'è un modo per fare questo?


Nel tuo esempio, se il numero successivo dopo 61346211 è 61346220, diciamo, ti aspetteresti che il secondo file di output copra l'intervallo a partire da 61346212 o 61346220?
Joe Lee-Moyet,

il secondo intervallo dovrebbe coprire da 61346212.
Agathusia

Risposte:


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
     }'

Scriverei a file.0000, file.0001... (il numero è int(($2-n)/10000)dove nè $2per la prima riga).

Nota che chiudiamo i file una volta che abbiamo smesso di scriverli come altrimenti, raggiungerai il limite sul numero di file aperti contemporaneamente dopo alcune centinaia di file (GNU awkpuò aggirare quel limite, ma poi le prestazioni peggiorano rapidamente).

Supponiamo che quei numeri aumentino sempre.


3
potresti spiegare cosa sta succedendo?
Fiximan,

Potresti spiegare cosa sta succedendo qui? Anche il commento qui sotto è lì per avere la lunghezza del nome del file di output per essere costante, come file.0000, file.0001 invece di file.1 file.2 .. file.100 .. file..2320?
Agathusia,

1
@Fiximan, non mi sento di poter spiegare molto di più senza parafrasare il codice. Quale parte trovi poco chiara?
Stéphane Chazelas,

Bene, capisco la generazione del nome file file = ..., ma come funziona l'iterazione? Non c'è parte che dice n = n + 10000né una lower_boundary <= $2 < upper_boundaryparte. In generale, tutto if (file != last_file) { close(last_file) ; last_file = file }è fuori dalla mia
portata

1
@Fixman, beh sì, è quello che definirei parafrasando if (file != last_file): se il file corrente non è lo stesso del file precedente, chiudi il file precedente (quindi apri un solo file alla volta (non è necessario tenerli tutti aperti come altre soluzioni))
Stéphane Chazelas,

7

Hack versione a una fodera. Forse forse più adatto a Code Golf di questo forum. Questo genera split1, split2, split3 e così via, come nomi di file.

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

Per avere file di output denominati split001, split002, split003, è necessario questo extra sprintf:

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

Per evitare il problema del rallentamento gawk identificato da @ Stéphane Chazelas, utilizzare 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
Per questo metodo, c'è un modo per avere i nomi dei file per essere più .. consecutivamente? Questo produce split1 .... split100 ... split1000, ma qualcosa di più nella linea di split00001 ... split 00100 .. split01000 ..?
Agathusia,

1
Certo, sprintfora è stata aggiunta magia extra .
steve

Si noti che se l'input ha 0, 9999, 12000, 19999, 21000, 22000, ciò mette 0, 9999 nel file1, ma 12000, 19999, 21000 nel file2 che sembra in contrasto con i requisiti.
Stéphane Chazelas,

1
Si noti che questo raggiungerebbe il limite sul numero di file aperti contemporaneamente dopo alcune centinaia di file (GNU awk può aggirare quel limite, ma le prestazioni peggioreranno rapidamente).
Stéphane Chazelas,

1
Si. Ho appena notato il problema che hai citato.
Agathusia,

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

Test con intervallo impostato su 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: produrrà file vuoti per intervalli vuoti; per rimuovere file vuoti, aggiungere:

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

Funzionerà su file per ogni passaggio del forciclo, quindi non è il più efficiente.


3

Se intendi solo il calcolo, non il conteggio delle righe:

awk 'NR==1 || n+10000<$2{n=$2; portion++}{print > FILENAME "." portion}' file

Si noti che se l'input ha 0, 9999, 12000, 19999, 21000, 22000, ciò mette 0, 9999 nel file1, ma 12000, 19999, 21000 nel file2 che sembra in contrasto con i requisiti.
Stéphane Chazelas,

Si noti che questo raggiungerebbe il limite sul numero di file aperti contemporaneamente dopo alcune centinaia di file (GNU awk può aggirare quel limite, ma le prestazioni peggioreranno rapidamente).
Stéphane Chazelas,

@ StéphaneChazelas Non sono sicuro che ti capisca chiaramente. Se vuoi 21000 in 3o file usa 9999 invece di 10000.
Costas

dalla mia comprensione della domanda, l'OP vuole linee da 0 a 9999 nel primo file, da 10000 a 19999 nel secondo file.
Stéphane Chazelas,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.