Gruppenweiser Uniq-Befehl?

7

Ich suche nach einem Befehl, um aus einer Datei in diesem Format zu gelangen:

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

Zu diesem Format (Deduplizieren durch Nehmen der letzten Zeile einer "Gruppe"):

hello 67
hi    2
ho    3000

Im Moment verwende ich ein Python- und Pandas-Snippet:

    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'))

Update: Danke für die tollen Antworten. Hier einige Benchmarks:

Die Eingabedatei ist 246 MB groß und enthält 8583313 Zeilen. Bestellung spielt keine Rolle. Die erste Spalte hat eine feste Größe von 9 Zeichen.

Beispiel für die Eingabedatei:

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)

Da die erste Spalte eine feste Länge hat, uniq -wkann auch verwendet werden:

tac {input} | uniq -w 9        3.25484s   O(1)
miku
quelle
Wie tac {input} | uniq -w 9funktioniert das?
Cuonglm
@ Gnouc, -w Nberücksichtigt nur die ersten NZeichen. Für meine lokale Datei hatte ich also eine 9-Zeichen-ID in der ersten Spalte uniq -w 9.
Miku
Ich habe mit Ihrer Eingabe getestet und das Ergebnis ist nicht korrekt.
Cuonglm
@ Gnouc, Ja für die kleine Eingabe, uniq -w 5sollte funktionieren. Ich denke, Ihre und Mikels Antworten sind besser, da sie keine Annahme über die Anzahl der Zeichen in der ersten Spalte machen. Wenn die Eingabe jedoch einer solchen Einschränkung folgt, uniq -wist sie die schnellste.
Miku

Antworten:

5

Das scheint verrückt zu sein und hoffentlich gibt es einen besseren Weg, aber:

tac foo | sort -k 1,1 -u

tac wird verwendet, um die Datei umzukehren, sodass Sie eher die letzte als die erste erhalten.

-k 1,1 sagt, benutze nur das erste Feld zum Vergleich.

-u macht es einzigartig.

Mikel
quelle
ah lol, tac war die Lösung für meine Antwort haha
polym
+1, dies ist die eleganteste Befehlszeilenlösung, die ich bisher gesehen habe. :)
Ramesh
Das ist wirklich gut und es war das erste, woran ich dachte, aber konntest du nicht einfach sort -ruk1,1 foo? Vielleicht lese ich es aber nicht richtig.
Mikeserv
Danke, das gefällt mir. Es ist eigentlich etwas langsamer als Python / Pandas, aber klar und prägnant. Der Speicheraufwand ist viel besser als bei Python und anderen Hash-basierten Lösungen.
Miku
4

Wenn Ihnen die Reihenfolge der Ausgabe nichts ausmacht, finden Sie hier eine awkLösung:

$ 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
cuonglm
quelle
3

Einige weitere Optionen:

  1. perl, wenn Sie sich nicht um die Reihenfolge der Zeilen kümmern.

    perl -lane '$k{$F[0]}=$F[1]; END{print "$_ $k{$_}" for keys(%k)}' file
  2. Ein einfacher awk

    awk '{c[$1]=$0;} END{for(i in c){print c[i]}}' file
  3. Eine dumme Muschel

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

Nun, du kannst es damit machen sort

sort -u -k1,1 test

EDIT: Tac ist die Lösung

polym
quelle