我正在处理包含大约4亿行和3列的文件。前两列是字母数字字符串,而最后一列是数字。像这样:
NM_001011874.1,NM_011441.4,-0.131672299779153
我有多个这样的文件,行数和行数大致相同。这些不一定基于3列中的任何一列进行排序。我正在尝试根据前两列的组合来组合这些文件。例如:
File 1
NM_001011874.1,XR_104537.1,-0.929524370261122
NM_001011874.1,XM_003084433.1,-0.603098764428879
File 2
NM_001011874.1,XR_104537.1,-0.11254525414
NM_001011874.1,NM_005040.1,-0.20509876488
File 3
NM_001011874.1,XR_104537.1,-0.41254525414
NM_001011874.1,NM_005040.1,-0.60509876488
我要做的是通过使用前两列中的值组合创建一个键,然后从该对的第三列中检索相应的值。我得到的最终结果是:
Output2
NM_001011874.1,XR_104537.1,-0.11254525414,-0.929524370261122,-0.41254525414
NM_001011874.1,NM_005040.1,-0.20509876488,,-0.60509876488
我正在使用awk执行上述操作:
awk -F',' 'NR==FNR{a[$1,$2]=$3;next}{$4=a[$1,$2];print}' OFS=',' file1.txt file2.txt
我正在为这项任务分配256GB。使用上述命令生成输出大约需要90分钟,通过两个文件组合,每个文件有大约4亿行和3列。输出文件再次有大约4亿行但是4列。生成输出文件所需的时间随着每个添加的列而增加。
我按顺序执行,即合并file1和file2以生成包含4列的output1。然后合并file3和output1以生成包含5列的output2,然后生成file4和output2以生成具有6列的output3,依此类推,直到我得到22列的最终输出。
我想知道在Python或Perl中这样做在速度和自动化方面是否更有效率?我有大约20个这样的文件,每个文件有3列,尽管行数从1亿到4亿不等。如果您认为我在Python或Perl中做得更好,请您分享一个示例来说明awk脚本如何转换为Python或Perl。
编辑: 根据评论添加了文件3和之后的最终输出。
答案 0 :(得分:4)
如果您拥有庞大的数据文件并希望有效地使用它们,那么最好将它们放入SQLite数据库,索引它们,然后查询它们。有关详情,请参阅my answer about CSV vs SQLite performance。
为数据创建一个表(东西是一个可怕的名字,但我不知道这些数据是什么,所以它是“东西”)。
create table stuff (
key1 text,
key2 text,
value real
);
使用SQLite shell将CSV导入表中。
sqlite> .mode csv
sqlite> .import file1 stuff
sqlite> .import file2 stuff
sqlite> .import file3 stuff
为密钥创建索引。
create index stuff_key on stuff (key1, key2);
询问你心中的内容。
select value
from stuff
where key1 = "NM_001011874.1" and
key2 = "XR_104537.1"
-0.929524370261122
-0.11254525414
-0.41254525414
完成导入和索引后,数据的大小无关紧要。您可以只使用新字段导入小型CSV文件,而不是更新CSV并重新导入所有CSV。或者您可以跳过CSV并直接插入它们。
insert into stuff (key1, key2, value)
values ("NM_204958293.2", "XR_29238498.3", -239.2),
("NM_2904892.3", "XR_3093.0", 9482.39);
我测试了它的性能,因为我多次提倡它,但没有测试过它。
首先,我清除了一堆磁盘空间,因为这些文件会变大。我是在2011年最先进的Macbook Pro i7上做到这一点。幸运的是它有一个售后SSD,因此I / O性能非常出色。它并不懈怠,但它也不是顶级服务器。重点是,您不需要花哨的硬件来获得良好的性能。
然后我写了Perl program to generate 400 million rows of data,然后在运行的时候我写了a C program to do it faster。在一个罕见的程序时间对于一次性脚本比程序员时间更重要的情况下,C程序首先完成了两个大致相同的14G文件。它们有点不同,但对我们来说无关紧要。
然后我创建了表并开始导入。最初的进口时间并不是非常重要,因为我不必坐在这里盯着它或为它做好准备。我知道它会起作用,我知道我只需要做一次,所以我可以并行处理任何数量的东西(比如编辑这篇文章)。不幸的是,SQLite并不是并行工作,它似乎只使用一个核心。 OTOH它使用的内存不超过3兆。
导入一个4亿行文件需要20分钟。由此产生的SQLite数据库大约有17个演出,所以数据没有很大的扩展。我不打算做其余的事,因为它很快就会变得多余。
现在我正在创建索引。再一次,这是一次我不必坐下来观看的事情......除了我这样做,因为它使用1 gig的虚拟内存,而SQLite文件现在是30演出。所以...更多删除文件。建立索引大约需要30分钟。
使用30演出的磁盘导入和索引50分钟,大约是原始数据的两倍。无需编程。