2012-04-23 4 views
26

У меня есть большой файл, содержащий данные, как это:GROUP BY/SUM из оболочки

a 23 
b 8 
a 22 
b 1 

Я хочу, чтобы иметь возможность получить это:

a 45 
b 9 

Я первый сортировать этот файл, а затем сделайте это в Python, сканируя файл один раз. Каков хороший прямой способ командной строки?

ответ

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

Надеюсь, это поможет.

+0

, что именно делает эти аргументы делают для сортировки? Я не вижу их на странице руководства, и страница с вызовом оставила меня в замешательстве. – EricR

+1

Современные версии сортировки предпочитают синтаксис '-k' для указания ключей сортировки:' sort -nk1,1' вместо 'sort + 0n -1'. Но поскольку ключи являются буквами, почему вы все-таки указываете '-n'? –

+0

@EricR: '+ 0n -1' старомодно для' -n -k1,1': сортировать численно первым (разделенным пробелом) полем. –

8

Нет необходимости AWK здесь, или даже своего рода - если у вас есть Bash 4.0, вы можете использовать ассоциативные массивы:

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

... или, если отсортировать файл первым (который будет более экономия памяти, GNU sort может делать трюки для сортировки файлов, больших, чем память, что наивный скрипт - будь то в awk, python или shell - обычно не будет), вы можете сделать это таким образом, который будет работать в более старые версии (я ожидаю, что следующие работы через Баш 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

Черт возьми, с некоторым минимальным переделкой выше будет работать в ваниль Бурне, не требуется bash.'то прочитайте значение ключа; do if ["$ key" = "$ cur_key"]; затем cur_value = \ 'expr $ cur_value + $ value \'; else echo "$ cur_key $ cur_value"; cur_key = "$ key"; cur_value = "$ value"; fi; сделано; echo "$ cur_key $ cur_value" ' –

+2

@MarkReed, определенно так, хотя влияние производительности подоболочки, использующей' expr', достаточно, чтобы расширение POSIX sh '$ (()) было лучше; в то время как '(())' является расширением bash, '$ (())' является стандартным; это только pre-1991-POSIX-стандарт Bourne sh, где требуется 'expr'. –

2

Один из способов, используя perl:

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

Содержание infile:

a 23 
b 8 
a 22 
b 1 

Выход:

a 45 
b 9 
2

С ГНУ AWK (версии менее 4):

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

С GNU AWK> = 4:

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

Я, кажется, наткнулся на это почти на три года позже. Что именно делает переменная 'WHINY_USERS'? –

+1

Сортирует ключи массива в [asciibetical порядке] (http://awk.info/?doc/tip/whinyUsers.html). –

+2

Ссылка мертва, этот, скорее всего, будет жить: https://stackoverflow.com/q/11697556/476716 – OrangeDog

6

Это Perl один вкладыш, кажется, чтобы сделать работу:

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

Это может быть легко достигнуто с единственным лайнером:

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" 

Вот пакет Python, termsql, используется, который является оберткой SQLite. Обратите внимание, что в настоящее время это не загружать на PyPI, а также может быть установлено только в масштабе всей системы (setup.py немного сломан), как:

sudo pip install https://github.com/tobimensch/termsql/archive/master.zip 
Смежные вопросы