AWK를 사용한 블록 합산(모드 변경 시 합산 다시 시작)

AWK를 사용한 블록 합산(모드 변경 시 합산 다시 시작)

다음과 같은 파일이 있습니다.

A 100
A 200
A 300 #sum=600
B 400
B 500 #sum=900
A 600
A 700
A 800 #sum=2100

나는 출력을 다음과 같이 원한다:

A 600
B 900
A 2100
C sum_of_C
D sum_of_D

for, 및 sed를 사용하여 grep이 작업을 수행 할 수 있습니다 awk.

그런데 공부 중이니까 대본 awk을 쓰고 싶어요 awk. 지금까지 나는 다음을 가지고 있습니다 :

if (${NR {print $1}} == ${NR-1 {print $1}}) 
  sum+=$2
  print $0"\t"sum
else
  sum=$2
  print $0"\t"sum

awk -f awkscript file성공하지 못했습니다. 해결책은 무엇입니까?

답변1

if당신이 그곳에서 무엇을 하고 싶은지 잘 모르겠습니다 . 목표인 경우 필드 수 NR에 사용되는 레코드 수입니다 . 이런 것의 중간에 블록을 NF놓을 수는 없습니다 .{}

귀하의 목표는 이 행의 필드 값을 이전 행의 필드 값과 비교하고 새로운 데이터 "그룹"에 도달하면 합계를 인쇄하는 것입니다. 그렇다면 이 스크립트는 귀하가 원하는 것을 수행할 것이며 귀하의 목표와 거의 동일하다고 생각합니다.

{
    if (last && $1 != last) {
        print last, sum
        sum = 0
    }
    sum = sum + $2
    last = $1
}
END {
    print last, sum
}

last이전 행의 첫 번째 필드( ) 값을 보유할 새 변수를 만듭니다 . $1우리는 이것을 보고 우리가 보고 있는 그룹을 추적하는 데 사용할 것입니다.

  • { ... }각 행에 대해( 최상위 수준에 있으므로 ) 먼저 a) last가 설정되어 있는지(첫 번째 행에 아무 것도 인쇄하지 않기 때문에) b) 첫 번째 필드의 값이 last. 그렇다면 값 last, 공백(because ,) 및 sum계산한 내용을 인쇄합니다. (탭을 원하시면 "\t"아까처럼 따옴표로 묶어서 사용하세요)
  • 인쇄 후 sum0으로 재설정됩니다.
  • $2어느 쪽이든 두 번째 필드( )의 값을 에 추가합니다 sum.
  • 각 행에 대해 첫 번째 필드(그룹)를 저장하여 last다음 행의 비교에 사용할 수 있습니다.
  • 마지막으로 마지막 그룹을 인쇄하려고 합니다. 이를 위해 우리는 END { ... }블록을 사용합니다. 데이터가 부족하면 프로그램이 끝날 때 실행됩니다. 이전과 같이 합계와 함께 작업 중인 그룹을 인쇄합니다.

내가 실행하면 :

awk -f sum.awk < data

귀하의 데이터 파일을 사용하여 다음과 같은 결과를 얻습니다.

A 600
B 900
A 2100

예상대로.


awk에서든 다른 방식으로든 이 작업을 수행하는 더 쉬운 방법이 있습니다. 특히 위의 본문을 다음으로 바꿀 수 있습니다.

last && $1 != last {
    print last, sum
    sum = 0
}
{
    sum = sum + $2
    last = $1
}

여기서는 명시적인 테스트 대신 awk의 조건부 블록 구문을 사용합니다 if. 프로그램은 위와 동일하게 동작하지만 더 관용적입니다. 이 예에는 큰 차이가 없지만 awk를 배우고 있는지 아는 것이 유용합니다.


#sum=제공한 파일 예제가 실제로 줄(또는 이와 유사한 것)이 있는 파일의 예제인 경우 다음 스크립트를 사용할 수 있습니다.

{
    sum = sum + $2
    if (NF == 3) {
        print $1, sum
        sum = 0
    }
}

각 행에 대해 두 번째 필드의 값을 sum변수에 추가합니다. 정확히 세 개의 필드( NF == 3)가 포함된 줄에서 합계를 인쇄하고 sum0으로 재설정합니다.

답변2

파일이 모든 합계를 메모리에 담을 수 있을 만큼 작은 경우 다음과 같이 간단한 작업을 수행할 수 있습니다.

$ awk '{sum[$1]+=$2}END{for(pat in sum){print pat,sum[pat]}}' file 
A 2700
B 900

이는 주석 스크립트와 동일합니다 awk.

#!/usr/bin/awk -f

{
    ## Here, we use $1 as the key of an associative array
    ## and increment its current value by $2. The result of 
    ## this will be an array element for each different $1 in 
    ## the file whose value will be the sum of all associated $2s.
    sum[$1]+=$2
}

## The END{} block is exacuted after the entire file
## has been processed.
END{
    ## Iterate through the keys of the array (the $1s),
    ## saving each as 'pat'. Then, print the current value of
    ## 'pat' as well as the associated value (the sum) from
    ## the array.
    for(pat in sum){
        print pat,sum[pat]
    }
}

이 접근 방식에서 발생할 수 있는 유일한 문제는 행이 너무 많으면 s 배열을 유지하면 $1메모리가 부족해진다는 것입니다. 현대 시스템에서는 이런 일이 일어날 가능성이 거의 없습니다. 반면에 이 방법은 정렬되지 않은 파일을 처리할 수 있으므로 파일의 줄이 순서대로 정렬되지 않은 경우에도 작동합니다.

관련 정보