soma par de colunas com base em campos correspondentes


11

Eu tenho um arquivo grande no seguinte 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 os valores na coluna 2 corresponderem, quero somar os valores nas colunas 3 e 4 de ambas as linhas, senão apenas a soma dos valores na linha exclusiva.

Portanto, a saída que eu espero seria assim:

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

Eu posso classificar arquivos de acordo com a coluna 2 com awkou sorte somar as últimas colunas com awk, mas apenas para linhas individuais, não para duas linhas em que a coluna 2 corresponde.


1
E a coluna 1?
Glenn Jackman

@glennjackman: a coluna 1 tem o mesmo valor em cada arquivo. Ele serve como um identificador para o arquivo (eu tenho 45 deles) e será usado em alguns processos posteriores. Para a minha pergunta, também pode ser ignorado (ou excluído) e depois adicionado novamente.
TomPio 18/03/2015

ou faça $1 $2como a chave.
Glenn Jackman

Respostas:


12

Eu faria isso em 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

Ou awk:

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

Se você deseja que a saída seja classificada de acordo com a segunda coluna, basta canalizar para sort:

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

Observe que ambas as soluções também incluem a 1ª coluna. A idéia é usar a primeira e a segunda colunas como chaves para um hash (em perl) ou uma matriz associativa (em awk). A chave em cada solução é column1 column2que, se duas linhas tiverem a mesma coluna dois, mas uma coluna diferente, elas serão agrupadas separadamente:

$ 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

Talvez isso possa ajudar, mas a coluna 1 é sempre 2 e os resultados dependem disso?

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

ou como mencionado por glenn jackman nos comentários sobre a classificação:

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

2
Se você possui o GNU awk, use em PROCINFO["sorted_in"] = "@ind_num_asc"vez de canalizar para sort. ref gnu.org/software/gawk/manual/html_node/…
glenn jackman

@taliezin: Obrigado, Taliezin e Terdon. Ambas as abordagens funcionaram como um encanto. Eu realmente aprecio sua ajuda.
TomPio 18/03/2015

1
@taliezin: Como eu disse, os dois funcionaram para mim, marquei as respostas do terdon como as "corretas". Eu acho que é isso que você pretendia. Obrigado novamente.
TomPio 20/03/2015

1
Se entendi a pergunta que você quer sobre o total de chaves exclusivas, podemos simplesmente adicionar um contador e imprimi-lo: awk '{map [$ 2] + = $ 3 + $ 4; } END {para (i no mapa) {print "2", i, map [i] | "classificar -t'n '"; cnt ++; } imprima o arquivo "total unique:" cnt} '
23-15-15:

1
É quase o mesmo: awk '{map [$ 2] + = $ 3 + $ 4; oc [$ 2] ++; } END {para (i no mapa) {print "2", i, map [i], oc [i] | "classificar -t'n '"; }} ', agora você verá outra coluna com ocorrências.
taliezin

4

Você pode pré-classificar os dados e deixar o awk manipular os detalhes:

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

Você pode redefinir o acumulador:

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

Resultado:

1019 15
1021 19
1022 28
1030 34

Se você realmente deseja manter a primeira coluna, faça algo assim:

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

Resultado:

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

Explicação

A pvariável mantém o $2valor da linha anterior, ou $1FS$2no segundo caso acima. Isso significa que o {print p,s}disparador é disparado quando $2a linha anterior não é a mesma da linha atual ( p!=$2).


nota que, mesmo se a primeira coluna tinham valores diferentes que você pode usar sort -k2para classificar pela segunda coluna
gaoithe

2

Usando o canivete suíço util mlr:

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

Resultado:

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

Notas:

  • --nidxdiz mlrpara usar nomes de campos numéricos.

  • put '$5=$3+$4'cria um novo campo, a soma dos campos 3 e 4 .

  • A stats1função (ou " verbo ") é um canivete suíço menor
    dentro do maior canivete suíço de mlr, com várias funções baseadas acumulador, tais como sum, count, mean, etc.

    stats1 -g 1,2agrupa os dados pelas colunas 1 e 2 e -f 5 -a sumadiciona o campo 5 desses grupos . stats1 imprime apenas campos nomeados.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.