сумма пары столбцов на основе совпадающих полей


11

У меня есть большой файл в следующем формате:

2 1019 0 12 
2 1019 3 0 
2 1021 0 2 
2 1021 2 0 
2 1022 4 5
2 1030 0 1 
2 1030 5 0 
2 1031 4 4

Если значения в столбце 2 совпадают, я хочу суммировать значения в столбцах 3 и 4 обеих строк, иначе просто сумму значений в уникальной строке.

Итак, результат, на который я надеюсь, будет выглядеть так:

2 1019 15 
2 1021 4 
2 1022 9 
2 1030 6 
2 1031 8

Я могу сортировать файлы по столбцу 2 с awkили sortи суммировать последние столбцы с awk, но только для отдельных строк, а не для двух строк, где столбец 2 совпадает.


1
Как насчет столбца 1?
Гленн Джекман

@glennjackman: столбец 1 имеет одинаковое значение в каждом файле. Он служит идентификатором для файла (у меня их 45) и будет использоваться для некоторых последующих процессов. На мой вопрос это может быть проигнорировано (или удалено) и позже добавлено снова.
TomPio

или сделать $1 $2как ключ.
Гленн Джекман

Ответы:


12

Я бы сделал это в Perl:

$ perl -lane '$k{"$F[0] $F[1]"}+=$F[2]+$F[3]; 
              END{print "$_ $k{$_}" for keys(%k) }' file 
2 1019 15
2 1021 4
2 1030 6
2 1031 8
2 1022 9

Или awk:

awk '{a[$1" "$2]+=$3+$4}END{for (i in a){print i,a[i]}}' file 

Если вы хотите, чтобы выходные данные были отсортированы в соответствии со вторым столбцом, вы можете просто направить sort:

awk '{a[$1" "$2]+=$3+$4}END{for (i in a){print i,a[i]}}' file | sort -k2

Обратите внимание, что оба решения включают 1-й столбец. Идея состоит в том, чтобы использовать первый и второй столбцы в качестве ключей для хеша (в perl) или ассоциативного массива (в awk). Ключ в каждом решении column1 column2таков: если две строки имеют одинаковый столбец два, но другой столбец один, они будут сгруппированы отдельно:

$ cat file
2 1019 2 3
2 1019 4 1
3 1019 2 2

$ awk '{a[$1" "$2]+=$3+$4}END{for (i in a){print i,a[i]}}' file
3 1019 4
2 1019 10

7

Может быть, это может помочь, но всегда ли столбец 1 равен 2 и зависят ли результаты от этого?

awk '{ map[$2] += $3 + $4; } END { for (i in map) { print "2", i, map[i] | "sort -t't'" } }' file

или как упомянуто Гленном Джекманом в комментариях о сортировке:

gawk '{ map[$2] += $3 + $4; } END { PROCINFO["sorted_in"] = "@ind_str_asc"; for (i in map) { print 2, i, map[i] } }' file

2
Если у вас есть GNU awk, используйте PROCINFO["sorted_in"] = "@ind_num_asc"вместо конвейера sort. ref gnu.org/software/gawk/manual/html_node/…
Гленн Джекман

@taliezin: Спасибо, Тализин и Тердон. Оба подхода работали как шарм. Я действительно ценю твою помощь.
TomPio

1
@taliezin: Как я уже сказал, оба работали на меня, я отметил ответы тердонов как «правильные». Я думаю, это то, что вы хотели. Еще раз спасибо.
TomPio

1
Если я понимаю вопрос о том, нужно ли вам общее количество уникальных ключей, мы можем просто добавить счетчик и напечатать его: awk '{map [$ 2] + = $ 3 + $ 4; } END {for (i на карте) {вывести «2», i, map [i] | "sort -t'n '"; CNT ++; } print "total unique:" cnt} 'file
taliezin

1
Это почти то же самое: awk '{map [$ 2] + = $ 3 + $ 4; ос [$ 2] ++; } END {for (i на карте) {вывести «2», i, map [i], oc [i] | "sort -t'n '"; }} ', теперь вы увидите еще один столбец с вхождениями.
Талиезин

4

Вы можете предварительно отсортировать данные и позволить awk обработать детали:

sort -n infile | awk 'NR>1 && p!=$2 {print p,s} {s+=$3+$4} {p=$2}'

Вы можете сбросить аккумулятор:

sort -n infile | awk 'NR>1 && p!=$2 {print p,s;s=0} {s+=$3+$4} {p=$2}'

Выход:

1019 15
1021 19
1022 28
1030 34

Если вы действительно хотите сохранить первый столбец, сделайте что-то вроде этого:

sort -n infile | awk 'NR>1 && p!=$1FS$2 {print p,s} {s+=$3+$4} {p=$1FS$2}'

Выход:

2 1019 15
2 1021 19
2 1022 28
2 1030 34

объяснение

pПеременная содержит $2значение предыдущей строки, или $1FS$2во втором случае выше. Это означает, что {print p,s}срабатывает, когда $2предыдущая строка отличается от текущей строки ( p!=$2).


обратите внимание, что даже если в первом столбце были другие значения, вы можете использовать sort -k2сортировку по второму столбцу
gaoithe

2

Использование швейцарского армейского ножа mlr:

mlr --nidx   put '$5=$3+$4'   then   stats1 -g 1,2 -f 5 -a sum   infile

Выход:

2   1019    15
2   1021    4
2   1022    9
2   1030    6
2   1031    8

Заметки:

  • --nidxговорит mlrиспользовать числовые имена полей.

  • put '$5=$3+$4'делает новое 5-е поле, сумма полей 3 и 4 .

  • stats1Функция (или « глагол ») является меньшим швейцарским армейским ножом
    в большем швейцарском армейском ноже mlr, с несколькими функций на основе аккумуляторных , такие как sum, count, mean, и т.д.

    stats1 -g 1,2группирует данные по столбцам 1 и 2 , а -f 5 -a sumзатем складывает поле 5 этих групп . stats1 печатает только именованные поля.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.