找到比较两个巨大但已排序文件的行的有效方法

时间:2015-02-11 11:37:02

标签: python bash perl shell scripting-language

我实际上只是坚持某些事情,需要一些帮助......

我有两个巨大的txt文件(标签分隔符),有这种格式:

文件1:

1  robert  youh  xpla@ioaio.fr
2  patrick  yuad  qqqq@ioaio.fr
3  bob  fsgfq  ddd@ioaio.fr
8  tim  qqjh  hjahj@uayua.com
9  john  ajkajk  rtaeraer@auiaui.com

文件2:

1  baby  france  paris
2  father  usa  detroit
3  mother  uk  london
4  baby  italy  milan

两个文件都已排序,但 File1 大于 File2 。我需要找到 File1 中没有出现在 File2 中的行(根据第一列,所以只有第一列用于比较)。

我尝试了很多方法: - awk:没有找到办法(由于长度不同) - python(用于哪个行语句和fileinput.input ...),我的脚本花了大约5分钟来执行0.3%的行。

结果:我能够使用python(对小文件进行测试)检索正确的结果,但我无法处理我的大文件。

有什么想法可以提供帮助吗?

我的文件大约有两百万行。

4 个答案:

答案 0 :(得分:1)

假设 - 正如您所说 - 文件已排序(我没有对其进行测试,但它应该有效)

FILE1_POS = 0
FILE2_POS = 1
MAX_POS = 2

missing = []
get_rec_no = lambda l, f=FILE1_POS: int(l.split(None, MAX_POS)[f])
with open('File1') as source, open('File2') as trgt:
    trgt = iter(trgt)
    for line in source:
        src_rec_no = get_rec_no(line)
        try:
            trgt_rec_no = get_rec_no(trgt.next(), FILE2_POS)
            while trgt_rec_no < src_rec_no:
               missing.append(src_rec_no)
               trgt_rec_no = get_rec_no(trgt.next(), FILE2_POS)
         except StopIteration:
            break
    for line in source:
        missing.append(get_rec_no(line))

修改

我改变了你的要求

答案 1 :(得分:1)

如果由于性能而遇到问题,您可能需要考虑使用Hadoop / MapReduce之类的东西将文件拆分成许多较小的文件,然后您可以在不同的处理器上运行每个子进程以实现加速。

作为一个简单的例子,将文件分成两部分,你可以有一个子文件,其中包含[A-M],[M-Z]之间的所有键。这样,如果您知道一个文件中的密钥,您就知道要搜索哪个子文件可能匹配。从理论上讲,如果将文件分成两部分,您将把搜索时间缩短一半(但是,由于涉及开销,因此不完全是一半)。

基本上,编程将涉及编写mapper.py和reducer.py。如果你不熟悉Hadoop / MapReduce,有许多关于在Python中设置MapReduce作业的好教程,但我建议的是名为"Intro to Hadoop and MapReduce"的Udacity课程,它使用Python和MapReduce解决了一些类似的问题。

此外,您可能需要考虑编辑标记以包含Hadoop MapReduce,在这种情况下,您可以获得有关编写mapper.py和reducer.py的更具体的帮助。

答案 2 :(得分:1)

文件长度不同的事实并不排除awk解决方案。拿两个输入:

 [ damien $] cat file1
cat: file1: No such file or directory
 [ damien $] cat file1.txt
1  robert  youh  xpla@ioaio.fr
2  patrick  yuad  qqqq@ioaio.fr
3  bob  fsgfq  ddd@ioaio.fr
8  tim  qqjh  hjahj@uayua.com
9  john  ajkajk  rtaeraer@auiaui.com
 [ damien $] cat file2.txt
1  baby  france  paris
2  father  usa  detroit
3  mother  uk  london
4  baby  italy  milan

 [ damien $] 

考虑以下脚本:

 [ damien $] cat n_join.awk
#!/usr/bin/gawk -f


NR==FNR{
  #  file2[$1]=$0;
   file2[$1]=1;
}
NR!=FNR{
    if(!($1 in file2)){
# print current record if not in file2
        print ;
    }else{
# if $1 from file1 has been found.
# if it's really unique in file1, it
# can be deleted from file2. Otherwise
# comment this line:
        delete file2[$1];
    }
}
 [ damien $]

给出输出:

[ damien $] chmod +x n_join.awk
 [ damien $] ./n_join.awk file2.txt file1.txt
8  tim  qqjh  hjahj@uayua.com
9  john  ajkajk  rtaeraer@auiaui.com
 [ damien $]

请注意,必须首先传递file2.txt。我不知道这是否适用于200万行的文件,但有兴趣知道你是否有时间尝试它。 :)

如果你可以提供文件(不太可能),我会亲自尝试......:D

编辑:我知道你已经接受了你的答案并且可能继续你的生活,但是,我想补充一些额外的信息。

如果我创建了两个您指定类型的大文件:file1.bit.txt包含500万条记录:

 [ damien $] seq 1 1 5000000 > file1.big.txt
 [ damien $] sed -i 's|$| bob  fsgfq  ddd@ioaio.fr|' file1.big.txt
 [ damien $] head file1.big.txt
1 bob  fsgfq  ddd@ioaio.fr
2 bob  fsgfq  ddd@ioaio.fr
3 bob  fsgfq  ddd@ioaio.fr
4 bob  fsgfq  ddd@ioaio.fr
5 bob  fsgfq  ddd@ioaio.fr
6 bob  fsgfq  ddd@ioaio.fr
7 bob  fsgfq  ddd@ioaio.fr
8 bob  fsgfq  ddd@ioaio.fr
9 bob  fsgfq  ddd@ioaio.fr
10 bob  fsgfq  ddd@ioaio.fr
 [ damien $] tail file1.big.txt
4999991 bob  fsgfq  ddd@ioaio.fr
4999992 bob  fsgfq  ddd@ioaio.fr
4999993 bob  fsgfq  ddd@ioaio.fr
4999994 bob  fsgfq  ddd@ioaio.fr
4999995 bob  fsgfq  ddd@ioaio.fr
4999996 bob  fsgfq  ddd@ioaio.fr
4999997 bob  fsgfq  ddd@ioaio.fr
4999998 bob  fsgfq  ddd@ioaio.fr
4999999 bob  fsgfq  ddd@ioaio.fr
5000000 bob  fsgfq  ddd@ioaio.fr
 [ damien $]
 [ damien $]
 [ damien $]
 [ damien $]

[ damien $]
 [ damien $] seq 2 2 5000000 > file2.big.txt
 [ damien $]  sed -i 's|$| baby  france  paris|' file2.big.txt
 [ damien $] head file2.big.txt
2 baby  france  paris
4 baby  france  paris
6 baby  france  paris
8 baby  france  paris
10 baby  france  paris
12 baby  france  paris
14 baby  france  paris
16 baby  france  paris
18 baby  france  paris
20 baby  france  paris
 [ damien $] tail file2.big.txt
4999982 baby  france  paris
4999984 baby  france  paris
4999986 baby  france  paris
4999988 baby  france  paris
4999990 baby  france  paris
4999992 baby  france  paris
4999994 baby  france  paris
4999996 baby  france  paris
4999998 baby  france  paris
5000000 baby  france  paris
 [ damien $]

仅使用偶数键。运行我的脚本给出:

 [ damien $]
 [ damien $] time ./n_join.awk file2.big.txt file1.big.txt > output.big

real    0m4.154s
user    0m3.893s
sys     0m0.207s
 [ damien $]
 [ damien $] head output.big
1 bob  fsgfq  ddd@ioaio.fr
3 bob  fsgfq  ddd@ioaio.fr
5 bob  fsgfq  ddd@ioaio.fr
7 bob  fsgfq  ddd@ioaio.fr
9 bob  fsgfq  ddd@ioaio.fr
11 bob  fsgfq  ddd@ioaio.fr
13 bob  fsgfq  ddd@ioaio.fr
15 bob  fsgfq  ddd@ioaio.fr
17 bob  fsgfq  ddd@ioaio.fr
19 bob  fsgfq  ddd@ioaio.fr
 [ damien $] tail output.big
4999981 bob  fsgfq  ddd@ioaio.fr
4999983 bob  fsgfq  ddd@ioaio.fr
4999985 bob  fsgfq  ddd@ioaio.fr
4999987 bob  fsgfq  ddd@ioaio.fr
4999989 bob  fsgfq  ddd@ioaio.fr
4999991 bob  fsgfq  ddd@ioaio.fr
4999993 bob  fsgfq  ddd@ioaio.fr
4999995 bob  fsgfq  ddd@ioaio.fr
4999997 bob  fsgfq  ddd@ioaio.fr
4999999 bob  fsgfq  ddd@ioaio.fr
 [ damien $] wc -l output.big
2500000 output.big
 [ damien $]

整个事情在大约4秒内完成,这似乎并不令人望而却步。要么数据集存在很大差异,要么您的脚本与我的操作有很大不同。也许这对某人有用。 :/

聚苯乙烯。根据/ proc / cpuinfo

,我有一个i7-3770 CPU @ 3.40GHz

答案 3 :(得分:0)

在bash中尝试这个:

join -v 1 -j 1 file1.txt file2.txt

结果是:

8 tim qqjh hjahj@uayua.com
9 john ajkajk rtaeraer@auiaui.com

参数如下:

-j 1是要加入的字段,即第一个字段

-v 1表示禁止第一个文件中的行