GNU sort stable sort, wenn sort die Sortierreihenfolge nicht kennt

18

Ich habe eine zweispaltige Datei. Die Datei ist in Spalte 1 bereits so sortiert, wie ich es möchte. Ich möchte nach Spalte 2 in jeder Kategorie von Spalte 1 sortieren. Versteht sortjedoch nicht die Sortierreihenfolge von Spalte 1.

Der normale Weg (von ähnlichen Fragen hier auf Stapel) wäre dieser:

sort --stable -k1,1 -k2,2n

Die Sortierung für k1 kann ich aber nicht angeben, da sie willkürlich ist.

Beispiel Eingabe:

C 2
C 1
A 2
A 1
B 2 
B 1

und Ausgabe:

C 1
C 2
A 1
A 2
B 1 
B 2
Evan Benn
quelle

Antworten:

20

Sie können awk verwenden, um für jeden Block eine neue Sortierung zu starten:

% awk -v cmd="sort -k2,2" '$1 != prev {close(cmd); prev=$1} {print | cmd}' foo
C 1
C 2
A 1
A 2
B 1
B 2
  • $1 != prev {close(cmd); prev=$1} - Wenn der gespeicherte Wert unterschiedlich ist, haben wir einen neuen Block, so dass wir alle zuvor gestarteten schließen sort
  • {print | "sort -k2,2"}'Leitet die Ausgabe an sortund startet sie, wenn sie noch nicht ausgeführt wird (awk kann Befehle verfolgen, die gestartet werden).
muru
quelle
2
awk ist wirklich unglaublich. Ich mag das viel mehr als ich erwartet hatte, das war eine awk dekorieren-sortieren-undekorieren!
Evan Benn
Ich habe versucht, die Leistung dieser Antwort mit der anderen zu vergleichen, nicht sicher, warum diese mehr Ressourcen verwendet ... Irgendwelche Ideen? gist.github.com/EvanTheB/5b64eafb84eeaf51c289295ac06e1b0b
Evan Benn
Wie viele Läufe haben Sie durchschnittlich gemacht?
muru
Ich habe keine Mittelwertbildung durchgeführt, sehe aber konstante Laufzeiten, wenn ich mich wiederhole und nachforsche.
Evan Benn
Hier ist eine ähnliche Datei zu dem, was ich verwende, wenn Sie untersuchen möchten:seq 30 | xargs -L1 bash -cs 'yes $1 | head -1000000 | paste - <(seq 1000000) | shuf' bash
Evan Benn
12

Sie könnten eine Schwartzsche Transformation verwenden (dies ist im Grunde der Ansatz, den Sie in einem Kommentar als Dekorieren-Sortieren- Undekorieren bezeichnet haben , aber wahrscheinlich performanter als die gute Antwort von muru, weil Sie einen einzelnen sortAufruf anstelle von mehreren verwenden) - indem awkSie eine Präfixspalte hinzufügen, die Inkrementiert mit einer Wertänderung in der ersten Spalte, sortiert nach der Präfixspalte, gefolgt von der "zweiten" Spalte (deren Ordinalposition sich 3aufgrund des Vorhandenseins der Präfixspalte vorübergehend verschoben hat ), und entfernt schließlich die Präfixspalte

awk '{print ($1 in a? c+0: ++c)"\t" $0; a[$1]}' file | sort -k1,1n  -k3,3 | cut -f 2-
iruvar
quelle
Ich bin überrascht, aber Sie haben Recht, das war schneller als die andere Antwort! 3 Minuten versus 2 Minuten in meiner 100 Millionen Zeilen umfassenden Datei (~ 30 uniq erste Spalten).
Evan Benn
1
Es ist nicht erforderlich, ein Array des eindeutigen Schlüssels aus der ersten Spalte beizubehalten. Ich denke, es sollte ausreichen, die erste Spalte der aktuellen Zeile mit der vorherigen zu vergleichen.
Kusalananda
So etwas wie awk -v OFS="\t" '$1 != prev { key++ } { print key, $0; prev = $1 }(ungetestet).
Kusalananda