2
votes

Comment ajouter les données de la 1ère colonne de chaque ligne à la tête de chaque colonne marquée par une chaîne ou des caractères spécifiques dans la ligne correspondante?

J'ai un gros morceau de données (un fichier) comme suit, chaque ligne a un nombre différent de colonnes (délimitées par des tabulations), la structure de données comme ceci:

>NP_56789.2matchnumber_1
HGRR
>NP_56789.2matchnumber_2
KQRHH
>NP_56789.2matchnumber_3
RVRK
>NP_56789.2matchnumber_4
HTHH

Dans le fichier ci-dessus, la 1ère ligne a 2 colonnes, la 2ème ligne a 5 colonnes, et la 3ème ligne a m + 1 colonnes ...; Évidemment, chaque ligne du fichier a "> accessionID" et "matchnumber_i_XXX". Je veux ajouter la 1ère colonne de chaque ligne à la tête de chaque colonne marquée par "matchnumber" dans la ligne correspondante, et imprimer au format fasta, la sortie est comme ceci:

cat a.txt |awk -v OFS="\t" '{print $1$2,$1$3,$1$4,$1$5}' | sed 's/\t/\n/g' | sed 's/_/ /g' | sed 's/NP /NP_/g' | sed 's/matchnumber /matchnumber_/g' | sed 's/ /\n/g' > a.fasta


0 commentaires

6 Réponses :


1
votes

Perl à la rescousse!

perl -lne  
# -l to remove newline when reading and add newline when print statement is used
# -n - suppress default printing at the end of each line processing
# -e - for perl on commandline

' /(^\S+) (.+)/;
split line by first word (^\S+) -> matches first column and stores it in $1 since we used () to capture
the second (.+) stores the rest of the text in $2 

$pre=$1;$mat=$2; 

Assign $1 to $pre and $2 to $mat

while($mat=~/(match.+?_\d+)_(\S+)/g) 
Now mat stores from 2nd column to the rest of the line.
// => match with regex and (match.+?_\d+) to capture it in $1
(\S+) => captures the "HGRR"
/g => like this we have many matches.. so 'g'lobally repeat the matching 
to get all of them using the while loop. If /g is ignored, then we will just get first match alone.

 { print "$pre $1\n$2" } 
Now print $pre, $1 newline and $2 --> This $1 and $2 is local to the while loop and 
don't get confused with the earlier $1 and $2 which we assigned to $pre and $mat
for each while loop turn $1 and $2 match different values and get printed.

Explication

$ cat james.txt
>NP_12345.1 matchnumber_1_RKHKK
>NP_56789.2 matchnumber_1_HGRR  matchnumber_2_KQRHH  matchnumber_3_RVRK matchnumber_4_HTHH
>XP_543421.1    matchnumber_1_RQRH matchnumber_2_QQQQ
$ perl -lne ' /(^\S+) (.+)/;$pre=$1;$mat=$2;while($mat=~/(match.+?_\d+)_(\S+)/g) { print "$pre $1\n$2" } ' james.txt
>NP_12345.1 matchnumber_1
RKHKK
>NP_56789.2 matchnumber_1
HGRR
>NP_56789.2 matchnumber_2
KQRHH
>NP_56789.2 matchnumber_3
RVRK
>NP_56789.2 matchnumber_4
HTHH
>XP_543421.1 matchnumber_1
RQRH
>XP_543421.1 matchnumber_2
QQQQ
$


1 commentaires

heureux que cela ait fonctionné rapidement! .. Je vais ajouter une explication à la réponse



2
votes
$ cat jfile
>NP_12345.1     matchnumber_1_RKHKK
>NP_56789.2     matchnumber_1_HGRR      matchnumber_2_KQRHH     matchnumber_3_RVRK      matchnumber_4_HTHH

$ awk -F"\t" '{for(i=2;i<=NF;i++){match($i,"(matchnumber_[0-9]+)_(.*)",r);print $1 r[1] ORS r[2];}}' jfile
>NP_12345.1matchnumber_1
RKHKK
>NP_56789.2matchnumber_1
HGRR
>NP_56789.2matchnumber_2
KQRHH
>NP_56789.2matchnumber_3
RVRK
>NP_56789.2matchnumber_4
HTHH
Loop from the second field $2 to last field $NF, use match and regex to take out things you wanted, eg matchnumber_1 and RKHKK for first one, and print.   In awk space is just for concatenate, ORS means line ending, effectively equals \n here. r is the regex matched array, with r[0] represent the whole matched string, r[1] and r[2] represent the first and second pair of () matched contents. -- You can change r to other variable name you want.  As for the regex, [0-9] means any single number, and the + after that means to match 1 or more afore denoted thing, here means 1 or more consecutive number(s). As above mentioned, parentheses are just there to catch contents to groups, for later reference. Each pair of parentheses matched contents will be saved into the one element of the array I provided, which is r here.

1 commentaires

merci beaucoup, lorsque les données sont plus compliquées, telles que:> NP_12345.1 matchnumber_1_starto = 17 ~ 21_RKHKK> NP_56789.2 matchnumber_1_starto = 26 ~ 29_HGRR matchnumber_2_starto = 98 ~ 102_KQRHH matchnumber_3_starto = 108 ~ 112_RHH matchnumber_3_starto = 108 ~ 112_RVumber_TH_number4 votre code comme "awk -F" \ t "'{for (i = 2; i <= NF; i ++) {match ($ i," (matchnumber_ [0-9] +) _ (starto = ‌ [0 -9] + ~ [0-9] +) _ (. *) "‌, r); print $ 1 r [1] r [2] ORS r [3];}} '", ça a très bien fonctionné! Très apprécié !



1
votes

Un autre liner perl one:

($c1,@r)=split/\s+/,$_;             # split allline into 1 col value and rest of the line
for(@r){                            # for each lols othar than 1rst one
    ($c,$v)=$_=~/^(.+)_(.+)$/;      # extract before the last underscore and after it
    say "$c1 $c\n$v"                # print col1 coln linebreak value
}

Explication:

perl -anE '($c1,@r)=split/\s+/,$_;for(@r){($c,$v)=$_=~/^(.+)_(.+)$/;say "$c1 $c\n$v"}' file.txt
>NP_12345.1 matchnumber_1
RKHKK
>NP_56789.2 matchnumber_1
HGRR
>NP_56789.2 matchnumber_2
KQRHH
>NP_56789.2 matchnumber_3
RVRK
>NP_56789.2 matchnumber_4
HTHH
>XP_543421.1 matchnumber_1
RQRH
>XP_543421.1 matchnumber_2
RQRH
>XP_543421.1 matchnumber_3
RQRH


2 commentaires

peut être raccourci perl -anE '($ c1, @ r) = split / \ s + /; m /^(.+)_(.+)$/ et dites "$ c1 $ 1 \ n $ 2" pour (@r) '


@ stack0114106: Vrai.



1
votes

Python n'est pas bon pour les commandes à une seule ligne, mais il est facile d'analyser votre fichier avec:

parser.py:

python parse.py file

Vous pouvez alors utiliser soit:

cat file | python parse.py

ou:

import fileinput

for line in fileinput.input():     # process stdin or files given as parameters
    words = line.split()           # split the line
    for w in words[1:]:            # process all words past the first
        ix = w.rindex('_')         # search last _ in the words
        print(words[0] + w[:ix])   # print first line
        print(w[ix+1:])            # and second one


1 commentaires

J'utilise: python parse.py file> output, pour obtenir les résultats.



2
votes

Les gawk suivants ( pour le gensub extenstion) pourrait fonctionner pour vous:

awk '{for(i=2;i<=NF;i++){print $1 gensub(/_([^_]+)$/,"\n\\1",1,$i)}}' file


0 commentaires

1
votes

Cela pourrait fonctionner pour vous (GNU sed):

sed -r ':a;h;/^(\S+)\s+(\S+)_(\S+)\s*(.*)/{s//\1\2\n\3/p;x;s//\1 \4/;ta};d' file

Faites une copie de la ligne courante. Utilisez la correspondance de modèle pour manipuler les premier et deuxième champs de l'espace de modèle au format requis et l'imprimer. Passez à la copie et raccourcissez-la en supprimant le deuxième champ et tout espace blanc suivant. Répétez jusqu'à ce que la correspondance de modèle échoue.


0 commentaires