重複した名前を削除し、一意の名前の後に配列を出力する方法

重複した名前を削除し、一意の名前の後に配列を出力する方法

以下の例のように、同じ名前の KO カテゴリを折りたたみ、各カテゴリに割り当てられた遺伝子名を配列で出力する方法。

私はこれを持っています:

K00002  gene_65472
K00002  gene_212051
K00002  gene_403626
K00003  gene_666
K00003  gene_5168
K00003  gene_7635
K00003  gene_12687
K00003  gene_175295
K00003  gene_647659
K00003  gene_663019
K00004  gene_88381
K00005  gene_30485
K00005  gene_193699
K00005  gene_256294
K00005  gene_307497

そして、これが欲しいです:

K00002  gene_65472  gene_212051 gene_403626             
K00003  gene_666    gene_5168   gene_7635   gene_12687  gene_175295 gene_647659 gene_663019
K00004  gene_88381                      
K00005  gene_30485  gene_193699 gene_256294 gene_307497 

次のコマンドは機能しました(roaimaの回答):

tr -d '\r' < file| awk '$1 != p { if (p>"") {printf "\n"} printf "%s",$1; p=$1 } { printf "\t%s",$2 } END { if(p>"") {printf "\n"} }' > output

答え1

同じことの繰り返し

awk '$1 != p { if (p>"") {printf "\n"} printf "%s",$1; p=$1 } { printf "\t%s",$2 } END { if(p>"") {printf "\n"} }' datafile

K00002  gene_65472      gene_212051     gene_403626
K00003  gene_666        gene_5168       gene_7635       gene_12687      gene_175295     gene_647659     gene_663019
K00004  gene_88381
K00005  gene_30485      gene_193699     gene_256294     gene_307497

分離を望まない場合はタブ\tをスペースに変更します。

仕組みは次のとおりです:

# Each line is processed in turn. "p" is the previous line's key field value

# Key field isn't the same as before
$1 != p {
    # Flush this line if we have printed something already
    if (p > "") { printf "\n" }

    # Print the key field name and set it as the current key field
    printf "%s", $1; p = $1
}

# Every line, print the second value on the line
{ printf "\t%s", $2 }

# No more input. Flush the line if we have already printed something
END {
    if (p > "") { printf "\n" }
}

から漠然 コメントあなたは作る皆さんの回答に反して、根本的な問題は、Windows システムで生成されたデータ ファイルを使用して、それが UNIX/Linux プラットフォームで動作することを期待しているということのようです。そうしないでください。または、どうしても必要な場合は、まずファイルを正しい形式に変換してください。

dos2unix < datafile | awk '...'       # As above

tr -d '\r' < data file | awk '...'    # Also as above

答え2

ファイル:

K00002  gene_65472
K00002  gene_212051
K00002  gene_403626
K00003  gene_666
K00003  gene_5168
K00003  gene_7635
K00003  gene_12687
K00003  gene_654221
K00003  gene_663019
K00004  gene_88381
K00005  gene_30485
K00005  gene_193699
K00005  gene_256294

awk の使用:

awk '1 {if (a[$1]) {a[$1] = a[$1]" "$2} else {a[$1] = $2}} END {for (i in a) { print i,a[i]}}' file

出力:

K00002 gene_65472 gene_212051 gene_403626
K00003 gene_666 gene_5168 gene_7635 gene_12687 gene_654221 gene_663019
K00004 gene_88381
K00005 gene_30485 gene_193699 gene_256294

これを撮った役職参考として。

答え3

ミラーを使用するhttp://johnkerl.org/miller/doc

mlr --csv --implicit-csv-header --headerless-csv-output cat -n -g 1 then label a,b,c then reshape -s a,c then unsparsify --fill-with "" input.csv

この例のcsv入力

A,234
A,4945
B,8798
B,8798
B,790

あなたは

A,234,4945,
B,8798,8798,790

答え4

値にスペースが含まれず、スペースで区切られていると仮定します。また、データは次の名前のファイルにあると仮定しますfile(タブ区切りバージョンについては以下を参照)。

for x in $(<file cut -d ' ' -f 1 | sort | uniq); do
    printf '%s %s\n' "$x" "$(grep "$x" file | cut -d ' ' -f 2- | tr '\n' ' ' | sed 's/.$//')"
done

この意志:

  • 最初のフィールドの個別の値を抽出します。
    • cut-f 1行の最初のチャンク ( ) のみを選択し、各スペース ( -d ' ') で分割します。
    • sort | uniq最初のフィールドの値をソートし、それぞれを1回だけ出力します(または、より短く、より効率的: sort -u);
  • それぞれについて:
    • fileから関連する行をすべて抽出しますgrep
    • cut最初のフィールドを(-f 2-は「2 番目以降のフィールドを取得する」という意味です )で削除します。
    • 残りをスペースで区切られた値のリストに変換します ( tr);
    • 最後の文字(不要なスペース)を次のように削除しますsed(はい、これは本当にエレガントではありません)。
    • 結果を最初のフィールドの値に連結し、標準出力に出力します。

入力がタブ区切りで、出力もタブ区切りにしたい場合、上記のコードは次のようになります。

for x in $(<file cut -f 1 | sort | uniq); do
    printf '%s\t%s\n' "$x" "$(grep "$x" file | cut -f 2- | tr '\n' '\t' | sed 's/.$//')"
done

ノート:

  1. パフォーマンス: このアプローチの実行時間は、ベースソリューションよりも大幅に長くなりますawk(私はテストしましたroaimaの回答)。少なくとも一桁は違います。
  2. 一方、このアプローチは、入力ファイルが順序付けられていない場合でも機能します。
  3. この種の解決策は、仕事を効率的に行うための素早い(そして汚い?)方法であるにもかかわらず、シェルループでテキストを処理することは一般的には推奨されません。参考のために「シェル ループを使用してテキストを処理するのはなぜ悪い習慣だと考えられるのでしょうか?「」。

関連情報