suma pary kolumn na podstawie pasujących pól

11

Mam duży plik w następującym formacie:

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

Jeśli wartości w kolumnie 2 są zgodne, chcę zsumować wartości w kolumnie 3 i 4 obu wierszy, w przeciwnym razie tylko suma wartości w unikalnym wierszu.

Tak więc wynik, na który mam nadzieję, wygląda następująco:

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

Jestem w stanie posortować pliki według kolumny 2 za pomocą awklub sorti zsumować ostatnie kolumny awk, ale tylko dla pojedynczych linii, a nie dla dwóch linii, w których kolumna 2 pasuje.

TomPio
źródło
1
Co z kolumną 1?
glenn jackman
@glennjackman: Kolumna 1 ma tę samą wartość w każdym pliku. Służy jako identyfikator pliku (mam 45 takich) i będzie używany w dalszym procesie. W przypadku mojego pytania równie dobrze można go zignorować (lub usunąć), a następnie dodać ponownie.
TomPio,
lub, $1 $2jako klucz.
glenn jackman

Odpowiedzi:

12

Zrobiłbym to w Perlu:

$ 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

Lub awk:

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

Jeśli chcesz posortować dane wyjściowe według drugiej kolumny, możesz po prostu potokować do sort:

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

Pamiętaj, że oba rozwiązania obejmują również pierwszą kolumnę. Ideą jest użycie pierwszej i drugiej kolumny jako kluczy do skrótu (w perlu) lub tablicy asocjacyjnej (w awk). Klucz w każdym rozwiązaniu jest column1 column2taki, że jeśli dwa wiersze mają tę samą kolumnę drugą, ale inną kolumnę jedną, zostaną one zgrupowane osobno:

$ 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
terdon
źródło
7

Może to może pomóc, ale czy kolumna 1 jest zawsze 2 i czy od tego zależą wyniki?

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

lub jak wspomniał glenn jackman w komentarzach na temat sortowania:

gawk '{ map[$2] += $3 + $4; } END { PROCINFO["sorted_in"] = "@ind_str_asc"; for (i in map) { print 2, i, map[i] } }' file
taliezin
źródło
2
Jeśli masz GNU awk, użyj PROCINFO["sorted_in"] = "@ind_num_asc"zamiast potokowania do sort. ref gnu.org/software/gawk/manual/html_node/…
glenn jackman
@taliezin: Dzięki taliezin i terdon. Oba podejścia działały jak urok. Doceniam twoją pomoc.
TomPio,
1
@taliezin: Jak powiedziałem, oba działały dla mnie, oznaczyłem odpowiedzi terdon jako „poprawne”. Myślę, że to właśnie zamierzałeś. Dzięki jeszcze raz.
TomPio,
1
Jeśli rozumiem pytanie, które chcesz uzyskać całkowitą liczbę unikalnych kluczy, możemy po prostu dodać licznik i wydrukować go: awk '{mapa [2 USD] + = 3 USD + 4 USD; } END {for (i in map) {print "2", i, map [i] | „sort -t'n '”; cnt ++; } wydrukuj plik „total unique:„ cnt} ”
taliezin
1
Jest prawie tak samo: awk '{mapa [2 USD] + = 3 USD + 4 USD; oc [2 $] ++; } END {for (i in map) {print "2", i, map [i], oc [i] | „sort -t'n '”; }} ”, teraz zobaczysz kolejną kolumnę z wystąpieniami.
taliezin
4

Możesz wstępnie posortować dane i pozwolić awk zająć się szczegółami:

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

Możesz zresetować akumulator:

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

Wynik:

1019 15
1021 19
1022 28
1030 34

Jeśli naprawdę chcesz zachować pierwszą kolumnę, zrób coś takiego:

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

Wynik:

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

Wyjaśnienie

pZmienna posiada $2wartość poprzedniej linii, albo $1FS$2w drugim przypadku powyżej. Oznacza to, że {print p,s}wyzwalane jest, gdy $2poprzednia linia nie jest taka sama jak ta w bieżącej linii ( p!=$2).

Thor
źródło
zwróć uwagę, że nawet jeśli pierwsza kolumna ma inne wartości, których możesz użyć sort -k2do sortowania według drugiej kolumny
gaoithe,
2

Korzystanie szwajcarski scyzoryk util mlr:

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

Wynik:

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

Uwagi:

  • --nidxkaże mlrużywać nazw pól numerycznych.

  • put '$5=$3+$4'tworzy nowe 5. pole, sumę pól 3 i 4 .

  • stats1Funkcja (lub „ czasownik ”) jest mniejsza armii szwajcarskiej
    w większej Swiss army nóż mlrz kilku funkcji, takich jak na bazie akumulatorów sum, count, mean, etc.

    stats1 -g 1,2grupuje dane według kolumn 1 i 2 , a -f 5 -a sumnastępnie sumuje pole tych grup 5 . stats1 drukuje tylko nazwane pola.

agc
źródło