동일한 열 헤더를 공유하는 파일의 여러 열을 병합하고 싶습니다. 파일은 다음과 같으며 탭으로 구분되거나 다른 방식으로 구분될 수 있습니다.
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
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에서 첫 번째 발생을 식별하고 각 연속 행에 대해 헤더와 null이 아닌 값 사이에 맵을 생성하고 "고유"를 누릅니다. 헤더의 순서는 다음과 같은 경우 인식된 대로 인쇄됩니다. 첫 번째 줄을 처리 중입니다.
그러나 이는 입력 파일이 탭으로 구분된 경우에만 작동합니다. 이는 "빈" 필드를 안정적으로 감지하는 유일한 방법이기 때문입니다.
delete
또한 모든 구현이 전체 배열에 대한 명령문을 지원하는 것은 아닙니다 (그러나 , 및 에서는 작동해야 함).linemap
awk
gawk
mawk
nawk