2013-09-12 3 views
0

У меня есть два файла, как показано ниже, которые являются разделителями табуляции:ошибка AWK: не может выделить память

файл А

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 байт памяти (не может выделить память)». Может ли кто-нибудь дать альтернативу этому, чтобы файл читался в меньших частях.

+0

Если заказ не важен, а 'fileA' меньше, чем' fileB', попробуйте обратное. Сохраните 'fileA' в памяти и обработайте строку' fileB' на строку. – Birei

+0

Если строки отсортированы, вы можете ограничить количество сохраненных ключей и значений. – konsolebox

+0

@Birei Я хочу, чтобы информация была добавлена ​​в файлA, поэтому ее нельзя отменить. – chas

ответ

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, и иметь возможность обрабатывать входные данные ... или это может не случиться!

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

+0

Thanks! что составляет 10 долларов США в приведенном выше коде? И в случае, если ключевые столбцы больше 2, то какими будут изменения в коде? можем ли мы сказать ключ = $ 3 "\ x1c" $ 4 "\ x1c" $ 5? – chas

+0

За 10 долларов извините, я неправильно истолковал ваш исходный код. Теперь это исправлено.Да, вы можете отредактировать его таким образом. Просто убедитесь, что поля из обоих файлов будут совпадать с ним. – konsolebox

+0

Я устал от вашего кода, и он дает ту же ошибку: не может выделить 6400 байт памяти (не может выделить память) – chas

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 

Он использует близко к нулю памяти, потому что не хранит значения внутри, но он будет медленным, потому что для каждой строки в файлеA он читает каждую строку в файлеB до тех пор, пока не найдет совпадение, в отличие от того, что вы уже пытались прочитать все строки es из fileB и хранить их как элементы массива, связанные с полями 3 и 4, и в этом случае это будет внутренний хэш-поиск для каждой строки файлаA вместо внешнего линейного поиска.

Если есть много ключей от FiLea, которые не существуют в FILEB, то вы можете ускорить его значительно сортировка FILEB на 3-м и 4-го полей, а затем изменить тест внутри цикла 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 
     } 

Вы можете найти правильную логику для этого - надеюсь, вы получите представление о том, что хотите остановить цикл, когда вы прошли точку, в которой значения, которые вы ищете, из файлаA могут существовать в отсортированном файлеB.

+0

привет, спасибо, и я попробовал ваш код, и он еще не завершился! он работал более 3 дней и все еще работает. любая помощь для ускорения? – chas

+0

Да, я сказал, что это будет медленно. Это с отсортированной версией для fileB? Насколько велика fileA? –

+0

no Я не использовал отсортированную версию, я использовал код tst.awk в начальной версии. Я попытался подключить отсортированную версию кода в начальной версии, но встретился с некоторыми ошибками с кодом. Не могли бы вы опубликовать полный отсортированный код версии, чтобы я попытался вернуться к вам. – chas

Смежные вопросы