2열과 3열을 기준으로 쉼표로 구분된 텍스트 파일을 병합하여 요약하고 4열의 평균을 계산합니다.

2열과 3열을 기준으로 쉼표로 구분된 텍스트 파일을 병합하여 요약하고 4열의 평균을 계산합니다.

모든 이벤트와 크기를 보고하는 파일이 있습니다. 위 행의 열 3이 아래 행의 열 2와 동일한 것으로 식별할 수 있는 연속 이벤트를 병합하여 해당 정보를 요약하려고 합니다(열 1이 동일한 경우). 동일한 형식이고 연속 문자열이 여러 행인 경우 열 2가 해당 열의 그룹화에서 가장 낮은 숫자이고 열 3이 해당 열의 그룹화에서 가장 높은 숫자이고 열이 단일 행으로 대체됩니다. 4는 열 4에 있는 모든 숫자의 평균(가장 가까운 정수로 반올림됨)이 됩니다.

명확하게 말하면 첫 번째 열은 특정 그룹, 두 번째 열은 시작 위치, 세 번째 열은 종료 위치, 네 번째 열은 발생 횟수입니다.

열 조합은 각 행마다 고유하며 열은 열 1, 열 2, 열 3을 기준으로 미리 정렬되어 있습니다. 열 2의 숫자는 동일해서는 안 됩니다/동일하지 않아야 하며, 열 3의 숫자는 동일해서는 안 됩니다/동일하지 않아야 합니다. 첫 번째 열은 동일할 수 있으며 일반적으로 동일합니다.

가능하다면 awk에서 이것을 달성하려고 노력하고 있지만 시도했지만 실패했습니다. 내가 시도한 것은 다음과 같습니다.

awk 'BEGIN {OFS=","} NR==1 {print} NR>1 {if ($1==prev && $2==end+1) {sum+=$4; count++; end=$3} else {if (NR>2) {print prev, start, end, int(sum/count+0.5);}; prev=$1; start=$2; end=$3; sum=$4; count=1}} END {print prev, start, end, int(sum/count+0.5)}'
###### reformatted via "awk -o- '...script_body...'"

awk '
BEGIN {
        OFS = ","
}

NR == 1 {
        print
}

NR > 1 {
        if ($1 == prev && $2 == end + 1) {
                sum += $4
                count++
                end = $3
        } else {
                if (NR > 2) {
                        print prev, start, end, int(sum / count + 0.5)
                }
                prev = $1
                start = $2
                end = $3
                sum = $4
                count = 1
        }
}

END {
        print prev, start, end, int(sum / count + 0.5)
}'

입력 예:

fgh1,45513382,45513383,43
fgh1,45513383,45513384,44
fgh1,45513384,45513385,44
fgh1,45513385,45513386,43
fgh1,45513386,45513387,43
fgh1,45513387,45513388,44
fgh2,63543512,63543513,44
fgh2,63543513,63543514,41
fgh2,63543514,63543515,44
fgh2,63543515,63543516,44

출력 예:

fgh1,45513382,45513388,44
fgh2,63543512,63543516,43

답변1

열 1은 동일하지만 세 번째 열과 다음 행의 두 번째 열 사이에 간격이 있는 입력 행 몇 개를 추가합니다.

$ cat raw.dat
fgh1,45513382,45513383,43
fgh1,45513383,45513384,44
fgh1,45513384,45513385,44
fgh1,45513385,45513386,43
fgh1,45513386,45513387,43
fgh1,45513387,45513388,44
fgh2,63543512,63543513,44
fgh2,63543513,63543514,41
fgh2,63543514,63543515,44
fgh2,63543515,63543516,44            # 3rd column (current line) does not match ...
fgh2,63543524,63543525,20            # 2nd column (next line)
fgh2,63543525,63543526,60

노트:OP의 설명과 샘플 데이터에서 이런 일이 발생할 수 있는지 여부가 명확하지 않습니다. 제안된 코드는 계속 작동해야 하며 || $2 != outcols[3]제거될 수 있습니다.

일반적인 접근:

  • 배열에서 다음 출력 열 세트를 추적합니다.
  • 조건이 일치하지 않으면 배열을 인쇄하고 배열을 지운 후 다음 출력 줄로 배열 채우기를 시작합니다.

아이디어 awk:

awk '
BEGIN { FS = OFS = "," }

function print_line () {

    if (outcols[1]) {                             # if we have something in outcols[1] then ...
       for (i=1;i<=3;i++)                         # loop through 1st three columns and ...
           printf "%s%s", outcols[i], OFS         # print to stdout then ...
       printf "%.0f\n", (sum / count)             # calculate/print average; let printf/.0f do the rounding
    }

    delete outcols                                # clear array
    sum = count = 0                               # reset counters
}

($1 != outcols[1]) ||
($2 != outcols[3])    { print_line() }

                      { if (! outcols[1]) {       # if nothing in outcols[1] then initialize 1st two output columns ...
                           outcols[1] = $1
                           outcols[2] = $2
                        }
                        outcols[3] = $3
                        sum += $4
                        count++
                      }

END                   { print_line() }            # flush last line to stdout

' raw.dat

그러면 다음이 생성됩니다.

fgh1,45513382,45513388,44
fgh2,63543512,63543516,43
fgh2,63543524,63543526,40

답변2

노력하는 모든 분들께 감사드립니다. 일부는 매우 가까워졌고 제 답변을 다시 생각하고 정리하는 데 도움이 되었습니다.

입력하려고 할 때에도 다음 솔루션이 작동하는 것 같습니다.

awk 'BEGIN {
    FS = ","
    OFS = ","
}

{
    if (NR == 1) {
        group = $1
        start = $2
        end = $3
        sum = $4
        count = 1
    } else if ($1 == group && $2 == end) {
        end = $3
        sum += $4
        count++
    } else {
        printf("%s,%d,%d,%d\n", group, start, end, int((sum + count / 2) / count))
        group = $1
        start = $2
        end = $3
        sum = $4
        count = 1
    }
}

END {
    printf("%s,%d,%d,%d\n", group, start, end, int((sum + count / 2) / count))
}'

원래 질문 입력:

fgh1,45513382,45513383,43
fgh1,45513383,45513384,44
fgh1,45513384,45513385,44
fgh1,45513385,45513386,43
fgh1,45513386,45513387,43
fgh1,45513387,45513388,44
fgh2,63543512,63543513,44
fgh2,63543513,63543514,41
fgh2,63543514,63543515,44
fgh2,63543515,63543516,44

원래 질문 출력:

fgh1,45513382,45513388,44
fgh2,63543512,63543516,43

2를 입력하세요:

fgh1,45513382,45513383,43
fgh1,45513383,45513384,44
fgh1,45513384,45513385,44
fgh1,45513385,45513386,43
fgh1,45513386,45513387,43
fgh1,45513387,45513388,44
fgh2,63543512,63543513,44
fgh2,63543513,63543514,41
fgh2,63543514,63543515,44
fgh2,63543515,63543516,44
fgh2,63543524,63543525,20
fgh2,63543525,63543526,60

출력 2:

fgh1,45513382,45513388,44
fgh2,63543512,63543516,43
fgh2,63543524,63543526,40

답변3

$1awk를 사용하고 한 번에 하나의 값만 메모리에 저장하십시오.

$ cat tst.awk
BEGIN { FS=OFS="," }
($1 != prev[1]) || ($2 != prev[3])  {
    prt()
    beg = $2
    cnt = sum = 0
}
{
    end  = $3
    sum += $4
    cnt ++
    split($0,prev)
}
END { prt() }
function prt() {
    if ( cnt ) {
        print prev[1], beg, end, int( (sum / cnt) + 0.5 )
    }
}

$ awk -f tst.awk orig_input
fgh1,45513382,45513388,44
fgh2,63543512,63543516,43

$ awk -f tst.awk input2
fgh1,45513382,45513388,44
fgh2,63543512,63543516,43
fgh2,63543524,63543526,40

위의 내용은 .5s를 반올림한다고 가정합니다.

답변4

테스트되지 않았지만 내 솔루션은 다음과 같습니다.

{
   AGG=$2 "," $3;
   if ( AGG == PREVAGG ) { 
      TOT+=$4;
      COUNT+=1;
   } else {
      if (PREVAGG) {
         print PREVAGG "," int(TOT/COUNT);
      }
      TOT=$4;
      COUNT=1;
      PREVAGG=AGG;
   }
}
END { print PREVAGG "," int(TOT/COUNT); }

가능하다면 이것을 awk로 정렬하려고 노력합니다.

입력 스트림의 레코드 순서를 참조하고 있습니까? 그렇다면 awk는 그다지 능숙하지 않습니다. 노력하다sort -k 1,2,3 -t','

코드가 파일의 헤더 행을 처리하는 것 같지만 예상한 출력에는 없습니다. 나는 이것을 간과했습니다.

어떻게 작동하나요?

AGG=$2 "," $3- $2와 $3가 변경되면 누적된 데이터를 출력하고 재설정합니다. 이를 복합 엔터티로 처리하면 일부 코드가 절약되고 복잡성이 줄어듭니다. 또한 다른 열을 고려하는 것도 간단합니다(S1의 값으로 수행해야 할 작업을 지정하지 않았습니다).

if ( AGG == PREVAGG ) {- 이 입력 행에 집계된 라벨이 이전 입력 행의 라벨과 동일합니까?

  • 그렇다면 계속해서 데이터를 집계하세요.
  • 그렇지 않은 경우 누산기와 마지막 행 레이블을 재설정하여 집계된 데이터를 출력합니다. 단, 이것이 첫 번째 입력 레코드가 아닌 경우에만 해당됩니다.

스크립트가 마지막 레코드에 도달한 후에는 데이터 출력을 트리거하기 위해 $2,$3에 변경 사항이 없습니다. 따라서 이는 END 블록에 명시적으로 설정됩니다.

관련 정보