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
6 Réponses :
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 $
heureux que cela ait fonctionné rapidement! .. Je vais ajouter une explication à la réponse
$ 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.
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é !
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
peut être raccourci perl -anE '($ c1, @ r) = split / \ s + /; m /^(.+)_(.+)$/ et dites "$ c1 $ 1 \ n $ 2" pour (@r) '
@ stack0114106: Vrai.
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
J'utilise: python parse.py file> output, pour obtenir les résultats.
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.