Comment extraire plusieurs bits d'informations qui apparaissent sur différentes lignes dans le même fichier texte

8

J'essaie d'extraire l'ID de séquence et le numéro de cluster qui se produisent sur différentes lignes dans le même fichier texte.

L'entrée ressemble à

>Cluster 72
0   319aa, >O311_01007... *
>Cluster 73
0   318aa, >1494_00753... *
1   318aa, >1621_00002... at 99.69%
2   318aa, >1622_00575... at 99.37%
3   318aa, >1633_00422... at 99.37%
4   318aa, >O136_00307... at 99.69%
>Cluster 74
0   318aa, >O139_01028... *
1   318aa, >O142_00961... at 99.69%
>Cluster 75
0   318aa, >O300_00856... *

La sortie souhaitée est l'ID de séquence dans une colonne et le numéro de cluster correspondant dans la seconde.

>O311_01007  72
>1494_00753  73
>1621_00002  73
>1622_00575  73
>1633_00422  73
>O136_00307  73
>O139_01028  74
>O142_00961  74
>O300_00856  75

Quelqu'un peut-il m'aider?

Tim
la source
L'ID de séquence sera-t-il toujours le champ 3D séparé par des espaces sur les lignes qui ne commencent pas >? En outre, vous pourriez être intéressé par notre site partenaire , Bioinformatics .
terdon

Réponses:

13

Avec awk:

awk -F '[. ]*' 'NF == 2 {id = $2; next} {print $3, id}' input-file
  • nous divisons les champs sur des espaces ou des périodes avec -F '[. ]*'
  • avec des lignes de deux champs (les >Clusterlignes), enregistrez le deuxième champ comme ID et passez à la ligne suivante
  • avec d'autres lignes, imprimez le troisième champ et l'ID enregistré
muru
la source
Plutôt que de masquer le nombre de champs, il peut être préférable de rechercher explicitement au $1 == ">Cluster"lieu de NF == 2, en fonction de ce qui pourrait se trouver dans le fichier.
Monty Harder
5

Vous pouvez utiliser awkpour cela:

awk '/>Cluster/{
      c=$2;
      next
    }{
      print substr($3,2,length($3)-4), c
    }' file

La première instruction de bloc capture l'ID de cluster. La deuxième instruction de bloc (par défaut) extrait les données souhaitées et les imprime.

oliv
la source
Vous n'avez pas besoin de donner " "comme argument print. Utilisez simplement une virgule pour séparer les arguments et il utilisera l'OFS, espace par défaut, pour séparer les arguments.
muru
4

Voici une alternative avec Ruby comme doublure:

ruby -ne 'case $_; when /^>Cluster (\d+)/;id = $1;when /, (>\w{4}_\w{5})\.\.\./;puts "#{$1} #{id}";end' input_file

ou étalé sur plusieurs lignes:

ruby -ne 'case $_
when /^>Cluster (\d+)/
  id = $1
when /, (>\w{4}_\w{5})\.\.\./
  puts "#{$1} #{id}"
end' input_file

Je suppose que c'est seulement plus lisible que la awkversion si vous connaissez Ruby et regexen. En prime, ce code pourrait être un peu plus robuste que le simple fractionnement des lignes, car il recherche le texte environnant.

Eric Duminil
la source
1

Perl:

$ perl -ne 'if(/^>.*?(\d+)/){$n=$1;}else{ s/.*(>[^.]+).*/$1 $n/; print}' file 
>O311_01007 72
>1494_00753 73
>1621_00002 73
>1622_00575 73
>1633_00422 73
>O136_00307 73
>O139_01028 74
>O142_00961 74
>O300_00856 75

Explication

  • perl -ne: lire le fichier d'entrée ligne par ligne ( -n) et appliquer le script donné par -eà chaque ligne.
  • if(/^>.*?(\d+)/){$n=$1;}: si cette ligne commence par un >, recherchez la plus longue séquence de nombres à la fin de la ligne et enregistrez-la sous $n.
  • else{ s/.*(>[^.]+).*/$1 $n/; print: si la ligne ne commence pas par >, remplacez tout par le plus long tronçon de non- .caractères suivant un >( >[^.]+), c'est-à-dire le nom de la séquence ( $1car nous avons capturé la correspondance d'expression régulière) et la valeur actuelle de $n.

Ou, pour une approche plus maladroite:

$ perl -lane 'if($#F==1){$n=$F[1]}else{$F[2]=~s/\.+$//; print "$F[2] $n"}' file 
>O311_01007 72
>1494_00753 73
>1621_00002 73
>1622_00575 73
>1633_00422 73
>O136_00307 73
>O139_01028 74
>O142_00961 74
>O300_00856 75

C'est juste une façon un peu plus lourde de faire la même idée de base que les différentes awkapproches. Je l'inclus pour la fin et pour les fans de Perl. Si vous avez besoin d'une explication, utilisez simplement les solutions awk :).

terdon
la source