2014-04-05 1 views
3

배열에서 열 인덱스로 요소를 변환하고 각 열 인덱스에 대해 $ 3 값을 반환해야합니다. gawk 4에 액세스 할 수 없으므로 실제 다차원 배열로 작업 할 수 없습니다.AWK의 피벗 테이블

입력

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 

실제 다차원 배열을 사용하지 않고이 작업을 해결하는 방법에 대한 어떤 제안이?

+1

BTW - 원하는 출력의 마지막 행이 잘못되었다고 생각합니다. 'Name5 ^^ 2 ^^ 1 ^^^^ 1' –

답변

1

두 개의 "차원"만 있으므로 각 차원에 대해 하나의 배열을 사용하고 계산 된 열 이름을 사용하여 조인 할 수 있습니다. 나는 열이나 행을 정렬하지 않았지만 그 아이디어는 꽤 기초적이다.

#!/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" 
    } 
} 
+0

printf 형식 필드에 입력 데이터를 사용하지 마십시오. 입력 데이터에 printf 형식 문자가 포함되어있을 때 암호가 잘못되어 실패하게됩니다. 예를 들어,'printf "^"col_name' 대신에'printf "^ % s", col_name'을 수행하십시오. 또한 SUBSEP에 대한 JS에 대한 내 의견을 참조하십시오. –

+1

@ Edmonton - 고마워. 왜 비슷한 이유에서 C 코드에서 꽤 많은 코어 덤프를보고 난 후에 왜 그렇게 생각하지 않았는지 모르겠습니다. –

3

다음 솔루션은 정렬을 위해 GNU awk v3.2 기능을 사용합니다. 이것은 다차원 배열을 사용하지 않습니다. 그것은 단지 하나를 시뮬레이트합니다. 이것은 어떤 AWK와 함께 작동하고 당신의 입력 파일에서 발생 순서대로 이름을 유지하면서 수치 계산의 출력을 주문합니다

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 
+1

+1 Awk에는 SUBSEP이라는 내장 변수가 있습니다. pseudo-2d 배열 색인을 쉼표로 구분할 때'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]'로 줄일 수 있습니다. 그것은 당신이 현재 테스트하고있는 null 문자열입니다. 그것은 당신이 어쨌든 인쇄하기를 원하는 것입니다. –

+1

고맙습니다. @ EdMorton. 훌륭한 피드백. 언제나처럼 그것을 이해하십시오. –

3

:

$ 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 
+1

+1 : 두 번째 열을 정렬하는 방식이 훌륭합니다. 기회가 생기면 설명을 해 주시겠습니까? 나는 아직도 그것을 따라가는 데 어려움을 겪고있다 (미안하다!). –

+1

@JS 감사합니다.하지만 그냥 평범한 이전 삽입 정렬 일뿐입니다 (http://en.wikipedia.org/wiki/Insertion_sort에서 멋진 그래픽을 볼 수 있습니다). 나는 코멘트를 추가했다. –

+1

+1 우아한 접근 방식 Ed Mortan –