GROUP BY / SUM dalla shell

Ho un file di grandi dimensioni contenente dati come questo:

a 23 b 8 a 22 b 1 

Voglio essere in grado di ottenere questo:

 a 45 b 9 

Posso prima ordinare questo file e poi farlo in Python eseguendo la scansione del file una volta. Che cosa è un buon modo diretto da linea di comando per farlo?

Modifica: la soluzione moderna (GNU / Linux), come menzionato nei commenti anni fa ;-).

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

La soluzione originariamente pubblicata, basata su vecchie opzioni di sort Unix:

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

Spero che aiuti.

Non c’è bisogno di awk qui, o nemmeno di ordinare – se hai Bash 4.0, puoi usare gli array associativi:

 #!/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, se si ordina prima il file (che sarà più efficiente in termini di memoria, GNU sort è in grado di eseguire trucchi per ordinare file più grandi della memoria, che uno script ingenuo – sia in awk, python o shell – tipicamente no), puoi farlo in un modo che funzionerà in versioni precedenti (mi aspetto che quanto segue funzioni tramite 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" 

Questo rivestimento di Perl sembra fare il lavoro:

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

Un modo 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 

Contenuto di infile :

 a 23 b 8 a 22 b 1 

Produzione:

 a 45 b 9 

Con GNU awk (versioni meno di 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 

Questo può essere facilmente ottenuto con il seguente single-liner:

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

O.

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

Qui viene usato un pacchetto Python, termsql , che è un wrapper attorno a SQLite. Nota che al momento non è caricato su PyPI , e può anche essere installato solo a livello di sistema ( setup.py è un po ‘rotto), come:

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