Groupwise uniq コマンド?

Groupwise uniq コマンド?

次の形式のファイルから取得するコマンドを探しています:

hello 32
hello 67
hi    2
ho    1212
ho    1390
ho    3000

この形式に変更します (「グループ」の最後の行を取得して重複を排除します):

hello 67
hi    2
ho    3000

現時点では、Python と pandas のスニペットを使用しています。

    df = pd.read_csv(self.input().path, sep='\t', names=('id', 'val'))

    # how to replace this logic with shell commands?
    surface = df.drop_duplicates(cols=('id'), take_last=True)

    with self.output().open('w') as output:
        surface.to_csv(output, sep='\t', cols=('id', 'val'))

更新: 素晴らしい回答をありがとうございます。ベンチマークをいくつかご紹介します。

入力ファイルは 246 MB で、8583313 行が含まれています。順序は関係ありません。最初の列のサイズは 9 文字に固定されています。

入力ファイルの例:

000000027       20131017023259.0        00
000000027       20131017023259.0        11
000000035       20130827104320.0        01
000000035       20130827104320.0        04
000000043       20120127083412.0        01
...

                              time        space complexity

tac .. | sort -k1,1 -u        27.43682s   O(log(n))
Python/Pandas                 11.76063s   O(n)
awk '{c[$1]=$0;} END{for(...  11.72060s   O(n)

最初の列の長さは固定されているので、次uniq -wのようにも使用できます。

tac {input} | uniq -w 9        3.25484s   O(1)

答え1

これはおかしいように思えますし、もっと良い方法があることを願っていますが、

tac foo | sort -k 1,1 -u

tacファイルを逆にするために使用されるので、最初のファイルではなく最後のファイルを取得します。

-k 1,1比較には最初のフィールドのみを使用すると言います。

-uユニークなものになります。

答え2

出力の順序を気にしない場合は、次のawk解決策があります。

$ awk '
    {a[$1] = !a[$1] ? $2 : a[$1] < $2 ? $2 : a[$1]}
    END {
        for (i in a) { print i,a[i] }
    }
' file
hi 2
hello 67
ho 3000

答え3

その他のオプション:

  1. perl行の順序を気にしない場合は、

    perl -lane '$k{$F[0]}=$F[1]; END{print "$_ $k{$_}" for keys(%k)}' file
    
  2. よりシンプルなawk

    awk '{c[$1]=$0;} END{for(i in c){print c[i]}}' file
    
  3. 馬鹿げた貝殻

    while read a b; do grep -w ^"$a" file | tail -n1 ; done < file | uniq
    

答え4

まあ、それはできるsort

sort -u -k1,1 test

編集:tacが解決策です

関連情報