열 머리글을 기반으로 파일 내의 열 병합

열 머리글을 기반으로 파일 내의 열 병합

내 파일 내에서 동일한 열 헤더를 공유하는 여러 열을 병합하고 싶습니다. 파일은 다음과 같으며 탭으로 구분되거나 다른 것으로 구분될 수 있습니다.

AaBbN    CcDdEeN    FfN     AaBbN    FfN
1        5          4   
3        1          2
2        NA         1
                            1        3
                            3        2
                            NA       4

따라서 필드에 숫자 또는 문자열 "NA"가 있습니다. 결과는 다음과 같습니다:

AaBbN    CcDdEeN    FfN
1        5          4
3        1          2
2        NA         1
1                   3
3                   2
NA                  4

순서가 지정되지 않은 열이 많기 때문에 제목 헤더를 일일이 수동으로 지정하는 대신 자동으로 읽어야 합니다. 빈 필드도 많이 있습니다. 나는 작업을 수행하기 위해 paste및 명령을 조사해 왔습니다 . join특히 join내 열이 동일한 파일 내에 있는 반면 별도의 파일에서 작동한다는 점을 제외하면 내가 필요한 작업을 수행하는 것 같습니다.

그래서 컬럼을 별도의 파일로 분리한 후 join. awk여기에서 파생된 명령을 사용했습니다 .

https://www.unix.com/unix-for-dummies-questions-and-answers/204303-splitting-up-text-file-into-multiple-files-columns.html

awk ' { for( i = 1; i <= NF; i++ ) printf( "%s\n", $(i) ) >i ".txt"; } ' file.txt

그러면 별도의 열이 제공되지만 여기서 첫 번째 문제가 발생했습니다. 헤더와 데이터 사이에 공백이 있는 모든 열이 올바르게 처리되지 않았습니다. 대신 이 파일에는 열 헤더만 있었습니다.

두 번째 문제는 다음과 같습니다 join. 파일을 다시 병합하려고 하면 입력이 정렬되지 않아 오류가 발생합니다. 물론 이는 불가능합니다. 어떤 정렬을 하게 되면 내가 돌보고 있는 관계가 파괴될 것입니다.

그래서 나는 막 다른 골목에 있습니다. 파일 내에서 직접 열을 병합하는 더 편리한 방법이 있습니까?


편집하다:

AdminBees 솔루션은 문제 해결에 가장 가깝지만 결과는 올바르지 않습니다. 위 예제에 awk 스크립트를 적용한 결과는 다음과 같습니다. 모든 항목이 탭 sed -i "s/[[:space:]]/ /g"(CTRL+V 및 TAB으로 삽입된 탭) 으로 구분되어 있는지 확인했습니다 .

AaBbN   CcDdEeN FfN     FfN
1   5   4   

3   1   2

2   NA  1

            1
            3
            NA

답변1

입력이 탭으로 구분된 경우:

awk -F"\t" '
NR == 1 {for (i=1; i<=NF; i++)  COL[i] = $i
        }
        {for (i=1; i<=NF; i++) OUT[NR, COL[i]] = $i
        }
END     {for (n=1; n<=NR; n++)  {split ("", DUP)
                                 for (i=1; i<=NF; i++)  if (!DUP[COL[i]]++) printf "%s" FS, OUT[n,COL[i]]
                                 printf RS
                                }
        }
' file
A   B   C   
1   5   4   
3   1   2   
2   2   1   
1       3   
3       2   
1       4   

나중에 부분 인덱스로 사용하기 위해 열 헤더를 저장한 다음 각 라인에 대해 라인 번호와 헤더 부분 인덱스로 인덱스된 배열로 값을 수집합니다. 섹션 에서는 END중복된 열 헤더를 처리하면서 원래 순서대로 해당 배열을 인쇄합니다.

보다 복잡한 파일 구조에서는 중복 처리가 큰 노력이 될 수 있습니다.

답변2

탭으로 구분된 입력의 경우.

헤더와 해당 열 번호를 입력 파일에 표시된 배열로 읽습니다. 그런 다음 각 열의 입력 파일을 동일한 headerName을 갖는 동일한 파일 이름 headerName.txt로 분할합니다. 결국 함께 붙여넣고column출력을 아름답게 하는 데 사용되는 명령입니다.

awk -F'\t' '
    ## find all the column number(s) when same header found and store in `h` array
    ## key is the column number and value is header name. for an example:
    ## for the header value 'A', keys will be columns 1 &4
    NR==1{ while (++i<=NF) h[i]=$i; next; }

         { for (i=1; i<=NF; i++) {

    ## save the field content to a file which its key column matches with the column 
    ## number of the current field. for an example:
    ## for the first field in column 1; the column number is 1, and so 1 is the key  
    ## column for header value A, so this will be written to "A.txt" filename
    ## only if it was not empty.
               if ($i!=""){ print $i> h[i]".txt" };
         }; }

    ## at the end paste those all files and beautify output with `column` command.
    ## number of .txt files above is limit to the number of uniq headers in your input. 
END{ system("paste *.txt |column \011 -tn") }' infile

주석 없는 명령:

awk -F'\t' '
    NR==1{ while (++i<=NF) h[i]=$i; next; }
         { for (i=1; i<=NF; i++) {
               if ($i!=""){ print $i> h[i]".txt" };
         }; }
END{ system("paste *.txt |column \011 -tn") }' infile

답변3

전체 파일을 "버퍼링"할 필요가 없는 약간 다른 접근 방식입니다.

AWK 스크립트 colmerge.awk:

FNR==1{
    for (i=1; i<=NF; i++)
    {
    hdr[i]=$i;
    if (map[$i]==0) {map[$i]=i; uniq_hdr[++u]=$i; printf("%s",$i);}
    if (i==NF) printf("%s",ORS); else printf("%s",OFS);
    }
}

FNR>1{
    delete linemap;
    for (i=1; i<=NF; i++) if ($i!="") linemap[hdr[i]]=$i;
    for (i=1; i<=u; i++)
    {
    printf("%s",linemap[uniq_hdr[i]]);
    if (i==u) printf("%s",ORS); else printf("%s",OFS);
    }
}

로 사용

awk -F'\t' -v OFS='\t' -f colmerge.awk file

이렇게 하면 모든 헤더를 수집하고 "고유한" 헤더와 라인 1에서 첫 번째 발생을 식별하고, 연속되는 각 라인에 대해 헤더와 비어 있지 않은 값 사이의 맵을 생성한 다음 "고유한" 헤더의 순서로 인쇄합니다. 첫 번째 줄을 처리하는 동안 식별된 대로입니다.

그러나 이는 입력 파일이 탭으로 구분된 경우에만 작동합니다. 이는 "빈" 필드를 안정적으로 감지하는 유일한 방법이기 때문입니다.

delete전체 배열에 대한 명령문 은 linemap모든 구현에서 지원되지 않을 수도 있습니다 (그러나 , 및 awk에서는 작동해야 함 ).gawkmawknawk

관련 정보