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)

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.


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

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.