我有两个包含大量文本的大文件,而我要做的就是将文件A中的所有行保留在与文件B中的字段匹配的字段中。
文件A类似于:
Name (tab) # (tab) # (tab) KEYFIELD (tab) Other fields
文件B我设法使用cut和sed等基本上将它归结为一个列表的字段。
所以目标是将文件A中的所有行保留在第4个字段(它表示KEYFIELD)中,如果该行的字段与文件B中的某行匹配。(不必是精确匹配,所以如果文件B有Blah,文件A说Blah_blah,没关系)
我试着这样做:
grep -f fileBcutdown fileA > outputfile
编辑:好的,我放弃了。我只是强迫它杀了它。
有更好的方法吗?对于任何关心的人来说,文件A是13.7MB,关闭后的文件B是32.6MB。
编辑:这是文件A中的示例行:
chr21 33025905 33031813 ENST00000449339.1 0 - 33031813 33031813 0 3 1835,294,104, 0,4341,5804,
文件B中的示例行减少:
ENST00000111111
答案 0 :(得分:3)
您正在达到使用基本shell工具的极限。假设每行约40个字符,文件A中有400,000行,文件B中有大约1,200,000行。你基本上是为文件A中的每一行运行grep,并且每次执行都要使用grep犁通过1,200,000行。这是您正在解析的480 BILLION 行。 Unix工具的速度惊人,但即使快速完成了8000亿次,也会加起来。
使用像Perl或Python这样的完整编程脚本语言会更好。您将文件B 中的所有行都放在哈希中。您在文件A中获取每一行,检查第四个字段是否与哈希中的内容匹配。
读几十行?创建10,000,000条目哈希? Perl可以在几分钟内解析这两个问题。
东西 - 脱离我的头顶。你没有给我们太多的方法,所以我没有做任何测试:
#! /usr/bin/env perl
use strict;
use warnings;
use autodie;
use feature qw(say);
# Create your index
open my $file_b, "<", "file_b.txt";
my %index;
while (my $line = <$file_b>) {
chomp $line;
$index{$line} = $line; #Or however you do it...
}
close $file_b;
#
# Now check against file_a.txt
#
open my $file_a, "<", "file_a.txt";
while (my $line = <$file_a>) {
chomp $line;
my @fields = split /\s+/, $line;
if (exists $index{$field[3]}) {
say "Line: $line";
}
}
close $file_a;
哈希意味着你只需要通读file_b一次而不是400,000次。启动程序,从办公室厨房拿一杯咖啡。 (百胜!非乳制品奶精!)当你回到办公桌时,它就会完成。
答案 1 :(得分:2)
这是使用GNU awk
的一种方式。像:
awk -f script.awk fileB.txt fileA.txt
script.awk
的内容:
FNR==NR {
array[$0]++
next
}
{
line = $4
sub(/\.[0-9]+$/, "", line)
if (line in array) {
print
}
}
或者,这是单行:
awk 'FNR==NR { array[$0]++; next } { line = $4; sub(/\.[0-9]+$/, "", line); if (line in array) print }' fileB.txt fileA.txt
GNU awk
还可以使用fileB.txt
和cut
执行您所描述的sed
的预处理。如果您希望我将其构建到上面的脚本中,您需要提供此行的示例。
使用HumanGenCodeV12
和GenBasicV12
文件进行更新:
运行如:
awk -f script.awk HumanGenCodeV12 GenBasicV12 > output.txt
script.awk
的内容:
FNR==NR {
gsub(/[^[:alnum:]]/,"",$12)
array[$12]++
next
}
{
line = $4
sub(/\.[0-9]+$/, "", line)
if (line in array) {
print
}
}
这可以在GenBasicV12
中找到可以在HumanGenCodeV12
中找到的行。输出文件(output.txt
)包含65340行。该脚本只需不到10秒即可完成。
答案 2 :(得分:0)
grep -f
似乎也很慢。我猜它会尝试输入流中每一行的每个模式。
对我而言更快的解决方案是使用while循环。这假设fileA
相当小(在您的示例中它是较小的一个),因此在较小的文件上多次迭代比多次迭代较大的文件更好。
while read line; do
grep -F "$line" fileA
done < fileBcutdown > outputfile
请注意,如果匹配多个模式,此循环将多次输出一行。要解决此限制,请使用sort -u
,但这可能会相当慢。你必须尝试。
while read line; do
grep -F "$line" fileA
done < fileBcutdown | sort -u | outputfile
如果您依赖于行的顺序,那么除了使用grep -f
之外,我认为您没有其他选择。但基本上它归结为尝试m * n模式匹配。
答案 3 :(得分:0)
使用以下命令:
awk 'FNR==NR{a[$0];next}($4 in a)' <your filtered fileB with single field> fileA