2012-04-23 1 views
26

Ich habe eine große Datei Daten wie folgt enthält:GROUP BY/SUM von Shell

a 23 
b 8 
a 22 
b 1 

Ich möchte dies umgehen können:

a 45 
b 9 

ich zum ersten Mal diese Datei sortieren und dann Machen Sie es in Python, indem Sie die Datei einmal scannen. Was ist eine gute direkte Befehlszeile, um dies zu tun?

Antwort

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

Ich hoffe, das hilft.

+0

was genau diese Argumente tun zu sortieren? Ich sehe sie nicht in der Manpage und die Aufrufseite hat mich verwirrt. – EricR

+1

Moderne Versionen von sort bevorzugen die '-k'-Syntax zum Angeben von Sortierschlüsseln: 'sort -nk1,1' anstelle von' sort + 0n -1'. Aber da die Schlüssel Buchstaben sind, warum gibst du dann '-n' an? –

+0

@EricR: '+ 0n -1' ist altmodisch für' -n -k1,1': numerisch sortiert nach dem ersten (Leerzeichen getrennten) Feld. –

8

Keine Notwendigkeit für awk hier, oder sogar Art - wenn Sie Bash 4.0 haben, können Sie assoziative Arrays verwenden:

#!/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 

... oder, wenn Sie die Datei zuerst sortieren (die mehr sein Speicher-effizient, GNU-Sortierung ist in der Lage, Tricks zu tun, um Dateien größer als Speicher zu sortieren, was ein naives Skript - ob in Awk, Python oder Shell - normalerweise nicht tun wird, Sie können dies in einer Weise tun, die funktioniert ältere Versionen (ich erwarte, dass die folgenden durch bash arbeiten 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

Heck, mit etwas minimalem Munging würde das oben in Vanilla Bourne funktionieren, keine Bash erforderlich. "While read key value; do wenn ["$ key" = "$ cur_key"]; dann cur_value = \ 'expr $ cur_value + $ value \ '; sonst echo "$ cur_key $ cur_value"; cur_key = "$ key"; cur_value = "$ value"; fi; getan; echo "$ cur_key $ cur_value" ' –

+2

@MarkReed, definitiv so, obwohl die Leistungseinwirkung der Subshell, die' expr' ausführt, ausreicht, dass die POSIX-sh '$ (())' -Erweiterung besser wäre; while '(())' ist eine Bash-Erweiterung, ist '$ (())' standardkonform; es ist nur BORNE SH vor 1991-POSIX-Standard, wo "expr" benötigt wird. –

2

Eine Möglichkeit, mit perl:

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

Inhalt des infile:

a 23 
b 8 
a 22 
b 1 

Ausgang:

a 45 
b 9 
2

Mit GNU awk (Versionen kleiner als 4):

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

Mit GNU awk> = 4:

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

Ich bin fast drei Jahre zu spät gestolpert. Was genau macht die 'WHINY_USERS' Variable? –

+1

Sortiert die Array-Schlüssel in [ascibetical order] (http://awk.info/?doc/tip/whinyUsers.html). –

+2

Link ist tot, dieser ist wahrscheinlicher zu leben: https://stackoverflow.com/q/11697556/476716 – OrangeDog

6

Dieser Perl-Einzeiler, den Job zu tun scheint:

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

Diese leicht mit den folgenden Single-Liner erreicht werden kann:

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

Or. En

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

Hier ist ein Python-Paket, termsql, verwendet wird, die ein Wrapper um SQLite ist. Beachten Sie, dass derzeit ist es nicht laden zu PyPI, und auch nur systemweit installiert werden kann (setup.py ist ein wenig gebrochen), wie:

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