タブ区切りファイルの単一列の重複項目に別の文字列を追加して一意にする方法

タブ区切りファイルの単一列の重複項目に別の文字列を追加して一意にする方法

4列のタブで区切られたファイルがあり、最後の列に重複することがあります。以下は文書から抜粋したものです。

chr7    116038644       116039744       GeneA
chr7    116030947       116032047       GeneA
chr7    115846040       115847140       GeneA
chr7    115824610       115825710       GeneA
chr7    115801509       115802609       GeneA
chr7    115994986       115996086       GeneA
chrX    143933024       143934124       GeneB
chrX    143933119       143934219       GeneB
chrY    143933129       143933229       GeneC

列の各重複値セットに対して、次のように変換したいと思います(列の重複しない値に実際に触れることなく)。

chr7    116038644       116039744       GeneA-1
chr7    116030947       116032047       GeneA-2
chr7    115846040       115847140       GeneA-3
chr7    115824610       115825710       GeneA-4
chr7    115801509       115802609       GeneA-5
chr7    115994986       115996086       GeneA-6
chrX    143933024       143934124       GeneB-1
chrX    143933119       143934219       GeneB-2
chrY    143933129       143933229       GeneC

または、Bashawkループを使用してこれをどのように実行できますか?sedfor

答え1

この試み

awk -F'\t' -v OFS='\t' '{$4=$4 "-" (++count[$4])}1' file.tsv

これは、4番目のフィールド値の各発生をカウンタの配列count(4番目のフィールドの値が「インデックス」として使用されます)に格納し、そのカウンタの事前に増加した値を4番目のフィールドとダッシュに追加します。

上記の「簡単な」例には1つの欠点があります。ファイルに一度だけ表示される列4の値に明確性番号を追加することもできます。これを抑制するには、次のようなデュアル転送アプローチを使用できます(読みやすくするためにコマンドを2行に分割\)。

 awk -F'\t' -v OFS='\t' 'NR==FNR{f[$4]++}\
      NR>FNR{if (f[$4]>1) {$4=$4 "-" (++count[$4])}; print}' file.tsv file.tsv

処理するファイルが記録されています。二重パラメータとして使用されるため、2回読み取られます。

  • 最初の読み取り(グローバルFNR行カウンターと同じファイル別のNR行カウンターで表されます)では、列4の各固有値がファイルに表示される頻度を計算し、それを配列に保存しますf
  • 2番目にファイルを読み取るときは、「簡単な」方法などの実際のテキスト処理を実行し、発生カウンタを列4に追加します。ただし、最初のパスで見つかった総発生回数が1より大きい場合にのみ該当します。

このアプローチは、ファイル全体のバッファリングを防ぎ、ファイルが非常に大きい場合に利点となります。もちろん、ファイルを2回読み取るため、処理時間が長くなります。

通常、テキスト処理にシェルループを使用する必要はほとんどありません。awk たとえば、ループ操作をより効率的な方法で独自に実行できるためです。

答え2

例に示すように、入力ファイルが4番目の列にグループ化されているとします。

$ cat tst.awk
$NF != prev {
    prt()
    cnt = 0
    prev = $NF
}
{ rec[++cnt] = $0 }
END { prt() }

function prt() {
    for (i=1; i<=cnt; i++) {
        print rec[i] (cnt > 1 ? "-"i : "")
    }
}

$ awk -f tst.awk file
chr7    116038644       116039744       GeneA-1
chr7    116030947       116032047       GeneA-2
chr7    115846040       115847140       GeneA-3
chr7    115824610       115825710       GeneA-4
chr7    115801509       115802609       GeneA-5
chr7    115994986       115996086       GeneA-6
chrX    143933024       143934124       GeneB-1
chrX    143933119       143934219       GeneB-2
chrY    143933129       143933229       GeneC

答え3

これは「-のみ追加されます。数字その値が一意でない場合は、指定された(ターゲット)フィールド(例では4番目のフィールド)に追加されます。また、入力がターゲット列に対してソートされていない場合も処理し、入力列の数に関係なく機能します。

次のAWKスクリプトはターゲットフィールドで入力をソートする必要があるため、パイプを使用して元の行の番号を付け(現在)、5番目のフィールド(最初のフィールドは先行番号)にソートし、非行にサフィックスを追加します。 5番目のフィールドの一意の値で、行を元の順序に戻し、先行番号を削除します。

nl file | sort -b -t '<TAB>' -k5,5 -k1n,1n | awk -F '\t' -v OFS='\t' -v kf=5 '
  function prn () {
    for (i = 1; i <= nfl; i++) {
      if (i == kf)
        printf("%s", prc[i] ( sw || cnt[prc[i]] ? "-"++cnt[prc[i]] : ""))
      else
        printf("%s", prc[i])
      printf("%s", (i == nfl ? ORS : OFS))
    }
  }
  NR > 1 {
    sw = ($kf == prc[kf])
    prn()
  }
  {
    nfl = split($0, prc)
  }
  END {
    if (NR > 0)
      prn()
  } ' | sort -k1n,1n | cut -f 2-

このAWKスクリプトのポイントは印刷することです。より早いkf行の最初のフィールドが現在の行のフィールドと同じであること、または最初のフィールドがkf複数回発生したことを確認します。どちらの場合も、kf最初のフィールドは追加された回数で印刷されます。

明確にしたい列の実際の位置を反映するように-v kf=5(およびキー)を調整する必要があります。-k5,5 sort

次の例(行を混ぜて列を追加した例)を考えると、次のようになりますfile

chr7    116038644   116039744   GeneA   foo
chrX    143933024   143934124   GeneB   foo
chr7    116030947   116032047   GeneA   foo
chr7    115824610   115825710   GeneA   foo
chrY    143933129   143933229   GeneC   foo
chr7    115994986   115996086   GeneA   foo
chrX    143933119   143934219   GeneB   foo
chr7    115801509   115802609   GeneA   foo
chr7    115846040   115847140   GeneA   foo

出力は次のとおりです。

chr7    116038644   116039744   GeneA-1 foo
chrX    143933024   143934124   GeneB-1 foo
chr7    116030947   116032047   GeneA-2 foo
chr7    115824610   115825710   GeneA-3 foo
chrY    143933129   143933229   GeneC   foo
chr7    115994986   115996086   GeneA-4 foo
chrX    143933119   143934219   GeneB-2 foo
chr7    115801509   115802609   GeneA-5 foo
chr7    115846040   115847140   GeneA-6 foo

答え4

簡単な2段階のawkコマンド:

$ awk -F '\t' '
    BEGIN { OFS=FS }
    pass == 1 { count[$4]++; next }
    count[$4] > 1 { $4 = $4 "-" ++number[$4] }; 1' pass=1 file pass=2 file
chr7    116038644       116039744       GeneA-1
chr7    116030947       116032047       GeneA-2
chr7    115846040       115847140       GeneA-3
chr7    115824610       115825710       GeneA-4
chr7    115801509       115802609       GeneA-5
chr7    115994986       115996086       GeneA-6
chrX    143933024       143934124       GeneB-1
chrX    143933119       143934219       GeneB-2
chrY    143933129       143933229       GeneC

これは、入力ファイルがタブで区切られていると仮定します。-F '\t'そうでない場合は、編集または削除してください。

ファイルを最初にナビゲートすると、連想配列はcount4列目の各遺伝子名の発生回数で埋められます。

ファイルを2回目に渡すときにデータセットに複数の遺伝子名が表示される場合(ファイルを介して最初に渡されると)、ダッシュと数字が遺伝子名の末尾に追加されます。

counter追加された数字は、配列と同様に遺伝子名で入力される別のカウンタです。

関連情報