awk错误:无法分配内存

时间:2013-09-12 13:22:27

标签: join awk

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

档案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太大并且它返回错误:“无法分配6400字节的内存(无法分配内存)”。有人可以提供替代方法,以便以较小的部分读取文件。

3 个答案:

答案 0 :(得分: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的每一行的内部哈希查找,而不是外部线性搜索。

如果fileB中存在许多不存在于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中的点时停止循环。< / p>

答案 1 :(得分: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 :(得分:0)

您可以尝试以下代码:

#!/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

相关问题