次の形式のファイルから取得するコマンドを探しています:
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
その他のオプション:
perl
行の順序を気にしない場合は、perl -lane '$k{$F[0]}=$F[1]; END{print "$_ $k{$_}" for keys(%k)}' file
よりシンプルな
awk
awk '{c[$1]=$0;} END{for(i in c){print c[i]}}' file
馬鹿げた貝殻
while read a b; do grep -w ^"$a" file | tail -n1 ; done < file | uniq