AWK가 없는 멀티패스 방식

AWK가 없는 멀티패스 방식

오늘 저는 다른 웹사이트에서 쉘 스크립트에 관한 흥미로운 질문을 발견했습니다. 문제는 다른 행렬의 값을 기반으로 행렬을 구성하는 것입니다.

https://askubuntu.com/questions/884372/count-number-in-two-columns-and-generate-matrix

원래 행렬은 다음과 같습니다.

joe it 9
wolf it 10
wolf pr 9
mark pm 6
jack pr 20
anton pm 5
joe pm 20
mark sa 35

출력 행렬은 다음과 같아야합니다

0 anton jack joe mark wolf
it 0 0 9 0 10
pm 5 0 20 6 0
pr 0 20 0 0 9
sa 0 0 0 35 0

보시다시피 첫 번째 행렬이 두 번째 행렬의 요약이라면

나는 다음과 같이 문제를 해결하려고 노력합니다.

첫 번째 행렬을 test01.txt라는 파일에 저장했습니다. 구분 기호로 공백을 사용합니다.

첫 번째 열에서 고유 요소를 추출하여 행으로 변환하고 new라는 파일에 저장합니다.

cut -d ' ' -f1-1 test01 |sort |uniq |awk  '{ printf( "%s ", $1 ); } END { printf( "\n" ); }' > new

두 번째 열의 고유 요소를 추출하여 new2라는 파일에 저장합니다.

cut -d ' ' -f2-2 test01|sort|uniq > new2

new의 첫 번째 요소로 0을 추가하고 new1로 저장합니다.

while read line; do echo "0 $line"; done < new > new1

파일의 첫 번째 열에는 5개의 서로 다른 요소가 있으므로 교차 비교를 위해 new2 파일의 각 행에 5개의 0이 추가됩니다.

while read line; do echo "$line 0 0 0 0 0"; done < new2 > new3

그리고 new3의 내용을 new1 파일의 끝에 추가합니다.

while read line; do echo $line |awk '{print $1,$2}'; done < new1

이제 new1의 내용은 다음과 같습니다.

0 anton jack joe mark wolf
it 0 0 0 0 0
pm 0 0 0 0 0
pr 0 0 0 0 0
sa 0 0 0 0 0

그때 내가 막혔어요.

new1의 행렬을 살펴보고 필요한 경우 0을 바꿀 수 있도록 각 요소를 test01의 행과 비교하는 방법을 모르겠습니다. 최종 결과는 다음과 같아야 합니다.

0 anton jack joe mark wolf
it 0 0 9 0 10
pm 5 0 20 6 0
pr 0 20 0 0 9
sa 0 0 0 35 0

어쩌면 그렇게 많은 중간 파일 없이 지금까지 사용해 본 것보다 결과를 얻는 더 효율적인 방법이 있을 수도 있습니다.

글이 길어져서 죄송합니다

답변1

AWK가 없는 멀티패스 방식

먼저 파일을 읽어 행 및 열 레이블을 추출합니다. 그런 다음 0과 첫 번째 행의 각 열 레이블이 인쇄됩니다.

이 루프는 레이블이 없는 행 처리를 담당합니다. 먼저 행 레이블을 인쇄한 다음 파일에서 해당 (행, 열) 쌍과 일치하는 모든 항목을 검색합니다. dc여러 행이 반환되면 이 결과의 세 번째 열에 있는 항목이 추가됩니다.

이 접근 방식의 명백한 문제는 파일을 읽는다는 것입니다.항목 당 한 번결과 매트릭스에서. 따라서 행 레이블과 열 레이블을 얻은 처음 두 번을 계산하면 예제는 22번 읽혀집니다!

호출 방법은 다음과 같습니다 ./contingency-table input-file.

#!/bin/sh
# file: contingency-table

columns=$(cut -d' ' -f 1 "$1" | sort | uniq)
rows=$(cut -d' ' -f 2 "$1" | sort | uniq)

printf '0'
printf ' %s' ${columns}
printf '\n'

for row in ${rows}; do
  printf "${row} "
  for col in ${columns}; do
    (grep "${col} ${row}" "${1}" \
     | cut -d' ' -f 3            \
     | tr '\n' '+'
     printf '\n')                \
    | sed -e 's/^/0 /'           \
          -e 's/$/pq/'           \
    | dc                         \
    | tr '\n' ' '
  done
  printf '\n'
done

AWK를 사용하는 보다 효율적인 방법

#!/usr/bin/awk -f

function max(val1, val2) {
    return ((val1 > val2) ? val1 : val2)
}

BEGIN {
    name_length = 0
    department_length = 0
    # This line influences sorting in GNU awk
    PROCINFO["sorted_in"] = "@ind_str_asc"
}

(!($1 in names)) {
    names[$1]
    name_length = max(length($1), name_length)
}

(!($2 in departments)) {
    departments[$2]
    department_length = max(length($2), department_length)
}

{
    hours[$2, $1] += $3
}

END {
    printf "%" department_length "s", 0
    for (name in names) {
        printf " %" name_length "s", name
    }
    printf "\n"
    for (department in departments) {
        printf "%" department_length "s", department
        for (name in names) {
            printf " %" name_length "d", hours[department, name]
        }
        printf "\n"
    }
}

시작 블록은 일부 변수를 설정하고 배열 순회를 정렬하도록 GNU awk를 구성합니다. 다음 두 블록은 입력을 스캔하는 동안 필요에 따라 이름과 부서를 추가합니다. 세 번째 블록은 각 누계를 계산합니다.

"사람이 읽을 수 있는" 형식을 원하지 않으면 이 …_length = max(…줄을 주석 처리하세요.

END블록은 이전에 생성된 배열을 반복하여 모든 출력 및 형식화가 발생하는 곳입니다. 이를 통해 출력 테이블의 각 항목에 대해 하나의 전달이 아닌 입력 파일에 대해 하나의 전달이 허용됩니다.

관련 정보