シェルを使用して区切り文字に基づいて複数の列を行に分割する

シェルを使用して区切り文字に基づいて複数の列を行に分割する

次のようにタブ区切りのファイルがあります。

cg13201342  F   ARNT;ARNT;ARNT;CTSK 3'UTR;3'UTR;3'UTR;TSS1500
cg05269359  F   SCN4B;SCN4B;SCN4B;SCN4B 3'UTR;3'UTR;3'UTR;Body
cg06018296  R   NEK3;NEK3;NEK3;NEK3 3'UTR;3'UTR;3'UTR;Body
cg05172994  F   WDR20;WDR20;WDR20;WDR20 3'UTR;3'UTR;3'UTR;Body

希望の出力:

cg13201342  F   ARNT   3'UTR
cg13201342  F   ARNT   3'UTR
cg13201342  F   ARNT   3'UTR
cg13201342  F   CTSK   TSS1500
cg05269359  F   SCN4B  3'UTR
.
.

など。

頑張った

awk 'BEGIN {
       FS = OFS = "\t"
     }
     {
       n = split($3, f, " *;*")
       for (i=1; i<=n; i++)
         print $1, f[i]
     }' probe-genes-regions >chk

ただし、これは3番目の列のみを分割します。最後の列を2番目の列から分離し、3番目の列の最初のフィールドと最後の列の1番目のフィールドなどで別々の行を形成したいと思います。

答え1

たとえば、フィールド3と4をセミコロンを使用して別々の配列に分割して繰り返します(フィールド3と4の要素数が同じであると仮定)。

解析.awk

BEGIN { OFS = "\t" }

{ 
  n = split($3, a, /;/); split($4, b, /;/)
  for(i=1; i<=n; i++)
    print $1, $2, a[i], b[i] 
}

次のように実行します。

awk -f parse.awk infile

出力:

cg13201342      F       ARNT    3'UTR
cg13201342      F       ARNT    3'UTR
cg13201342      F       ARNT    3'UTR
cg13201342      F       CTSK    TSS1500
cg05269359      F       SCN4B   3'UTR
cg05269359      F       SCN4B   3'UTR
cg05269359      F       SCN4B   3'UTR
cg05269359      F       SCN4B   Body
cg06018296      R       NEK3    3'UTR
cg06018296      R       NEK3    3'UTR
cg06018296      R       NEK3    3'UTR
cg06018296      R       NEK3    Body
cg05172994      F       WDR20   3'UTR
cg05172994      F       WDR20   3'UTR
cg05172994      F       WDR20   3'UTR
cg05172994      F       WDR20   Body

答え2

使用ミラー( mlr):

sed 's/;/\t/' file | mlr --nidx --fs tab nest --evar ';' -f 4

まず、各行の最初の行をタブにsed置き換え、;タブで区切られた3番目のフィールドを2つの別々のフィールドに分割します。 GNUはsedこのコマンドを使用してタブ文字を挿入できますが、すべての実装が可能なわけではありません。そうでない場合は、+を押すのではなくリテラルタブ文字を入力してください。\tssed\tCtrl+VTab

その後、Millerはタブ区切りデータ()を読み書きし、4番目のタブ区切りフィールド()の区切りサブフィールドに基づいて--nidx --fs tab各レコードを「爆発」(または「ネスト解除」)します。;nest --evar ';' -f 4

質問のデータ出力を提供します。

cg13201342      F       ARNT    ARNT
cg13201342      F       ARNT    ARNT
cg13201342      F       ARNT    CTSK 3'UTR
cg13201342      F       ARNT    3'UTR
cg13201342      F       ARNT    3'UTR
cg13201342      F       ARNT    TSS1500
cg05269359      F       SCN4B   SCN4B
cg05269359      F       SCN4B   SCN4B
cg05269359      F       SCN4B   SCN4B 3'UTR
cg05269359      F       SCN4B   3'UTR
cg05269359      F       SCN4B   3'UTR
cg05269359      F       SCN4B   Body
cg06018296      R       NEK3    NEK3
cg06018296      R       NEK3    NEK3
cg06018296      R       NEK3    NEK3 3'UTR
cg06018296      R       NEK3    3'UTR
cg06018296      R       NEK3    3'UTR
cg06018296      R       NEK3    Body
cg05172994      F       WDR20   WDR20
cg05172994      F       WDR20   WDR20
cg05172994      F       WDR20   WDR20 3'UTR
cg05172994      F       WDR20   3'UTR
cg05172994      F       WDR20   3'UTR
cg05172994      F       WDR20   Body

この操作は、uniq隣接する行から重複する項目を削除します。


以下のみを使用してくださいawk

awk -F '\t' 'BEGIN { OFS=FS }
    {
        nf = split($3,a,";")
        for (i = 2; i <= nf; ++i) print $1, $2, a[1], a[i]
    }' file

これにより、3番目のフィールドが分​​割され、;3番目のフィールドの2番目のサブフィールドに対して最初の2つのフィールドと元の3番目のフィールドの1番目のサブフィールドが引き続き出力されます。

その出力は、この回答の上部にあるパイプの出力と同じです。

答え3

入力の空白に関係なく、POSIX awkを使用してください。

$ awk -F'[[:space:];]+' -v OFS='\t' '{
    n=(NF-2)/2; for (i=1; i<=n; i++) print $1, $2, $(2+i), $(2+i+n)
}' file
cg13201342      F       ARNT    3'UTR
cg13201342      F       ARNT    3'UTR
cg13201342      F       ARNT    3'UTR
cg13201342      F       CTSK    TSS1500
cg05269359      F       SCN4B   3'UTR
cg05269359      F       SCN4B   3'UTR
cg05269359      F       SCN4B   3'UTR
cg05269359      F       SCN4B   Body
cg06018296      R       NEK3    3'UTR
cg06018296      R       NEK3    3'UTR
cg06018296      R       NEK3    3'UTR
cg06018296      R       NEK3    Body
cg05172994      F       WDR20   3'UTR
cg05172994      F       WDR20   3'UTR
cg05172994      F       WDR20   3'UTR
cg05172994      F       WDR20   Body

関連情報