我想合併文件中共享相同列標題的不同列。該文件如下所示,可以用製表符分隔或其他形式:
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 行上的第一次出現,並為每個連續行在標頭和非空值之間建立一個映射,然後按“唯一”標頭的順序列印出來如處理第一行時所辨識的。
然而,只有當您的輸入檔案是製表符分隔時,這才有效,因為這是可靠地檢測「空」欄位的唯一方法。
另請注意,並非所有實作都支援delete
整個陣列的語句(但是應該適用於、和)。linemap
awk
gawk
mawk
nawk