soma o par de colunas com base nos campos correspondentes

10

Encontrei um problema que excede meu conhecimento básico de unix e realmente apreciaria alguma ajuda. 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 coincidirem, quero somar os valores na coluna 3 e 4 de ambas as linhas, senão apenas a soma dos valores na linha exclusiva.

Assim, 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 os arquivos de acordo com coluna 2 com awk ou sort e somar as últimas colunas com awk , mas somente para linhas individuais não para duas linhas onde coluna 2 corresponde.

    
por TomPio 18.03.2015 / 12:42

4 respostas

10

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, você pode canalizar para sort :

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

Observe que as duas soluções também incluem a primeira coluna. A idéia é usar a primeira e a segunda colunas como chaves para um hash (em perl) ou um array associativo (em awk). A chave em cada solução é column1 column2 , portanto, 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
    
por 18.03.2015 / 13:21
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 classificação:

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

Você pode classificar os dados e deixar o awk lidar com os detalhes:

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

Você pode querer redefinir o acumulador:

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

Saída:

1019 15
1021 19
1022 28
1030 34

Se você realmente quiser 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}'

Saída:

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

Explicação

A variável p contém o valor $2 da linha anterior ou $1FS$2 no segundo caso acima. Isso significa que {print p,s} é acionado quando $2 da linha anterior não é igual ao da linha atual ( p!=$2 ).

    
por 18.03.2015 / 13:12
0

Usando o utilitário canivete suíço mlr :

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

Saída:

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

Notas:

  • --nidx indica mlr para usar nomes de campo numéricos.

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

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

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

por 07.03.2018 / 16:08