특정 열을 기준으로 합계를 구합니다.

특정 열을 기준으로 합계를 구합니다.

저는 약 3천만에서 4천만 개의 데이터 항목에 달하는 엄청난 양의 데이터를 보유하고 있습니다. 우리는 이 파일들을 처리하여 다른 인터페이스 팀으로 보내야 합니다.

다음은 우리가 받은 파일 형식입니다.

c1  c2  c3  c4  c5  c6
A   B   C   D   5   s
A   B   C   D   4   s
A   B   E   F   5   s
A   B   E   F   8   S
C   D   E   F   9   S

출력 파일에 모든 열을 인쇄해야 합니다. 이는 GPRS 사용과 관련이 있으므로 그룹화해야 합니다.c1 - c4그런 다음 모든 것이 일치하면 다음을 요약해야 합니다.c5, 출력 파일의 모든 내용을 인쇄합니다.

다음은 샘플 출력 파일입니다.

c1  c2  c3  c4  c5  c6
A   B   C   D   9   s
A   B   E   F   13  s
C   D   E   F   9   s

또한 이 작업 흐름은 Unix 스크립팅보다 Perl에서 훨씬 빠르게 작동한다고 들었습니다.

답변1

perl@terdon의 답변과 유사하지만 더 나은 형식 출력을 제공하는 또 다른 솔루션은 다음과 같습니다.

$ perl -alne '
    (print && next) if $. == 1;   
    $h{"@F[0..3]"}{s} += $F[4];
    $h{"@F[0..3]"}{t}  = $F[5];
    END {
        for (keys %h) {
            printf "%-4s%-4s%-4s%-4s%-4s%-4s",split(" ",$_),$h{$_}{s},$h{$_}{t};                        
            printf "\n";
        }
    }' file
c1  c2  c3  c4  c5  c6
A   B   E   F   13  S   
A   B   C   D   9   s   
C   D   E   F   9   S

답변2

도구 선택과 관련하여 일반적으로 도구가 전문화될수록 속도가 빨라집니다. 따라서 , , , 등을 포함하는 파이프는 tr, , 보다 빠른 경향 이 있는 것보다 빠른 경향이 있는 것보다 빠른 경향이 있습니다 . 그러나 그것은 물론 작업에 따라 많이 달라집니다. Perl이 더 빠르다는 내용을 읽었다면 잘못 읽은 것입니다. 아니면 한 번에 한 줄씩 처리하는 쉘 루프와 비교한 것입니다(수백만 줄이 있는 파일의 경우 속도가 확실히 느려질 것입니다).cutgrepsortsedawkperlpythonruby

입력이 병합할 줄이 연속적인 형식인 경우 awk가 좋은 선택입니다(sed에서 추가를 수행하는 정상적인 방법은 없습니다).

awk -v OFS='\t' '                      # use tabs to separate output fields
    NR==1 {print; next}                # keep the first line intact
    function flush () {                # function to print a completed sum
        if (key != "") print previous, sum, more;
        sum=0
    }
    {key = $1 OFS $2 OFS $3 OFS $4}    # break out the comparison key
    key!=previous {flush()}            # if the comparison key has changed, print the accumulated sum
    {previous=key; sum+=$5; more=$6}   # save the current line
    END {flush()}                      # print the last 
'

라인이 연속적이지 않은 경우 정렬을 통해 연속적으로 만들 수 있습니다. 일반적인 sort구현은 고도로 최적화되어 있으며 고급 언어로 데이터 구조를 조작하는 것보다 빠릅니다.

sort | awk …

이는 열 구분 기호가 일관적이라고 가정합니다(예: 항상 탭). 그렇지 않은 경우 입력을 전처리하여 그렇게 되도록 하거나 sort -k1,1 -k2,2 -k3,3 -k4,4구분 기호를 고려하지 않고 이러한 특정 필드를 비교하는 데 사용하십시오.

답변3

이렇게 하면 시작할 수 있습니다.

perl -ane '$h{"@F[0 .. 3]"} += $F[4] }{ print "$_ $h{$_}\n" for keys %h' input-file

수행할 작업을 지정하지 않았기 때문에 마지막 열은 인쇄되지 않습니다. 또한 헤더 라인을 올바르게 처리하지 못하지만 수정하기 쉬워야 합니다.

답변4

내가 당신을 올바르게 이해한다면 다음과 같은 것을 원할 것입니다.

$ perl -lane 'if($.>1){$k{"@F[0..3]"}{sum}+=$F[4]; $k{"@F[0..3]"}{last}=$F[5]}
              else{print "@F"}
              END{
                foreach (keys(%k)){ print "$_ $k{$_}{sum} $k{$_}{last}"}
              }' file
c1 c2 c3 c4 c5 c6
C D E F 9 S
A B E F 13 S
A B C D 9 s

이렇게 하면 열 정렬이 유지되지 않습니다. 이것이 문제인지는 모르겠습니다. 그러나 헤더를 올바르게 처리하고 필요한 출력을 생성해야 합니다.

설명

  • perl -lane: -l각 문자열의 끝에서 개행 문자를 제거하고 이를 각 print명령문에 추가합니다. 각 입력 행을 공백 필드로 분할 a하고 해당 필드를 배열에 저장합니다 @F. 수단n입력 파일을 한 줄씩 읽고 주어진 스크립트를 적용하십시오.-e.

다음은 주석 처리된 스크립트 형식의 동일한 한 줄입니다.

#!/usr/bin/env perl

## This is the equivalent of perl -ne
## in the one-liner. It iterates through
## the input file.
while (<>) {
    
    ## This is what the -a flag does
    my @F=split(/\s+/);
    ## $. is the current line number.
    ## This simply tests whether we are on the
    ## first line or not.
    if ($.>1) {
    ## @F[0..3] is an array slice. It holds fields 1 through 4.
    ## The slice is used as a key for the hash %k and the 5th
    ## field is summed to $k{slice}{sum} while the last column is 
    ## saved as $k{slice}{last}.
    $k{"@F[0..3]"}{sum}+=$F[4]; $k{"@F[0..3]"}{last}=$F[5];
    }

    ## If this is the first line, print the fields.
    ## I am using print "@F" instead of a simple print 
    ## so that all lines are formatted in the same way.
    else {
    print "@F\n";
    }
}

## This is the same as the END{} block
## in the one liner. It will be run after
## the whole file has been read.

## For each of the keys of the hash %k
foreach (keys(%k)){ 
    ## Print the key ($_, a special variable in Perl),
    ## the value of $k{$key}{sum} (the summed values),
    ## and the last column.
    print "$_ $k{$_}{sum} $k{$_}{last}\n"
}


    

관련 정보