分析字段数不确定的文本



我有一个文件(~50,000行)文本.txt如下所示,其中包含来自五个个体(AB,BB,CA,DD,GG)的一些基因信息。文件中的 \t 是一个制表符分隔符。文件中还有很多没有用的信息,我想清理一下。所以我需要的是用'transcript=' id提取物种名称,如果他们有的话,还要提取'DD:"和'GG:"部分。

$head文本.txt

GeneAtAB:xrbyk | jdnif | otherTexttBB:abdf | jdhfkc | otherDifferentTexttCA:bdmf | nfjvks | transcript=aaabb.1tDD:hudnf.1 type=cdstGG:jdubf.1 type=cds
GeneBtBB:dfsfg | dfsfvdf | otherDifferenttCA:zdcfsdf | xfgdfgs | transcript=sdfs.1tDD:sdfsw.1 type=cdstGG:fghfg.1 type=cds
GeneCtAB:dsfsdf | xdvv | otherText1tBB:xdsd | sdfsdf | otherDifferentText2tDD:hudnf.1 type=cdstGG:jdubf.1 type=cds
GeneDtAB:dfsdf | Asda | transcript=asdasd.2tCA:bdmf | nfjvks | transcript=aaabb.1tDD:hudnf.1 type=cdstGG:jdubf.1 type=cds

我希望输出是

GeneAtCA:transcript=aaabb.1tDD:hudnf.1tGG:jdubf.1
GeneBtCA:transcript=sdfs.1tDD:sdfsw.1tGG:fghfg.1
GeneCtDD:hudnf.1tGG:jdubf.1
GeneDtAB:transcript=asdasd.2tCA:transcript=aaabb.1tDD:hudnf.1tGG:jdubf.1

我已经搜索和思考了整整一个下午,只有用第一列基因名称按物种撕开这个文件的想法,然后分别修改每个文件,最后根据基因名称将文件合并在一起。但如您所见,文件的每一行不必具有相同数量的字段,因此我不能简单地使用awk来打印某个列。我不知道我怎么能按物种撕毁它们。

我试图模仿这个如何使用sed/grep提取两个单词之间的文本?,但没有成功。我还阅读了一些关于Python如何拆分文本的信息(因为我开始学习这种语言),但仍然无法弄清楚。有人可以帮忙吗?多谢!

更新输入数据的澄清: 在上面显示的例子中,每个个体的基因信息用制表符(\t)分隔,这意味着个人名称加冒号(例如AB:)后面的所有文本都属于个体(例如AB的"xrbyk | jdnif | otherText")。是否将个体保留在最终输出中取决于该个体是否有"transcript="的信息,DD 和 GG 除外。这就是为什么在最终输出中,第一行以CA开头,而不是以AB开头。

这个解决方案有点长,但应该很容易使用:

#!/usr/bin/env python3
# main.py
import csv
import fileinput
import re

def filter_fields(row):
output = []
for field_number, field in enumerate(row, 1):
if field_number == 1:
output.append(field)
elif "DD:" in field or "GG:" in field:
output.append(field.split()[0])
elif "transcript=" in field:
# Remove stuff from after the colon to the last space
output.append(re.sub(r":.* ", ":", field))
return "t".join(output)

reader = csv.reader(fileinput.input(), delimiter="t")
for row  in reader:
print(filter_fields(row))

如何运行它:

# Output to the screen
python3 main.py text.txt
# Output to a file
python3 main.py text.txt > out.txt
# Use as a filter
cat text.txt | python3 main.py

笔记

  • 在此解决方案中,每行文本被分成一行字段。
  • 函数filter_fields将占用每一行,决定要保留的字段并重新格式化。然后,它返回这些字段,以制表符分隔。
  • re.sub(...)电话说:删除冒号后的所有内容,直到最后一个空格。

该任务很简单,可以通过定义指定感兴趣信息的正则表达式在一行代码中解决。

要测试输入数据,请将while <DATA>替换为while <>,并将文件名作为脚本的参数。

use strict;
use warnings;
use feature 'say';
my $re = qr/(Gene.|[A-Z]{2}:(?=[^:]*transcript=)|transcript=S+|GG:S+|DD:S+)/;
say join("t",/$re/g) while <DATA>;
exit 0;
__DATA__
GeneA   AB:xrbyk | jdnif | otherText    BB:abdf | jdhfkc | otherDifferentText   CA:bdmf | nfjvks | transcript=aaabb.1   DD:hudnf.1 type=cds GG:jdubf.1 type=cds
GeneB   BB:dfsfg | dfsfvdf | otherDifferent CA:zdcfsdf | xfgdfgs | transcript=sdfs.1    DD:sdfsw.1 type=cds GG:fghfg.1 type=cds
GeneC   AB:dsfsdf | xdvv | otherText1   BB:xdsd | sdfsdf | otherDifferentText2  DD:hudnf.1 type=cds GG:jdubf.1 type=cds
GeneD   AB:dfsdf | Asda | transcript=asdasd.2   CA:bdmf | nfjvks | transcript=aaabb.1   DD:hudnf.1 type=cds GG:jdubf.1 type=cds

输出

GeneA   CA:     transcript=aaabb.1      DD:hudnf.1      GG:jdubf.1
GeneB   CA:     transcript=sdfs.1       DD:sdfsw.1      GG:fghfg.1
GeneC   DD:hudnf.1      GG:jdubf.1
GeneD   AB:     transcript=asdasd.2     CA:     transcript=aaabb.1      DD:hudnf.1      GG:jdubf.1

只需更改少量代码,即可实现所需的确切输出

use strict;
use warnings;
use feature 'say';
my $re = qr/(Gene.|[A-Z]{2}:transcript=S+|GG:S+|DD:S+)/;
while( <DATA> ) {
s/[^:]*(?=transcript=)//g;
say join("t",/$re/g);
}
exit 0;
__DATA__
GeneA   AB:xrbyk | jdnif | otherText    BB:abdf | jdhfkc | otherDifferentText   CA:bdmf | nfjvks | transcript=aaabb.1   DD:hudnf.1 type=cds GG:jdubf.1 type=cds
GeneB   BB:dfsfg | dfsfvdf | otherDifferent CA:zdcfsdf | xfgdfgs | transcript=sdfs.1    DD:sdfsw.1 type=cds GG:fghfg.1 type=cds
GeneC   AB:dsfsdf | xdvv | otherText1   BB:xdsd | sdfsdf | otherDifferentText2  DD:hudnf.1 type=cds GG:jdubf.1 type=cds
GeneD   AB:dfsdf | Asda | transcript=asdasd.2   CA:bdmf | nfjvks | transcript=aaabb.1   DD:hudnf.1 type=cds GG:jdubf.1 type=cds

输出

GeneA   CA:transcript=aaabb.1   DD:hudnf.1      GG:jdubf.1
GeneB   CA:transcript=sdfs.1    DD:sdfsw.1      GG:fghfg.1
GeneC   DD:hudnf.1      GG:jdubf.1
GeneD   AB:transcript=asdasd.2  CA:transcript=aaabb.1   DD:hudnf.1      GG:jdubf.1

假设示例文本中的那些t是真正的选项卡,这个 Perl 一行将做到这一点。 如果它们是文字t文本,那么这需要稍微调整一下。 将要抓取的每个字段放在正则表达式交替中GG:之后。

perl -lne '@wanted = $_ =~ m{(^Gene[ABCD]|(?:transcript=|DD:|GG:)S+)}g; print join "t", @wanted if @wanted; ' inputfile.txt > outputfile.txt

输出:

GeneA   transcript=aaabb.1      DD:hudnf.1      GG:jdubf.1
GeneB   transcript=sdfs.1       DD:sdfsw.1      GG:fghfg.1
GeneC   DD:hudnf.1      GG:jdubf.1
GeneD   transcript=asdasd.2     transcript=aaabb.1      DD:hudnf.1      GG:jdubf.1

关于您的澄清,这一个衬里将使您非常精细地控制捕获的内容。 这更像是一种完整的解析方法。

perl -lne '($gene, @parts) = split/t/,$_; my @wanted; foreach $p (@parts) { $p =~ m{(?|^([A-Z]{2}:).*?(transcript=S+)|(DD:S+)|(GG:S+))} and push @wanted, "$1$2"; }; print join "t", $gene, @wanted if @wanted; ' inputfile.txt > outputfile.txt

输出:

GeneA   CA:transcript=aaabb.1   DD:hudnf.1      GG:jdubf.1
GeneB   CA:transcript=sdfs.1    DD:sdfsw.1      GG:fghfg.1
GeneC   DD:hudnf.1      GG:jdubf.1
GeneD   AB:transcript=asdasd.2  CA:transcript=aaabb.1   DD:hudnf.1      GG:jdubf.1

这将获取所有 DD 和 GG 优先组件以及任何带有transcript=的 ID。

它的工作原理是这样的。

分离
  1. 出基因并分解每个标签分离的部分
  2. 在每个部分中,使用正则表达式抓住我们关心的部分
  3. 保存匹配的位,并仅在正则表达式匹配时才将其推送到@wanted
  4. (?|正则表达式术语会导致编号的捕获变量在交替中保持不变,而不是继续上升。 即我们只有$1$2而不是$1, $2, $3, $4.
  5. 如果有任何@wanted,请打印出该行

请记住,每次"简化"任务都有丢失实际需要的数据的风险。 如果数据足够复杂,您最终可能会得到一个完整的解析器。

点击这里阅读更多关于如何使用Perl进行流水线的信息。 https://perldoc.perl.org/perlrun

相关内容

最新更新