Fractionnement du fichier pour tous les 10000 numéros (pas les lignes)


8

J'ai un fichier qui ressemble à ceci:

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    

Je veux diviser ce fichier pour chaque intervalle de 10000 du 2e champ (PAS les lignes, mais l'intervalle de nombre). Donc, pour ce fichier, je voudrais séparer de la première ligne (la ligne avec 61336212) à la ligne qui a ou jusqu'à 61346211 (61336212 + 9999), puis de 61346212 à 61356211, et ainsi de suite et ainsi de suite. Comme vous pouvez le voir, les chiffres dans le 2ème champ / colonne ne sont pas «remplis».

Y a-t-il un moyen de faire cela?


Dans votre exemple, si le numéro suivant après 61346211 est 61346220, par exemple, vous attendriez-vous à ce que le deuxième fichier de sortie couvre la plage commençant à 61346212 ou 61346220?
Joe Lee-Moyet

la deuxième plage devrait couvrir à partir de 61346212.
agathusia

Réponses:


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

Écrirais à file.0000, file.0001... (le nombre étant int(($2-n)/10000)nest $2la première ligne).

Notez que nous fermons les fichiers une fois que nous avons cessé d'écrire dessus, sinon vous atteindriez la limite du nombre de fichiers ouverts simultanément après quelques centaines de fichiers (GNU awkpeut contourner cette limite, mais les performances se dégradent rapidement).

Nous supposons que ces chiffres augmentent toujours.


3
pourriez-vous expliquer ce qui se passe?
Fiximan

Pourriez-vous expliquer ce qui se passe ici? De même, comme le commentaire ci-dessous, la longueur du nom du fichier de sortie doit être constante, comme file.0000, file.0001 au lieu de file.1 file.2 .. file.100 .. file..2320?
agathusie

1
@Fiximan, je ne pense pas pouvoir expliquer beaucoup plus sans paraphraser le code. Quelle partie trouvez-vous peu claire?
Stéphane Chazelas

Eh bien, je comprends la génération du nom de fichier file = ..., mais comment fonctionne l'itération? Il n'y a ni partie qui dit n = n + 10000ni lower_boundary <= $2 < upper_boundarypartie. En général le tout if (file != last_file) { close(last_file) ; last_file = file }est hors de ma ligue
Fiximan

1
@Fixman, eh bien oui, c'est ce que j'appellerais la paraphrase if (file != last_file): si le fichier actuel n'est pas le même que le fichier précédent, fermez le fichier précédent (alors n'ouvrez qu'un seul fichier à la fois (nous n'avons pas besoin de les garder) tous ouverts comme le font d'autres solutions))
Stéphane Chazelas

7

Version hack one-liner. Peut-être plus approprié pour Code Golf que ce forum. Cela génère split1, split2, split3 et ainsi de suite, en tant que noms de fichiers.

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

Pour avoir des fichiers de sortie nommés split001, split002, split003, cela implique ceci sprintf:

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

Pour éviter le problème de ralentissement gawk identifié par @ Stéphane Chazelas, utilisez 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
Pour cette méthode, existe-t-il un moyen de faire en sorte que les noms de fichiers soient plus .. consécutifs? Cela produit split1 .... split100 ... split1000, mais quelque chose de plus dans la ligne de split00001 ... split 00100 .. split01000 ..?
agathusia

1
Bien sûr, de la sprintfmagie supplémentaire est désormais ajoutée.
steve

Notez que si l'entrée a 0, 9999, 12000, 19999, 21000, 22000, cela met 0, 9999 dans le fichier 1, mais 12000, 19999, 21000 dans le fichier 2, ce qui semble contraire aux exigences.
Stéphane Chazelas

1
Notez que cela atteindrait la limite du nombre de fichiers ouverts simultanément après quelques centaines de fichiers (GNU awk peut contourner cette limite, mais les performances se dégradent rapidement).
Stéphane Chazelas

1
Ouais. Je viens de remarquer le problème que vous avez mentionné.
agathusie

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 avec intervalle défini sur 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  

Remarque: produira des fichiers vides pour des intervalles vides; pour supprimer les fichiers vides, ajoutez:

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

Exécute le fichier pour chaque étape de la forboucle, donc pas le plus efficace.


3

Si vous voulez dire juste un calcul et non un comptage de lignes:

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

Notez que si l'entrée a 0, 9999, 12000, 19999, 21000, 22000, cela met 0, 9999 dans le fichier 1, mais 12000, 19999, 21000 dans le fichier 2, ce qui semble contraire aux exigences.
Stéphane Chazelas

Notez que cela atteindrait la limite du nombre de fichiers ouverts simultanément après quelques centaines de fichiers (GNU awk peut contourner cette limite, mais les performances se dégradent rapidement).
Stéphane Chazelas

@ StéphaneChazelas Je ne suis pas sûr que vous compreniez bien. Si vous voulez 21000 dans le 3ème fichier, utilisez 9999 au lieu de 10000.
Costas

d'après ma compréhension de la question, l'OP veut des lignes avec 0 à 9999 dans le premier fichier, 10000 à 19999 dans le deuxième fichier.
Stéphane Chazelas
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.