GNU sort estable sort cuando sort no conoce el orden de clasificación

18

Tengo un archivo de dos columnas; el archivo está ordenado de la manera que ya lo quiero en la columna 1. Me gustaría ordenar en la columna 2, dentro de cada categoría de la columna 1. Sin embargo, sortno comprende el orden de clasificación de la columna 1.

La forma normal (de preguntas similares aquí en la pila) sería esta:

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

Pero no puedo especificar el tipo en k1, porque es arbitrario.

Entrada de ejemplo:

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

y salida:

C 1
C 2
A 1
A 2
B 1 
B 2
Evan Benn
fuente

Respuestas:

20

Puede usar awk para comenzar un nuevo tipo para cada bloque:

% 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} - cuando el valor guardado es diferente, tenemos un nuevo bloque, por lo que cerramos cualquier inicio anterior sort
  • {print | "sort -k2,2"}'canaliza la salida a sort, iniciándola si aún no se está ejecutando (awk puede realizar un seguimiento de los comandos que inicia)
muru
fuente
2
Awk es realmente increíble. ¡Esto me gusta mucho más de lo que esperaba, que era un awk decorate-sort-undecorate!
Evan Benn
Traté de comparar el rendimiento de esta con la otra respuesta, no estoy seguro de por qué esta usa más recursos ... ¿Alguna idea? gist.github.com/EvanTheB/5b64eafb84eeaf51c289295ac06e1b0b
Evan Benn
¿Cuántas carreras promediaste?
Muru
No hice promedios, pero estoy viendo tiempos de ejecución consistentes mientras repito e investigo.
Evan Benn
Aquí hay un archivo similar al que estoy usando si quieres investigar:seq 30 | xargs -L1 bash -cs 'yes $1 | head -1000000 | paste - <(seq 1000000) | shuf' bash
Evan Benn
12

Podría usar una transformación de Schwartz (esto es básicamente el enfoque de decorar-ordenar-decorar al que aludió en un comentario, pero probablemente más eficaz que la respuesta fina de muru debido al uso de una sola sortinvocación en lugar de múltiples) - usando awkagregar una columna de prefijo que se incrementa con un cambio de valor en la primera columna, ordene por la columna de prefijo seguida de la "segunda" columna (cuya posición ordinal se ha desplazado temporalmente 3debido a la presencia de la columna de prefijo), y finalmente elimine la columna de prefijo

awk '{print ($1 in a? c+0: ++c)"\t" $0; a[$1]}' file | sort -k1,1n  -k3,3 | cut -f 2-
iruvar
fuente
Estoy sorprendido, pero tienes razón, ¡esto fue más rápido que la otra respuesta! 3 minutos frente a 2 minutos en mi archivo de 100 millones de líneas (~ 30 primeras columnas uniq).
Evan Benn
1
No es necesario mantener una matriz de la clave única de la primera columna. Creo que debería ser suficiente comparar la primera columna de la línea actual con la anterior.
Kusalananda
Algo como awk -v OFS="\t" '$1 != prev { key++ } { print key, $0; prev = $1 }(no probado).
Kusalananda