【问题标题】:awk error: cannot allocate memoryawk 错误:无法分配内存
【发布时间】:2013-09-16 21:58:58
【问题描述】:

我有两个如下所示的以​​制表符分隔的文件:

文件 A

chr1   123 aa b c d
chr1   234 a  b c d
chr1   345 aa b c d
chr1   456 a  b c d
....

文件B

xxxx  abcd    chr1   123    aa    c    d    e
yyyy  defg    chr1   345    aa    e    f    g
...

我想将基于 2 列的两个文件与“chr1”、“123”连接起来,并将文件 B 中的前两列添加到文件 A。这是使用

awk 'NR==FNR{a[$3,$4]=$1OFS$2;next}{$7=a[$1,$2];print}' OFS='\t' fileb filea

输出:

chr1   123    aa    b    c    d    xxxx    abcd
chr1   234    a     b    c    d
chr1   345    aa    b    c    d    yyyy    defg
chr1   456    a     b    c    d

但是对于真实的数据,fileb 太大并且返回错误:“can't allocate 6400 bytes of memory (cannot allocate memory)”。有人可以提供另一种方法来执行此操作,以便以较小的部分读取文件。

【问题讨论】:

  • 如果顺序不重要a并且fileA小于fileB,尝试相反。将fileA 保存在内存中,每行处理fileB
  • 如果行已排序,您可以限制存储的键和值的数量。
  • @Birei 我希望将信息添加到文件 A 中,这样它就不能被反转。
  • @konsolebox 你能编辑一下代码吗

标签: join awk


【解决方案1】:

一种快速而肮脏的技术是操纵您的输入数据并使用join

$ awk '{print $3"-"$4,$1,$2}' fileb | sort > fileb2
$ awk '{print $1"-"$2,$3,$4,$5}' filea | sort > filea2
$ join -a1 filea2 fileb2
chr1-123 aa b c xxxx abcd
chr1-234 a b c
chr1-345 aa b c yyyy defg
chr1-456 a b c

如有必要,您可以去掉第一列中的 -。请注意,这并不可靠,购买可能就足够了。 join 可能需要比 awk 更少的内存并且能够处理输入......或者它可能不会!

【讨论】:

    【解决方案2】:

    你可以试试这个代码:

    #!/usr/bin/awk -f
    
    BEGIN {
        file1 = ARGV[1]
        file2 = ARGV[2]
        LIMIT = 1000
        OFS = "\t"
        i = 0
        while ((getline < file2) > 0) {
            key = $3 "\x1c" $4
            if (!(key in a)) {
                a[key] = $1 OFS $2
                if (i == LIMIT) {
                    break
                }
            }
        }
        while ((getline < file1) > 0) {
            key = $1 "\x1c" $2
            if (key in a) {
                $7 = a[key]
                print
                delete a[key]
                while ((getline < file2) > 0) {
                    key = $3 "\x1c" $4
                    if (!(key in a)) {
                        a[key] = $1 OFS $2
                        break
                    }
                }
            } else {
                $7 = ""
                print
            }
        }
        exit 0
    }
    

    用法:awk -f script.awk filea fileb

    【讨论】:

    • 谢谢!上面代码中的 10 美元是多少?如果键列超过 2,那么代码中的编辑是什么?我们可以说 key = $3 "\x1c" $4 "\x1c" $5 吗?
    • 对于 10 美元的抱歉,我误解了您的原始代码。现在已经修好了。是的,您可以这样编辑它。只需确保两个文件中的字段与其匹配即可。
    • 我厌倦了使用你的代码,它给出了同样的错误:无法分配 6400 字节的内存(无法分配内存)
    • @user1779730 也许这取决于您使用的 awk。你在使用 GNU 吗?需要多长时间才会出现错误?
    【解决方案3】:

    这用速度换取内存:

    $ cat tst.awk                
    BEGIN{
        FS=OFS="\t"
        lookup = ARGV[--ARGC]
        delete ARGV[ARGC]
    }
    {
        found = 0
        while ( !found && ((getline str < lookup) > 0) ) {
            split(str,arr)
            if ( ($1 == arr[3]) && ($2 == arr[4]) ) {
                $0 = $0 OFS arr[1] OFS arr[2]
                found = 1
            }
        }
        close(lookup)
        print
    }
    $ gawk -f tst.awk fileA fileB
    chr1    123     aa      b       c       d       xxxx    abcd
    chr1    234     a       b       c       d
    chr1    345     aa      b       c       d       yyyy    defg
    chr1    456     a       b       c       d
    

    它使用接近于零的内存,因为它不会在内部存储任何值,但它会很慢,因为对于 fileA 中的每一行,它会读取 fileB 中的每一行,直到找到匹配项,而不是你已经尝试过的是从 fileB 读取所有行并将它们存储为由字段 3 和 4 键入的数组元素,在这种情况下,它将是 fileA 的每一行的内部哈希查找,而不是外部线性搜索。

    如果 fileA 中的许多键在 fileB 中不存在,那么您可以通过在第 3 和第 4 个字段对 fileB 进行排序,然后将 getline 循环中的测试更改为类似以下内容来显着加快速度:

            if ( ($1 FS $2) == (arr[3] FS arr[4]) ) {
                $0 = $0 OFS arr[1] OFS arr[2]
                found = 1
            }
            else (if ($1 FS $2) < (arr[3] FS arr[4]) ) {
                found = 1
            }
    

    您可以找出正确的逻辑 - 希望您知道当您从 fileA 中查找的值可能存在于已排序的 fileB 中时,您想要停止循环。

    【讨论】:

    • 嗨,谢谢,我试过你的代码,但它还没有完成!!它运行了 3 天多,并且仍在运行。有什么加快速度的帮助吗?
    • 是的,我确实说过它会很慢。那是fileB的排序版本吗? fileA 有多大?
    • 不,我没有使用排序版本,我使用了 tst.awk 代码,即初始版本。我尝试在初始版本中插入代码的排序版本,但遇到了一些代码错误。您能否发布一个完整的排序版本代码,以便我会尝试回复您。
    • 不,您需要自己为此付出一点努力,否则您将永远无法学习如何使用 awk。我已经给了你要使用的代码段,尝试自己修改脚本,运行它,如果它失败尝试修复它,如果你不能然后用修改后的脚本和运行结果更新你的问题和我们可以为您提供更多帮助。再次 - fileA 有多大?
    • 好的,我会尝试修复它。 fileA 为 30Mb,fileB 为 70Gb
    猜你喜欢
    • 1970-01-01
    • 2017-12-07
    • 2016-04-17
    • 1970-01-01
    • 2020-01-14
    • 2018-06-23
    • 2012-09-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多