Ordinamento GNU ordinamento stabile quando l'ordinamento non conosce l'ordinamento


18

Ho un file a due colonne; il file è ordinato come lo voglio già nella colonna 1. Vorrei ordinare sulla colonna 2, all'interno di ogni categoria di colonna 1. Tuttavia, sortnon comprende l'ordinamento della colonna 1.

Il modo normale (da domande simili qui in pila) sarebbe questo:

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

Ma non posso specificare l'ordinamento su k1, perché è arbitrario.

Esempio di input:

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

e uscita:

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

Risposte:


20

È possibile utilizzare awk per iniziare un nuovo ordinamento per ciascun blocco:

% 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} - quando il valore salvato è diverso, abbiamo un nuovo blocco, quindi chiudiamo quelli precedentemente avviati sort
  • {print | "sort -k2,2"}'convoglia l'output a sort, avviandolo se non è già in esecuzione (awk può tenere traccia dei comandi che avvia)

2
awk è davvero incredibile. Mi piace molto di più di quello che mi aspettavo, che era un pazzo decorare-indecorare!
Evan Benn,

Ho provato a confrontare perf di questo con l'altra risposta, non sono sicuro del perché questo utilizza più risorse ... Qualche idea? gist.github.com/EvanTheB/5b64eafb84eeaf51c289295ac06e1b0b
Evan Benn

Quante corse hai segnato in media?
muru

Non ho fatto una media, ma vedo tempi di esecuzione costanti mentre ripeto e indago.
Evan Benn,

Ecco un file simile a quello che sto usando se vuoi indagare:seq 30 | xargs -L1 bash -cs 'yes $1 | head -1000000 | paste - <(seq 1000000) | shuf' bash
Evan Benn

12

Potresti usare una trasformazione di Schwartz (questo è fondamentalmente l'approccio di decor-sort-undecorate a cui hai accennato in un commento, ma probabilmente più performante della bella risposta di Muru a causa dell'utilizzo di una singola sortinvocazione anziché di più) - usando awkaggiungi una colonna prefisso che incrementa con una variazione di valore nella prima colonna, ordina per la colonna del prefisso seguita dalla colonna "seconda" (la cui posizione ordinale è temporaneamente passata a 3causa della presenza della colonna del prefisso), e infine elimina la colonna del prefisso

awk '{print ($1 in a? c+0: ++c)"\t" $0; a[$1]}' file | sort -k1,1n  -k3,3 | cut -f 2-

Sono sorpreso, ma hai ragione, questo è stato più veloce dell'altra risposta! 3 minuti contro 2 minuti sul mio file da 100 milioni di righe (~ 30 prime colonne uniq).
Evan Benn,

1
Non è necessario mantenere una matrice della chiave univoca dalla prima colonna. Penso che dovrebbe essere sufficiente per confrontare la prima colonna della riga corrente con la precedente.
Kusalananda

Qualcosa di simile awk -v OFS="\t" '$1 != prev { key++ } { print key, $0; prev = $1 }(non testato).
Kusalananda
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.