підсумовуйте пари стовпців на основі відповідних полів


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?
glenn jackman

@glennjackman: У стовпці 1 є однакове значення для кожного файлу. Він служить ідентифікатором для файлу (у мене 45 таких) і буде використовуватися для деякого подальшого процесу. Що стосується мого запитання, він може бути проігнорований (або видалений), а потім знову доданий.
TomPio

або, зробіть $1 $2як ключ.
glenn jackman

Відповіді:


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-й стовпчик. Ідея полягає у використанні першого та другого стовпців як ключів до хешу (в перлі) або асоціативного масиву (в 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/…
glenn jackman

@taliezin: Дякую талієзину та тердону. Обидва підходи спрацювали як шарм. Я дуже ціную вашу допомогу.
TomPio

1
@taliezin: Як я сказав, що обидва працювали на мене, я позначив відповіді тердона як "правильні". Я думаю, це те, що ти задумав. Знову дякую.
TomPio

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

1
Це майже те саме: awk '{map [$ 2] + = $ 3 + $ 4; oc [$ 2] ++; } END {for (i на карті) {print "2", i, map [i], oc [i] | "сортувати -t'n '"; }} ', тепер ви побачите інший стовпець із подіями.
taliezin

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.