【问题标题】:in Linux: merge two very big files在 Linux 中:合并两个非常大的文件
【发布时间】:2014-01-15 17:29:17
【问题描述】:

我想合并两个文件(一个是空格分隔,另一个是制表符分隔),只保留两个文件之间匹配的记录:

文件 1:空格分隔

A B C D E F G H
s e id_234 4 t 5 7 9
r d id_45 6 h 3 9 10
f w id_56 2 y 7 3 0
s f id_67 2 y 10 3 0

文件 2:制表符分隔

I L M N O P
s e 4 u id_67 88
d a 5 d id_33 67
g r 1 o id_45 89

我想将文件 1 字段 3 ("C") 与文件 2 字段 5 ("O") 匹配,并像这样合并文件:

文件 3:制表符分隔

I L M N O P A B D E F G H
s e 4 u id_67 88 s f 2 y 10 3 0
g r 1 o id_45 89 r d 6 h 3 9 10

文件 1 中的条目未出现在文件 2 中,反之亦然,但我只想保留交集(公共 ID)。

我真的不关心订单。

我不希望使用 join,因为这些文件非常大,未排序,并且 join 需要先按公共字段排序,这需要很长时间和大量内存。

我已经尝试过 awk 但没有成功

awk > file3 'NR == FNR {
  f2[$3] = $2; next 
}
$5 in f2 {
 print $0, f2[$2]
}' file2 file1 

有人可以帮帮我吗?

非常感谢

【问题讨论】:

    标签: linux merge bigdata


    【解决方案1】:

    嗯.. 理想情况下,您最好避免使用 n^2 解决方案,这似乎是基于 awk 的方法所需要的。对于 file1 中的每条记录,您必须扫描 file2 以查看是否发生。这就是时间的流逝。

    我建议为此编写一个 python(或类似的)脚本,并为其中一个文件构建一个地图 id-> 文件位置,然后在扫描另一个文件时查询该位置。这会给你一个 nlogn 运行时,至少对我来说,它看起来是你可以在这里做的最好的(使用哈希作为索引会给你带来寻找文件 pos 的代价高昂的问题)。

    事实上,这是执行此操作的 Python 脚本:

    f1 = file("file1.txt")
    
    f1_index = {}
    
    # Generate index for file1
    fpos = f1.tell()
    line = f1.readline()
    while line:
        id = line.split()[2]
        f1_index[id] = fpos
        fpos = f1.tell()
        line = f1.readline()
    
    # Now scan file2 and output matches
    f2 = file("file2.txt")
    line = f2.readline()
    while line:
        id = line.split()[4]
        if id in f1_index:
            # Found a matching line, seek to file1 pos and read
            # the line back in
            f1.seek(f1_index[id], 0)
            line2 = f1.readline().split()
            del line2[2] # <- Remove the redundant id_XX
            new_line = "\t".join(line.strip().split() + line2)
            print new_line
        line = f2.readline()
    

    【讨论】:

    • 非常感谢您。 Python 似乎太慢了,因为文件非常大:运行您编写的脚本大约需要 10 分钟(无论如何感谢)。我想知道是否有更快的方法来做到这一点? Bash 也会一样吗?
    • 在尝试了其他 Linux 命令之后,tobe 在 Python 中执行此操作的解决方案似乎是最好的。我现在才刚开始了解 Python,现在我遇到了一个非常基本的问题:我得到了我认为的合并文件(我想如果一个是制表符而另一个空格分隔并不重要),但标题消失了。有没有办法保留这两个文件的标题?再次感谢您!
    • 如果标题是指“I L M N O P A B D E F G H”,则只需在脚本顶部添加:打印“I L M N O P A B D E F G H”。至于加快速度……这完全是为了最大化 I/O 子系统,当前的解决方案在这方面不是最佳的,因为它在每个读取行停止/启动。
    【解决方案2】:

    如果对两个文件进行排序(在要匹配的列上)是可能的(并且不会以某种方式破坏内容),join 可能比尝试使用bashawk。既然你说你并不真正关心订单,那么这可能是一个合适的方法。

    看起来像这样:

    加入 -1 3 -2 5 -o '2.1,2.2,2.3,2.4,2.5,2.6,1.1,1.2,1.4,1.5,1.6,1.7,1.8'

    我希望有一种更好的方法来告诉它要输出哪些列,因为这需要大量输入,但它就是这样工作的。您可能还可以省略 -o ... 的东西,然后使用 awk 或其他东西对输出进行后处理,使其按您想要的顺序...

    【讨论】:

    • 嗨,twalberg,谢谢,我宁愿避免“加入”,因为我在问题中说,因为它们是巨大的未排序文件,如果我使用加入,我将不得不对它们进行排序,这需要很长时间…我希望有一个快速的替代方案?
    猜你喜欢
    • 2016-08-24
    • 2017-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-19
    • 2014-10-28
    • 2011-09-21
    • 2012-06-24
    相关资源
    最近更新 更多