2012-04-23 12 views
26

Tengo un archivo de gran tamaño que contiene los datos de la siguiente manera:GROUP BY/SUM de Shell

a 23 
b 8 
a 22 
b 1 

Quiero ser capaz de conseguir esto:

a 45 
b 9 

primera vez que puedo solucionar este archivo y luego hazlo en Python escaneando el archivo una vez. ¿Cuál es una buena forma directa de hacer línea de comandos?

Respuesta

26
awk '{ 
    arr[$1]+=$2 
    } 
    END { 
    for (key in arr) printf("%s\t%s\n", key, arr[key]) 
    }' file \ 
    | sort +0n -1 

Espero que esto ayude.

+0

lo que es exactamente lo que hacen los argumentos para ordenar? No los veo en la página del manual y la página de invocación me ha dejado confundido. – EricR

+1

Las versiones modernas de género prefieren la sintaxis '-k' para especificar claves de clasificación:' ordenar -nk1,1' en lugar de 'ordenar + 0n -1'. Pero dado que las teclas son letras, ¿por qué estás especificando '-n' de todos modos? –

+0

@EricR: '+ 0n -1' está pasado de moda para' -n -k1,1': ordenar numéricamente por el primer campo (separado por espacios en blanco). –

8

No hay necesidad de awk aquí, o incluso ordenar - si tiene Bash 4.0, se pueden utilizar matrices asociativas:

#!/bin/bash 
declare -A values 
while read key value; do 
    values["$key"]=$(($value + ${values[$key]:-0})) 
done 
for key in "${!values[@]}"; do 
    printf "%s %s\n" "$key" "${values[$key]}" 
done 

... o, si ordena primero el archivo (que habrá más eficiente en memoria; GNU sort puede hacer trucos para ordenar archivos más grandes que la memoria, lo que un script ingenuo, ya sea en awk, python o shell, normalmente no lo hará), puedes hacer esto de una manera que funcione en versiones anteriores (I esperan que el siguiente para trabajar a través de bash 2.0):

#!/bin/bash 
read cur_key cur_value 
while read key value; do 
    if [[ $key = "$cur_key" ]] ; then 
    cur_value=$((cur_value + value)) 
    else 
    printf "%s %s\n" "$cur_key" "$cur_value" 
    cur_key="$key" 
    cur_value="$value" 
    fi 
done 
printf "%s %s\n" "$cur_key" "$cur_value" 
+3

Diablos, con un munging mínimo lo anterior funcionaría en Bourne vainilla, no se requiere bash. Mientras se lee el valor clave; do if ["$ key" = "$ cur_key"]; luego cur_value = \ 'expr $ cur_value + $ value \ '; else echo "$ cur_key $ cur_value"; cur_key = "$ key"; cur_value = "$ value"; fi; hecho; echo "$ cur_key $ cur_value" ' –

+2

@MarkReed, definitivamente, aunque el impacto en el rendimiento de la subshell que ejecuta' expr' es suficiente para que la expansión POSIX sh '$ (())' sea mejor; mientras que '(())' es una extensión bash, '$ (())' es estándar-obediente; es solo Bourne-sh anterior a 1991-POSIX donde se necesita 'expr'. –

2

Una forma usando perl:

perl -ane ' 
    next unless @F == 2; 
    $h{ $F[0] } += $F[1]; 
    END { 
     printf qq[%s %d\n], $_, $h{ $_ } for sort keys %h; 
    } 
' infile 

contenido de infile:

a 23 
b 8 
a 22 
b 1 

de salida:

a 45 
b 9 
2

Con GNU awk (versiones de menos de 4):

WHINY_USERS= awk 'END { 
    for (E in a) 
    print E, a[E] 
    } 
{ a[$1] += $2 }' infile 

Con GNU awk> = 4:

awk 'END { 
    PROCINFO["sorted_in"] = "@ind_str_asc" 
    for (E in a) 
    print E, a[E] 
    } 
{ a[$1] += $2 }' infile 
+0

Parece que he tropezado con esto casi tres años tarde. ¿Qué hace exactamente la variable 'WHINY_USERS'? –

+1

Ordena las teclas de matriz en [orden cifrado] (http://awk.info/?doc/tip/whinyUsers.html). –

+2

El enlace está muerto, este es más probable que viva: https://stackoverflow.com/q/11697556/476716 – OrangeDog

6

Este Perl de una sola línea parece hacer el trabajo:

perl -nle '($k, $v) = split; $s{$k} += $v; END {$, = " "; foreach $k (sort keys %s) {print $k, $s{$k}}}' inputfile 
0

Esto se puede lograr fácilmente con el siguiente single-liner:

cat /path/to/file | termsql "SELECT col0, SUM(col1) FROM tbl GROUP BY col0" 

Or.

termsql -i /path/to/file "SELECT col0, SUM(col1) FROM tbl GROUP BY col0" 

Aquí un paquete Python, termsql, se utiliza, que es una envoltura alrededor de SQLite. Tenga en cuenta, que en la actualidad no es subir a PyPI, y sólo se puede instalar en todo el sistema (setup.py está un poco roto), como:

sudo pip install https://github.com/tobimensch/termsql/archive/master.zip