如何将每一行的第一列数据添加到相应行中由某些特定字符串或字符标记的每一列的开头?

时间:2019-01-04 12:59:33

标签: python regex bash awk sed

我有一大块数据(一个文件),如下所示,每一行具有不同数量的列(由制表符分隔),数据结构像 这个:

>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_m_RVRR
...

在上面的文件中,第一行有2列,第二行有5列,第三行有m + 1列...;显然,文件的每一行都有“> accessionID”和“ matchnumber_i_XXX”。我想将每一行的第一列添加到相应行中以“ matchnumber”标记的每一列的开头,并以fasta格式打印出来,输出就像这样:

>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
>XP_543421.1matchnumber_1
RQRH
....
>XP_543421.1matchnumber_m
RVRR
...

有人可以帮助我吗?预先感谢。

注意:例如,当存在一个行文件时,文件“ a.txt”只有一个行内容:

>NP_56789.2 matchnumber_1_HGRR  matchnumber_2_KQRHH  matchnumber_3_RVRK matchnumber_4_HTHH

我可以使用管道化的awk和sed命令来解析数据:

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

a.fasta就像愚蠢的人一样

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

当a.txt包含多行数据时,我不知道要解决该问题。

6 个答案:

答案 0 :(得分:2)

$ 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

从第二个字段$2到最后一个字段$NF,使用matchregex取出想要的内容,例如matchnumber_1和{{ 1}},然后打印。

在awk中,空间仅用于连接,RKHKK表示行结束,此处有效等于ORS\n是正则表达式匹配的数组,其中r [0]表示整个匹配的字符串,r [1]和r [2]表示第一对和第二对r匹配的内容。 -您可以将()更改为所需的其他变量名称。

对于正则表达式,[0-9]表示任何单个数字,其后的r表示匹配1个或多个上述字符,此处表示1个或多个连续数字。如上所述,括号仅用于将内容捕获到组中,以供以后参考。每对括号中匹配的内容将保存到我提供的数组的一个元素中,这里为+

答案 1 :(得分:2)

以下gawk(针对gensub扩展名)可能对您有用:

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

答案 2 :(得分:1)

抢救Perl!

$ 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
$

说明

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.

答案 3 :(得分:1)

另一个perl一个班轮:

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

说明:

($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
}

答案 4 :(得分:1)

Python不擅长使用一行命令,但是很容易使用它来解析文件:

parser.py:

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

然后您可以使用以下任何一种方式:

cat file | python parse.py

或:

python parse.py file

答案 5 :(得分:1)

这可能对您有用(GNU sed):

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

复制当前行。使用模式匹配将模式空间中的第一个字段和第二个字段操纵为所需格式并进行打印。交换到副本,并通过删除第二个字段和随后的任何空白来缩短副本。重复直到模式匹配失败。