根據列標題合併文件中的列

根據列標題合併文件中的列

我想合併文件中共享相同列標題的不同列。該文件如下所示,可以用製表符分隔或其他形式:

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

有很多列沒有排序,因此需要自動讀取標題標題,而不是手動指定每一列。還有很多空地。我一直在研究pastejoin命令來完成這項工作。特別是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整個陣列的語句(但是應該適用於、和)。linemapawkgawkmawknawk

相關內容