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

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)

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-

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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.