한 행의 열이 다른 행의 열과 일치하는 경우 다음을 추가하세요.

한 행의 열이 다른 행의 열과 일치하는 경우 다음을 추가하세요.

같은 표의 첫 번째 열에 주어진 항목을 기준으로 아래 표의 열에 나열된 수치 값을 합산하고 싶습니다. 표의 내용은 다음과 같습니다.

10,Mumbai,0,4,5,0,6,3,55,M
2,Mumbai,1,3,2,0,4,4,4,M
4,Chennai,5,6,7,8,9,0,6,F

예상 결과는 다음과 같습니다(두 번째 및 마지막 열로 그룹화된 데이터).

12,Mumbai,1,7,7,0,10,7,59,M
4,Chennai,5,6,7,8,9,0,6,F

Linux에서 awk를 사용하여 이 출력을 얻으려면 어떻게 해야 합니까?

답변1

$ cat tst.sh
#!/usr/bin/env bash

awk '
    BEGIN { FS=OFS="," }
    $2 != vals[2] {
        if ( NR>1 ) {
            prt()
        }
        split($0,vals)
        next
    }
    {
        for ( i=1; i<=NF; i++ ) {
            if ( $i+0 == $i ) {
                vals[i] += $i
            }
        }
    }
    END {
        prt()
    }
    function prt(    i) {
        for (i=1; i<=NF; i++) {
            printf "%s%s", vals[i], (i<NF ? OFS : ORS)
        }
    }
' "${@:--}"

$ ./tst.sh file
12,Mumbai,1,7,7,0,10,7,59,M
4,Chennai,5,6,7,8,9,0,6,F

입력 파일이 아직 두 번째 필드로 그룹화되지 않은 경우(게시한 입력 예에서와 같이) 다음을 변경하십시오.

awk '...' "${@:--}"

이와 관련하여:

sort -t',' -k2,2 "${@:--}" | awk '...'

답변2

두 번째 열을 키로 사용하고 출력하는 동안 레코드 순서를 유지하면서 이 작업을 수행할 수 있습니다.

awk -F, -v OFS=, '!seen[$2]++{ recNr++ }
{ for(i=1; i<=NF; i++)
      if(i!=2 && i!=NF)
          sumCol[recNr, i, $2]+= $i
      else 
          sumCol[recNr, i, $2]= $i (i==NF?ORS:"")
}

END{ for (key in sumCol){
         if(sumCol[key]!=""){
             recNumbr++; sep=""
             split(key, tmp, SUBSEP)
             for(j=1; j<=NF; j++){
                 printf ("%s", sep sumCol[recNumbr, j, tmp[3]])
                 sep=OFS
                 delete sumCol[recNumbr, j, tmp[3]]
             }
         }
     }
}' infile

답변3

GNU 사용 datamash:

$ datamash -s -t , groupby 2,10 sum 1,3-9 <file | datamash -t , cut 3,1,4-10,2
4,Chennai,5,6,7,8,9,0,6,F
12,Mumbai,1,7,7,0,10,7,59,M

이는 datamash열 1과 열 3~9를 합산하고 입력을 열 2와 10의 조합으로 그룹화합니다.

datamash그룹화된 열이 출력에서 ​​먼저 출력되므로 두 번째 단계를 수행하여 원래 datamash순서로 재정렬합니다.

출력은 그룹화 열을 기준으로 정렬되므로 Chennai이전에 입력됩니다 Mumbai. 원본 데이터가 이미 정렬되어 있는 경우 -s명령에서 제거합니다.

다른 예시:

$ cat file
10,Mumbai,0,4,5,0,6,3,55,M
2,Mumbai,1,3,2,0,4,4,4,M
4,Chennai,5,6,7,8,9,0,6,F
4,Chennai,5,6,7,8,9,0,6,F
4,Chennai,5,6,7,8,9,0,6,M
$ datamash -s -t , groupby 2,10 sum 1,3-9 <file | datamash -t , cut 3,1,4-10,2
8,Chennai,10,12,14,16,18,0,12,F
4,Chennai,5,6,7,8,9,0,6,M
12,Mumbai,1,7,7,0,10,7,59,M

답변4

사용행복하다(이전 Perl_6)

~$ raku -e 'my %class = lines.classify(*.split(",").[1, *-1].join("\t"), as => {$_.split(",").[ 0,2..*-2 ][*;*]});  \
            for %class.kv -> $k,$v {say $k => $v.elems > 1 ?? [Z+] $v<> !! $v[*;*]};'   file

OP는 이 문제를 해결하기 위해 Perl 언어 계열을 사용하는 것을 고려할 수 있습니다. 위의 내용은 Raku를 사용하는 한 가지 방법만을 나타냅니다. 즉, 두 번째 및 마지막 열( 쉼표 뒤의 색인) 을 lines읽고 편집합니다. 이 열 정보는 분류자에도 유지되는 경우 중복되므로 이 매개변수는 구성 요소에서 숫자가 아닌 두 개의 열을 제거하는 데 사용됩니다 . 데이터는 해시에 저장됩니다 .classify[1, *-1]splitkeyvalueasclassifyvalue%class

여기에서 쌍은 키/값 구성 요소 %class로 분할되고 인쇄 되며 Raku의 삼항 연산자를 사용하여 테스트되어 여러 요소가 포함되어 있는지 확인합니다 . 여러 요소가 발견되면 열이 합산되어 사용됩니다 (컨테이너화되지 않은 다음 요소별 합산). 요소가 하나만 있는 경우 열이 제거됩니다 ( 합산되지 않고 평면화만 됨).kvkeyvalueelemsput[Z+] $v<>put$v[*;*]

입력 예:

10,Mumbai,0,4,5,0,6,3,55,M
2,Mumbai,1,3,2,0,4,4,4,M
4,Chennai,5,6,7,8,9,0,6,F
4,Chennai,5,6,7,8,9,0,6,F
4,Chennai,5,6,7,8,9,0,6,M

출력 예(탭으로 구분 keys):

Chennai M => (4 5 6 7 8 9 0 6)
Chennai F => (8 10 12 14 16 18 0 12)
Mumbai  M => (12 1 7 7 0 10 7 59)

Raku에서는 쉼표로 구분된 출력이 확실히 가능하지만 아래 답변에서는 단순화를 위해 두 개의 "그룹화" 열을 열 1과 열 2로 추상화합니다.

~$ raku -e 'my %class = lines.classify(*.split(",").[1, *-1].join(","), as => {$_.split(",").[ 0,2..*-2 ][*;*]});  \
            for %class.kv -> $k,$v {put $k ~","~ ($v.elems > 1 ?? [Z+] $v<> !! $v[*;*]).join(",")};'  file
Chennai,F,8,10,12,14,16,18,0,12
Mumbai,M,12,1,7,7,0,10,7,59
Chennai,M,4,5,6,7,8,9,0,6

마지막으로 더 넓은 샘플 입력 데이터 세트를 제공한 @Kusalananda에게 감사드립니다.

https://docs.raku.org/routine/classify
https://docs.raku.org/언어/operators#index-entry-operator_ternary
https://raku.org

관련 정보