suma par de columnas basadas en campos coincidentes

11

Tengo un archivo grande en el siguiente 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

Si los valores en la columna 2 coinciden, quiero sumar los valores en la columna 3 y 4 de ambas líneas, de lo contrario solo la suma de los valores en la línea única.

Entonces el resultado que espero se vería así:

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

Puedo ordenar los archivos de acuerdo con la columna 2 con awko sorty sumar las últimas columnas con awk, pero solo para líneas individuales, no para dos líneas donde la columna 2 coincide.

TomPio
fuente
1
¿Qué pasa con la columna 1?
Glenn Jackman
@glennjackman: la columna 1 tiene el mismo valor en cada archivo. Sirve como un identificador para el archivo (tengo 45 de esos) y se utilizará para algún proceso posterior. Para mi pregunta, bien podría ignorarse (o eliminarse) y luego agregarse nuevamente.
TomPio
o hacer $1 $2como la clave.
Glenn Jackman

Respuestas:

12

Haría esto en 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 

Si desea que la salida se ordene de acuerdo con la segunda columna, simplemente puede canalizar sort:

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

Tenga en cuenta que ambas soluciones incluyen la primera columna también. La idea es usar la primera y segunda columnas como claves para un hash (en perl) o una matriz asociativa (en awk). La clave en cada solución es column1 column2que si dos líneas tienen la misma columna dos pero una columna diferente, se agruparán por separado:

$ 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
fuente
7

Quizás esto podría ayudar, pero ¿la columna 1 es siempre 2 y los resultados dependen de ello?

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

o como lo mencionó Glenn Jackman en los comentarios sobre la clasificación:

gawk '{ map[$2] += $3 + $4; } END { PROCINFO["sorted_in"] = "@ind_str_asc"; for (i in map) { print 2, i, map[i] } }' file
taliezin
fuente
2
Si tienes GNU awk, úsalo en PROCINFO["sorted_in"] = "@ind_num_asc"lugar de conectarlo a sort. ref gnu.org/software/gawk/manual/html_node/…
glenn jackman
@taliezin: Gracias taliezin y terdon. Ambos enfoques funcionaron como un encanto. Realmente aprecio tu ayuda.
TomPio
1
@taliezin: Como dije que ambos funcionaban para mí, marqué las respuestas de terdon como las "correctas". Supongo que eso es lo que pretendías. Gracias de nuevo.
TomPio
1
Si entiendo la pregunta que desea el total de claves únicas, podemos agregar un contador e imprimirlo: awk '{map [$ 2] + = $ 3 + $ 4; } END {para (i en el mapa) {print "2", i, map [i] | "sort -t'n '"; cnt ++; } print "total unique:" cnt} 'file
taliezin
1
Es casi lo mismo: awk '{map [$ 2] + = $ 3 + $ 4; oc [$ 2] ++; } END {para (i en el mapa) {imprimir "2", i, mapa [i], oc [i] | "sort -t'n '"; }} ', ahora verá otra columna con ocurrencias.
taliezin
4

Puede ordenar previamente los datos y dejar que awk maneje los detalles:

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

Es posible que desee restablecer el acumulador:

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

Salida:

1019 15
1021 19
1022 28
1030 34

Si realmente desea mantener la primera columna, haga algo como esto:

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

Salida:

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

Explicación

La pvariable contiene el $2valor de la línea anterior, o $1FS$2en el segundo caso anterior. Esto significa que {print p,s}se activa cuando $2la línea anterior no es la misma que la de la línea actual ( p!=$2).

Thor
fuente
tenga en cuenta que incluso si la primera columna tuviera valores diferentes que podría usar sort -k2para ordenar por la segunda columna
gaoithe
2

Usando util navaja suiza mlr:

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

Salida:

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

Notas:

  • --nidxle dice mlrque use nombres de campo numéricos.

  • put '$5=$3+$4'crea un nuevo quinto campo, la suma de los campos 3 y 4 .

  • La stats1función (o " verbo ") es una navaja suiza más pequeño
    dentro de la mayor navaja suiza de mlr, con varias funciones basadas acumuladores tales como sum, count, mean, etc.

    stats1 -g 1,2agrupa los datos por las columnas 1 y 2 , y -f 5 -a sumluego suma el campo 5 de esos grupos . stats1 imprime solo los campos con nombre.

agc
fuente