헤더를 기준으로 열 합계

헤더를 기준으로 열 합계

다음과 같이 탭으로 구분된 파일1이 있습니다(표시 하위 집합, 실제 행렬 60x60000).

rowname   header1 header2 header3 header4 header5 header6 header7 header8
rowname1    1       1        10       2     3       1       10       2 
rowname2    0       7        200      3     37      1        2       1 

다음과 같은 또 다른 file2가 있습니다.

header1,header2
header3
header4,header5
header6,header8
header7

file2의 각 행에 지정된 열을 합산하고 싶습니다.

rowname    header1 header3 header4 header6 header7 
rowname1    2        10        5     3       10
rowname2    7        200       40    2       2

즉, 1열+열2, 3열, 4열+5열, 6열+8열, 7열...

합산해야 하는 열도 있고 그렇지 않은 열도 있으며, 합산할 열이 항상 연속적인 것은 아닙니다.

열을 합산하는 경우 첫 번째 열의 헤더가 출력 파일에 유지되어야 합니다.

해결책이 있는지 궁금합니다. 지금까지는 헤더 항목을 저장하는 방법만 알고 있습니다.

awk '
NR==1 {
    for (i=1; i<=NF; i++) {
        f[$i] = i
    }

답변1

awk '
  FNR==NR{
    newhdr[FNR]=$1       # new header name
    newhdrcnt++          # number of new header names
    for (i=1;i<=NF;i++)
      hdrnames[FNR]=$0   # save new header names comma-separated
    next
  }
  FNR==1{
    # save column numbers for new header names in array hdrcols
    for (i=1;i<=newhdrcnt;i++){
      n=split(hdrnames[i], oldhdr, ",")
      for(j=1;j<=n;j++){
        for(k=2;k<=NF;k++){
          if ($(k) == oldhdr[j]){
            hdrcols[i]=(j==1 ? "" : hdrcols[i] ",") k;
            if (j==n) break
          }
        }
      }
    }
    # print header
    printf $1
    for (i=1;i<=newhdrcnt;i++)
      printf FS newhdr[i]
    printf ORS
    next
  }
  { # print data
    printf $1
    for (i=1;i<=newhdrcnt;i++){
      n=split(hdrcols[i], cols, ",")
      res=0
      for(j=1;j<=n;j++)
        res=res+$(cols[j])
      printf FS res
    }
    printf ORS
  }
' FS="," file2 FS="\t" file1

산출:

rowname header1 header3 header4 header6 header7
rowname1        2       10      5       3       10
rowname2        7       200     40      2       2

답변2

뇌를 다친 것 같지만gawk

#!/usr/bin/awk -f

BEGIN{FS="\t"} 
  NR==FNR{H[NR]=","$0","; next}
  FNR==1{for (i=2;i<=NF; i++) T[i]=","$i","; printf $1; 
    for (i=1; i<=length(H); i++){split(H[i],sp,","); printf "\t"sp[2]}; print ""}
  FNR>1{delete S; for (i=2; i<=NF; i++) 
    {for (h in H) {if (H[h] ~ T[i]) S[h]+=$i}} printf $1; 
    for (i=1; i<=length(H); i++) printf "\t"S[i]; print""}

다음과 같이 호출됨

./script file2 file1 | column -t

산출

rowname   header1  header3  header4  header6  header7
rowname1  2        10       5        3        10
rowname2  7        200      40       2        2

@freddy에 대한 @paul_pedant의 의견에 따라 지정되지 않은 열은 제거됩니다.

송곳

설정FS

BEGIN{FS="\t"} 

요구사항과 열 목록을 배열에 로드하고 H[]각 끝에 ","를 추가합니다(이후 정규식 불일치를 방지하기 위해).

  NR==FNR{H[NR]=","$0","; next}

데이터 파일의 첫 번째 행인 경우 배열에 열 헤더를 로드 T[]하고 헤더 이름의 각 끝에 ","를 추가합니다.$i

  FNR==1{for (i=2;i<=NF; i++) T[i]=","$i","; 

...추출된 제목을 지정된 문자열의 첫 번째 부분의 합으로 인쇄합니다.

    printf $1; for (i=1; i<=length(H); i++){split(H[i],sp,","); printf "\t"sp[2]}; print ""}

각 데이터 행에 대해 합계 배열을 지운 S[]다음 필드를 반복합니다.

  FNR>1{delete S; for (i=2; i<=NF; i++)

H[]필드 이름을 확인 T[]하고, 있는 경우 일치 항목에 색인된 합계 배열에 필드 값을 추가합니다.S[h]

    {for (h in H) {if (H[h] ~ T[i]) S[h]+=$i}}

각 줄의 끝에 인쇄

    printf $1; for (i=1; i<=length(H); i++) printf "\t"S[i]; print""}

관련 정보