さまざまな列に基づいてデータを比較する

さまざまな列に基づいてデータを比較する

列数が異なる行を含むファイル(タブ区切り)があります。このように:

Bin_37:_Pelotomaculum_sp._DTU098    GH3 GH57    GH15    GH18    GT2 GT4 GT28                                                                        
Bin_45_1:_Thiopseudomonas_denitrificans GH3 GH57    GT2 GT9 CBM48                                                                               
...

私の質問は、データが構成されている列ごとの行比較を含む別のファイル(tsv)を生成する方法です。たとえば、次のように欠落している値が入力されます。

Bin_37:_Pelotomaculum_sp._DTU098 GH3 GH57 GH15 GH18 GT2 GT4 GT28 NA NA
Bin_45_1:_Thiopseudomonas_denitrificans GH3 GH57 NA NA GT2 NA NA GT9 CBM48
...

答え1

非常に大きなファイルには最も効率的ではありませんが、動作するバージョンである可能性があります。

入力するファイルファイル1:

Bin_37:_Pelotomaculum_sp._DTU098        GH3     GH57    GH15    GH18    GT2     GT4     GT28
Bin_45_1:_Thiopseudomonas_denitrificans GH3     GH57    GT2     GT9     CBM48
Bin_99:_to_make_sure_no_columns_is_ok

スクリプト(/bin/sh または /bin/bash):

#!/bin/sh
F="file1";
COLS=$(cat "${F}"|sed 's/^[^\t]*//g;s/\t/\n/g'|sort|uniq|xargs);
# list of all available unique columns in SORTED order
echo "All avaiulable columns: [${COLS}]";
echo
# reading from the file line by line
cat "${F}"|while read L; do
  # assign to A the first column
  A=$(echo "${L}"|cut -d' ' -f1);
  # if A is not empty
  [ -n "${A}" ] && 
  {
    # take one by one all possible column values
    for C in ${COLS}; do
      # if the taken line has such column, add it to A,
      # otherwise add to A NA
      echo "${L} "|grep "\s${C}\s" >/dev/null && 
        A="$A"$'\t'"${C}" || 
        A="$A"$'\tNA'; 
    done;
    # print result line
    echo "${A}";
  };
done

出力:

All avaiulable columns: [CBM48 GH15 GH18 GH3 GH57 GT2 GT28 GT4 GT9]

Bin_37:_Pelotomaculum_sp._DTU098        NA      GH15    GH18    GH3     GH57    GT2     GT28    GT4     NA
Bin_45_1:_Thiopseudomonas_denitrificans CBM48   NA      NA      GH3     GH57    GT2     NA      NA      GT9
Bin_99:_to_make_sure_no_columns_is_ok   NA      NA      NA      NA      NA      NA      NA      NA      NA

同じ(最初に利用可能な列のリストはありません)ライナーとして:

F="file1"; COLS=$(cat "${F}"|sed 's/^[^\t]*//g;s/\t/\n/g'|sort|uniq|xargs); cat "${F}"|while read L; do A=$(echo "${L}"|cut -d' ' -f1); [ -n "${A}" ] && { for C in ${COLS}; do echo "${L} "|grep "\s${C}\s" >/dev/null && A="$A"$'\t'"${C}" || A="$A"$'\tNA'; done; echo "${A}"; }; done

直す。より効率的なバージョン最適化、コメントの提案に基づいています(/bin/bash が必要)。

F="file1"; IFS=$'\n'; COLS=($(sed 's/^[^\t]*//g;s/\t/\n/g' "${F}"|sort -u)); while read -r L; do A="${L%%$'\t'*}"; [ -n "${A}" ] && for C in ${COLS[@]}; do [[ "${L}"$'\t' == *$'\t'"${C}"$'\t'* ]] && A="$A"$'\t'"${C}" || A="$A"$'\tNA'; done && echo "${A}"; done <${F}; IFS=' '

答え2

これまでの他のすべての回答と同様に、提供された入力によって提供される期待される出力は生成されませんが、入力に実際にタブ区切りの空のフィールドが含まれている場合、NAそのフィールドはsで埋められます。

$ cat tst.awk
BEGIN { FS=OFS="\t" }
NR == FNR {
    gsub(/\t+$/,"")
    maxNF = (NF>maxNF ? NF : maxNF)
    next
}
{
    for (i=1; i<=maxNF; i++) {
        printf "%s%s", ($i == "" ? "NA" : $i), (i < maxNF ? OFS : ORS)
    }
}

$ awk -f tst.awk file file
Bin_37:_Pelotomaculum_sp._DTU098        GH3     GH57    GH15    GH18    GT2     GT4     GT28
Bin_45_1:_Thiopseudomonas_denitrificans GH3     GH57    GT2     GT9     CBM48   NA      NA

答え3

入力ファイルがタブで区切られている場合は、次のGNU awkスクリプトを使用できます。

awk 'BEGIN{RS="[\t\n]"} !NF{$1="NA"} {printf "%s%s", $0, RT}' file

レコード区切り文字は、フィールドのRS数を取得するためにタブまたは改行に設定されますNF

空の場合NF(2つのタブ間に単語がないことを意味)、文字列が追加さNAれます。

スクリプトはレコードターミネーターRT(a\tまたはa)を使用して\n結果レコードを印刷します。

関連情報