Циклический ассоциативный массив AWK

Я использую столбец в одном файле для поиска значений в другом файле. Второй файл очень большой, и я хотел бы найти все значения за один проход с помощью awk. Я пытался сделать это с помощью ассоциативного массива, но не знаю, как получить результат, который я хочу. Я хочу взять F1, использовать $2 для поиска значений в F2 и получить вывод, показанный ниже, который представляет собой $0 из F1 в качестве заголовка, за которым следуют $10 из F2, отсортированные и подсчитанные для каждой уникальной строки (т.е. пропущенные через sort | уникальный -с).

F1

+ID=dnaK.p01 12121 TTGGGCAGTTGAAACCAGACGTTTCGCCCCTATTACAGAC[T]CACAACCACATGATGACCG

F2

solid309_20110930_FRAG_BC_bcSample12273_1541_657_F3     0       NC_012759       12121   42      35M     *       0       0       ACACAACCACATGATGACCGAATATATAGTGGCTC     BBBBBBA@BBBAB@?B@BBBB<5BBBAA@:>>&B7
solid309_20110930_FRAG_BC_bcSample12295_323_1714_F3     0       NC_012759       12121   42      35M     *       0       0       ACACAACCACATGATGACCGAATATATAGTGGAGA     BB@@A@@A@@@?@<=?@@=><6*7=?9993>4&7,
solid309_20110930_FRAG_BC_bcSample12325_1148_609_F3     0       NC_012759       12121   42      35M     *       0       0       ACACAACCACATGATGACCGAATATATAGTGGAGA     BBBB@B@?@B@@A@??BBBA@<.<==:6:1>9(<-
solid309_20110930_FRAG_BC_bcSample11796_1531_1170_F3    0       NC_012759       12122   42      35M     *       0       0       CACAACCACATGATGACCGAATATATAGTGGAGCA     '&&+&&)&')&0(.,',(.3+&&&+,&&&&&&&&&
solid309_20110930_FRAG_BC_bcSample12110_1166_1149_F3    0       NC_012759       12122   42      35M     *       0       0       CACAACCACATGATGACCGAATATATAGTGGAGAC     -(:18)538;,9277*'8:<)&,0-+)//3&'1+'
solid309_20110930_FRAG_BC_bcSample183_686_962_F3        0       NC_012759       12123   42      35M     *       0       0       ACAACCACATGATGACCGAATATATAGTGGAGTGC     BB?BBBB;BBBBBB@ABB;@7AA@@A@*>?+B8@9

Я делаю это с помощью следующего скрипта

for line in `awk '{if ($1~"-") print ($2-34);else print $2}' $1`
do
awk -v l=$line '{if ($1~"-") l=l+34;if ($2==l) print }' $1 >> f2
awk -v l=$line '{if ($4==l) print $10}' URA2.sam | sort | uniq -c |awk '{if ($1>15) print}'>> f2
done

Для этого требуется несколько проходов с awk для каждой строки. Я подумал, что могу использовать ассоциативный массив, созданный из F1, чтобы сделать это за один проход. F2 сортируется по $4. Я использовал следующий скрипт, чтобы попытаться получить желаемый результат.

awk 'FNR==NR{a[$2]=$0;next}$4 in a{print $10}' f1 f2 | sort | uniq -c

person jeffpkamp    schedule 24.01.2014    source источник
comment
подумайте о том, чтобы отредактировать свой вопрос, чтобы показать текущий вывод из awk 1-liner, если вы удалите sort | уникальный -с. Это кажется очень близким к тому, что должно работать, учитывая ваше описание «использование столбца в одном файле для поиска значений в другом файле». НО вывод вашего примера, похоже, на самом деле не соответствует вашему заявленному требованию, «за которым следуют 10 долларов от F2, отсортированные и подсчитанные для каждой уникальной строки». Удачи.   -  person shellter    schedule 25.01.2014
comment
Действительно ли нужно сортировать вывод для каждого заголовка или достаточно, чтобы он был уникальным с учетом количества?   -  person Ed Morton    schedule 25.01.2014
comment
@EdMorton Значения ниже необходимо отсортировать для программы uniq, потому что она учитывает только следующую строку при определении того, является ли строка уникальной.   -  person jeffpkamp    schedule 25.01.2014
comment
@jeffpkamp, ​​если все, что вам нужно, это подсчет уникальных записей, это тривиально делается в awk (см. уже опубликованные ответы), вам не нужно передавать его вывод в uniq. Относительно сложная вещь в awk — это сортировка.   -  person Ed Morton    schedule 25.01.2014


Ответы (2)


Вот вывод с использованием GNU awk для asorti() и delete array:

$ cat tst.awk
function prtCounts(     val,sorted,idx) {
    if (prev in f1) {
        print f1[prev]
        asorti(count,sorted)
        for (idx=1; idx in sorted; idx++) {
            val = sorted[idx]
            print count[val], val
        }
    }
    delete count
}

NR==FNR { f1[$2] = $0; next }

{
    if ( (FNR>1) && ($4!=prev) )
        prtCounts()
    count[$10]++
    prev = $4
}

END { prtCounts() }

$ gawk -f tst.awk file1 file2
a 1 b c d
1 BALH
2 BLAH
b 2 b c d
1 HAHA
2 ZAHA
person Ed Morton    schedule 24.01.2014
comment
Это отлично работает в тесте, но у меня проблемы с истинным набором данных. В реальном наборе данных он дает мне сумму уникальных строк для каждого уникального 4 доллара, а не только те, которые указаны в файле F1. - person jeffpkamp; 25.01.2014
comment
@jeffpkamp - на самом деле, я ДУМАЮ, что понимаю ситуацию, которую вы описываете, поэтому я обновил свой ответ решением. Я только что опубликовал версию gawk с отсортированным выводом, сделав ее краткой... - person Ed Morton; 25.01.2014
comment
теперь я не получаю никакого вывода. Есть ли место, где я могу опубликовать два небольших файла для тестирования? Данные на 256 КБ выглядят ужасно в посте :/. - person jeffpkamp; 25.01.2014
comment
Я работаю над получением небольшого набора для тестирования, глядя на исходный сценарий, который вы написали (который я скопировал, а не напечатал), похоже, он печатает все поля, независимо от того, находятся ли они в f1 [массив] или нет. Дайте мне 5 минут, чтобы выложить 2 тестовых файла. - person jeffpkamp; 25.01.2014

Я бы сначала обработал F1 и использовал ассоциированный массив для сохранения, второе поле в качестве ключа и всю строку в качестве значения. Тогда вам нужно будет обработать F2 только один раз, и каждый раз, когда это четвертое поле изменяется, выводить количество повторяющихся значений в десятом поле, например:

awk '
    ## Process F1.
    FNR == NR {
        f1[$2] = $0
        next
    }

    ## Process F2.
    {
        if (FNR == 1 || prev_key == $4) {
            f2[$10]++
        }
        else {
            printf "%s\n", f1[prev_key]
            for (key in f2) {
                printf "%d %s\n", f2[key], key
            }
            delete f2
            f2[$10]++
        }
        prev_key = $4
    }

    END {
        printf "%s\n", f1[prev_key]
        for (key in f2) {
            printf "%d %s\n", f2[key], key
        }
    }
' F1 F2

Это дает:

a 1 b c d
1 BALH
2 BLAH
b 2 b c d
2 ZAHA
1 HAHA
person Birei    schedule 24.01.2014