somma coppia di colonne in base ai campi corrispondenti


11

Ho un file di grandi dimensioni nel seguente formato:

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

Se i valori nella colonna 2 corrispondono, voglio sommare i valori nella colonna 3 e 4 di entrambe le righe, altrimenti solo la somma dei valori nella riga univoca.

Quindi l'output che spero sarebbe simile a questo:

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

Sono in grado di ordinare i file in base alla colonna 2 con awko sorte sommare le ultime colonne con awk, ma solo per le singole righe non per due righe in cui la colonna 2 corrisponde.


1
E la colonna 1?
Glenn Jackman,

@glennjackman: la colonna 1 ha lo stesso valore in ogni file. Serve come identificatore per il file (ne ho 45) e verrà utilizzato per alcuni processi downstream. Per la mia domanda potrebbe anche essere ignorato (o eliminato) e successivamente aggiunto di nuovo.
TomPio

oppure, crea $1 $2come chiave.
Glenn Jackman,

Risposte:


12

Lo farei in 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

O awk:

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

Se desideri che l'output sia ordinato in base alla seconda colonna, puoi semplicemente eseguire il pipe a sort:

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

Si noti che entrambe le soluzioni includono anche la 1a colonna. L'idea è di usare la prima e la seconda colonna come chiavi per un hash (in perl) o un array associativo (in awk). La chiave in ogni soluzione è column1 column2quindi se due righe hanno la stessa colonna due ma una colonna diversa, verranno raggruppate separatamente:

$ 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

Forse questo potrebbe aiutare, ma la colonna 1 è sempre 2 e i risultati dipendono da essa?

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

o come menzionato da Glenn Jackman nei commenti sull'ordinamento:

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

2
Se hai GNU awk, usa PROCINFO["sorted_in"] = "@ind_num_asc"invece di piping to sort. ref gnu.org/software/gawk/manual/html_node/…
glenn jackman

@taliezin: grazie taliezin e terdon. Entrambi gli approcci hanno funzionato come un fascino. Apprezzo molto il vostro aiuto.
TomPio

1
@taliezin: Come ho detto, entrambi hanno funzionato per me, ho contrassegnato le risposte del terdon come "corrette". Immagino sia quello che volevi. Grazie ancora.
TomPio

1
Se capisco la domanda che vuoi avere le chiavi univoche totali, possiamo semplicemente aggiungere un contatore e stamparlo: awk '{map [$ 2] + = $ 3 + $ 4; } END {for (i in map) {print "2", i, map [i] | "sort -t'n '"; cnt ++; } stampa il file "total unique:" cnt} '
taliezin

1
È quasi lo stesso: awk '{map [$ 2] + = $ 3 + $ 4; oc [$ 2] ++; } END {for (i in map) {print "2", i, map [i], oc [i] | "sort -t'n '"; }} ", ora vedrai un'altra colonna con occorrenze.
Taliezin,

4

È possibile preordinare i dati e lasciare che awk gestisca i dettagli:

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

Potrebbe essere necessario ripristinare l'accumulatore:

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

Produzione:

1019 15
1021 19
1022 28
1030 34

Se vuoi davvero mantenere la prima colonna, fai qualcosa del genere:

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

Produzione:

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

Spiegazione

La pvariabile contiene il $2valore della riga precedente o $1FS$2nel secondo caso sopra. Ciò significa che {print p,s}viene attivato quando $2la riga precedente non è uguale a quella sulla riga corrente ( p!=$2).


si noti che anche se la prima colonna avesse valori diversi, è possibile utilizzare sort -k2per ordinare in base alla seconda colonna
gaoithe,

2

Usando il coltellino svizzero util mlr:

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

Produzione:

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

Appunti:

  • --nidxdice mlrdi usare nomi di campi numerici.

  • put '$5=$3+$4'crea un nuovo quinto campo, la somma dei campi 3 e 4 .

  • La stats1funzione (o " verbo ") è un coltellino svizzero minore
    all'interno del maggiore coltellino svizzero mlr, con diverse funzioni basate accumulatori quali sum, count, mean, etc.

    stats1 -g 1,2raggruppa i dati in base alle colonne 1 e 2 , -f 5 -a sumquindi aggiunge il campo 5 di tali gruppi . stats1 stampa solo i campi con nome.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.