헤더를 일치시켜 열을 하나의 파일로 병합

헤더를 일치시켜 열을 하나의 파일로 병합

여러 행과 열이 있는 파일을 가져와 일치하는 헤더가 있는 열의 값을 가로로 추가(합계)하고 싶습니다. 내 파일(이 예제 아님)에서는 처음 9개 열과 첫 번째 행을 무시합니다. 결과에 영향을 주지 않고 인쇄되기를 원하기 때문입니다.

열은 반복되는 순서가 아니며 예제보다 더 많은 열이 있습니다.

아이디어는 다음과 같습니다.

입력하다:

var x y x y x y
a 1 0 1 1 0 1
b 1 1 0 0 1 1
c 1 1 0 0 0 0

산출:

var x y
a 2 2
b 2 2
c 1 1

내가 지금까지 얻은 것은 다음과 같습니다 ...

awk -F '\t' '{FS==OFS} FNR==1; FNR>1 {for (i=10; i<=NF; i++) {} print}' FILE.tsv > FILE_norepcols.tsv

또한 가능하다면 이 코드를 더 잘 작성하는 방법을 이해하려고 노력 중이므로 변경할 수 있거나 변경해야 하는 사항을 제안해 주세요.

답변1

$ perl -lane '$,="\t";
   print(qw/var x y/),next if $. == 1;
   push @A, shift @F;
   $A[$|--+1] += $_ for @F;
   print splice @A;
' file

결과:

var x   y
a   2   2
b   2   2
c   1   1

가정:

  • 필드 수가 홀수입니다.
  • 필드#2 및 후속 필드는 모두 숫자입니다.

설명하다(간단히):

  • OP에 표시된 대로 제목이 명시적으로 인쇄됩니다.

  • @A이 함수를 사용하여 배열을 인쇄할 때 배열을 지워서 각 행의 배열을 다시 채웁니다 splice.

  • 배열은 0인덱싱된 @F입력 레코드의 필드를 저장합니다 .$_

  • 배열의 첫 번째 요소(0번째 요소가 아님)가 @A배열의 앞쪽에서 이동됩니다 @F. 에서 새 레코드를 읽을 때 마다 @F배열이 생성됩니다 perl. 이는 $1, $2, $3, ..., $NF의 필드 와 유사합니다 awk.

  • 배열의 나머지 부분은 배열의 해당 합계를 누적하는 @A이진 요소 인덱스입니다 .(0|1)+1=>(1|2)@F

그것이 분명하기를 바랍니다.

답변2

상황을 이해하는 데 도움이 되도록 상당히 긴 변수 이름과 중간 변수를 사용하십시오(각 줄을 주석 처리하거나 나중에 설명을 추가하는 대신).

$ cat tst.awk
BEGIN { FS=OFS="\t" }
NR==1 {
    for (inFldNr=2; inFldNr<=NF; inFldNr++) {
        fldName = $inFldNr
        if ( !(fldName in fldName2outFldNr) ) {
            outFldNr2name[++numOutFlds] = fldName
            fldName2outFldNr[fldName] = numOutFlds
        }
        outFldNr = fldName2outFldNr[fldName]
        out2inFldNrs[outFldNr,++numInFlds[outFldNr]] = inFldNr
    }

    printf "%s%s", $1, OFS
    for (outFldNr=1; outFldNr<=numOutFlds; outFldNr++) {
        outFldName = outFldNr2name[outFldNr]
        printf "%s%s", outFldName, (outFldNr<numOutFlds ? OFS : ORS)
    }
    next
}
{
    printf "%s%s", $1, OFS
    for (outFldNr=1; outFldNr<=numOutFlds; outFldNr++) {
        sum = 0
        for (inFldIdx=1; inFldIdx<=numInFlds[outFldNr]; inFldIdx++) {
            inFldNr = out2inFldNrs[outFldNr,inFldIdx]
            sum += $inFldNr
        }
        printf "%s%s", sum, (outFldNr<numOutFlds ? OFS : ORS)
    }
}

.

$ awk -f tst.awk file
var     x       y
a       2       2
b       2       2
c       1       1

답변3

GNU 사용 datamash:

<file datamash -W transpose | 
  datamash -W --headers -s -g1 sum 2-4 |
  datamash --output-delimiter=' ' transpose
  • 공백을 구분 기호로 사용하여 파일 바꾸기( -W)
  • 첫 번째 필드를 -g1그룹화하고 ( ), -s그룹화하기 전에 정렬하고( ), 2~4 필드의 값을 합산합니다. 첫 번째 입력 행은 열 머리글로 처리되어 출력( --headers) 에 인쇄됩니다.
  • 결과를 바꾸고 탭 문자 대신 공백 문자를 구분 기호로 사용합니다.

산출:

GroupBy(var) x y
sum(a) 2 2
sum(b) 2 2
sum(c) 1 1

결과를 인쇄하려면 다음 명령을 사용하십시오 sed.

<file datamash -W transpose | 
  datamash -W --headers -s -g1 sum 2-4 |
  datamash --output-delimiter=' ' transpose | 
  sed 's/^[^(]*(\([^)]*\))/\1/'

산출:

var x y
a 2 2
b 2 2
c 1 1

다음 cut명령을 사용하여 입력 파일에서 처음 9개 열을 제거하고 결과에 다시 추가할 수 있습니다 paste.

paste -d ' ' <(cut -d' ' -f-9 file) <(cut -d' ' -f10- file | datamash ... )

head -n1print를 사용하고 추가 헤더 줄을 건너뛸 수 있습니다 tail -n+2.

{
  head -n1 file 
  paste -d ' ' <(tail -n+2 file | cut -d' ' -f-9) <(tail -n+2 file | cut -d' ' -f10- | datamash ... )
} 

관련 정보