Сводная таблица в AWK

Мне нужно преобразовать элементы из массива в индекс столбца и вернуть значение 3 доллара для каждого индекса столбца. У меня нет доступа к gawk 4, поэтому я не могу работать с реальными многомерными массивами.

Вход

Name^Code^Count
Name1^0029^1  
Name1^0038^1   
Name1^0053^1  
Name2^0013^3  
Name2^0018^3  
Name2^0023^5  
Name2^0025^1  
Name2^0029^1  
Name2^0038^1  
Name2^0053^1  
Name3^0018^1  
Name3^0060^1  
Name4^0018^2  
Name4^0025^5  
Name5^0018^2  
Name5^0025^1  
Name5^0060^1

Желаемый результат

Name^0013^0018^0023^0025^0029^0038^0053^0060
Name1^^^^^1^1^1^  
Name2^3^3^5^1^1^1^1^  
Name3^^1^^^^^^1  
Name4^^2^^5^^^^  
Name5^^^^1^^^^1 

Есть предложения, как решить эту задачу без использования реальных многомерных массивов?


person eh2deni    schedule 05.04.2014    source источник
comment
Кстати - я думаю, что последняя строка в вашем желаемом выходе неверна. Это должно быть Name5^^2^^1^^^^1   -  person D.Shawley    schedule 05.04.2014


Ответы (3)


Следующее решение использует GNU awk v3.2 функции для сортировки. При этом не используются многомерные массивы. Он только имитирует один.

awk -F"^" '
NR>1{
    map[$1,$2] = $3
    name[$1]++
    value[$2]++
}
END{
    printf "Name"
    n = asorti(value, v_s)
    for(i=1; i<=n; i++) {
        printf "%s%s", FS, v_s[i]
    }
    print ""
    m = asorti(name, n_s)
    for(i=1; i<=m; i++) { 
        printf "%s", n_s[i]
        for(j=1; j<=n; j++) { 
            printf "%s%s", FS, map[n_s[i],v_s[j]]
        }
        print ""
    }
}' file
Name^0013^0018^0023^0025^0029^0038^0053^0060
Name1^^^^^1^1^1^
Name2^3^3^5^1^1^1^1^
Name3^^1^^^^^^1
Name4^^2^^5^^^^
Name5^^2^^1^^^^1
person jaypal singh    schedule 05.04.2014
comment
+1 Awk имеет встроенную переменную SUBSEP, которая используется для разделения индексов псевдо-двумерных массивов, когда вы используете запятую между ними, поэтому вместо map[$1 FS $2] вы можете написать более естественный map[$1,$2]. Также ((map[n_s[i] FS v_s[j]]) ? map[n_s[i] FS v_s[j]] : "") можно сократить до просто map[$1,$2], поскольку, если это нулевая строка (что вы сейчас тестируете), это то, что вы хотите напечатать в любом случае. - person Ed Morton; 05.04.2014
comment
Большое спасибо @EdMorton. Отличная обратная связь. Цени как всегда. - person jaypal singh; 05.04.2014

Это будет работать с любым awk и упорядочит вывод счетчиков в числовом порядке, сохраняя имена в том порядке, в котором они встречаются во входном файле:

$ cat tst.awk
BEGIN{FS="^"}

NR>1 {
    if (!seenNames[$1]++) {
        names[++numNames] = $1
    }

    if (!seenCodes[$2]++) {
        # Insertion Sort - start at the end of the existing array and
        # move everything greater than the current value down one slot
        # leaving open the slot for the current value to be inserted between
        # the last value smaller than it and the first value greater than it.
        for (j=++numCodes;codes[j-1]>$2+0;j--) {
            codes[j] = codes[j-1]
        }
        codes[j] = $2
    }

    count[$1,$2] = $3
}

END {
    printf "%s", "Name"
    for (j=1;j<=numCodes;j++) {
        printf "%s%s",FS,codes[j]
    }
    print ""

    for (i=1;i<=numNames;i++) {
        printf "%s", names[i]
        for (j=1;j<=numCodes;j++) {
            printf "%s%s",FS,count[names[i],codes[j]]
        }
        print ""
    }
}

...

$ awk -f tst.awk file
Name^0013^0018^0023^0025^0029^0038^0053^0060
Name1^^^^^1^1^1^
Name2^3^3^5^1^1^1^1^
Name3^^1^^^^^^1
Name4^^2^^5^^^^
Name5^^2^^1^^^^1
person Ed Morton    schedule 05.04.2014
comment
+1: То, как вы сортируете второй столбец, просто великолепно. Если у вас будет возможность, не могли бы вы объяснить это? Мне все еще трудно следить за ним (извините!). - person jaypal singh; 05.04.2014
comment
@JS 웃 спасибо, но это просто старая простая сортировка вставкой (см. en.wikipedia.org/wiki/Insertion_sort за красивую графику работы). Я добавил комментарий. - person Ed Morton; 05.04.2014

Поскольку у вас есть только два «измерения», достаточно просто использовать один массив для каждого измерения и объединяющий массив с вычисляемым именем столбца. Я не занимался сортировкой столбцов или строк, но идея довольно проста.

#!/usr/bin/awk -f
#
BEGIN { FS = "^" }
(NR == 1) {next}

{
    rows[$1] = 1
    columns[$2] = 1
    join_table[$1 "-" $2] = $3
}

END {
    printf "Name"
    for (col_name in columns) {
        printf "^%s", col_name
    }
    printf "\n"
    for (row_name in rows) {
        printf row_name
        for (col_name in columns) {
            printf "^%s", join_table[row_name "-" col_name]
        }
        printf "\n"
    }
}
person D.Shawley    schedule 05.04.2014
comment
Никогда не используйте входные данные в поле формата printf, так как это приведет к загадочной ошибке, если входные данные содержат символы форматирования printf. Так, например, сделайте printf "^%s", col_name вместо printf "^" col_name. Также см. Мой комментарий к JS о SUBSEP. - person Ed Morton; 05.04.2014
comment
@EdMorton - спасибо ... Я не уверен, почему я не подумал об этом, увидев довольно много дампов ядра в коде C по аналогичным причинам. - person D.Shawley; 07.04.2014