Soma de valores alternativos em uma coluna usando sed ou nawk

2

foo.txt :

1  10     11
2   20     22
3   30     32
4   40     42
5   50     52
6   60     62
7   70     72
8   80     82
9   90     92
10  100   110

Desejado Out.txt :

1  10     11
2   20     22
3   30     32
4   40     42
5   50     52
6   60     62
7   70     72
8   80     82
9   90     92
10  100   110
25  250   275   #Line 11
30  300   330   #Line 12
45  550   595  #Line 13

A linha 11 é uma soma de linhas alternativas a partir da linha 1 na 1ª, 2ª e 3ª colunas, a linha 12 é a soma de linhas alternativas a partir da linha 2 na 1ª, 2ª e 3ª colunas. A linha 13 é a soma das colunas na linha 11 e na linha 12. Estou usando o KSH e o Solaris 5.10. Os valores no arquivo de entrada podem não ser sequenciais e não terão mais de 3 dígitos inteiros. Meu arquivo de entrada terá apenas 10 linhas. Como conseguir isso?

    
por ayrton_senna 19.06.2015 / 23:35

4 respostas

3
$ awk -v OFS='\t' '{for (i=1;i<=NF;i++) {s[2-NR%2,i]+=$i;s[3,i]+=$i;};$1=$1;print} END{for (n=1;n<=3;n++) print s[n,1],s[n,2],s[n,3]}' foo.txt 
1       10      11
2       20      22
3       30      32
4       40      42
5       50      52
6       60      62
7       70      72
8       80      82
9       90      92
10      100     110
25      250     259
30      300     318
55      550     577

O acima foi testado no GNU awk e linux.

Como funciona

  • -v OFS='\t'

    Opcional: isso define a saída como separada por tabulações.

  • {for (i=1;i<=NF;i++) {s[2-NR%2,i]+=$i;s[3,i]+=$i;}; $1=$1; print}

    Isso percorre cada coluna, adicionando seus valores à matriz s . Para cada coluna i , as linhas pares numeradas são adicionadas a s[2,i] , enquanto as linhas ímpares são adicionadas a s[1,i] . Coluna i em todas as linhas é adicionada a s[3,i] .

    Esta linha é então impressa.

  • END{for (n=1;n<=3;n++) print s[n,1],s[n,2],s[n,3]}

    Depois que chegamos ao final do arquivo, os resultados são impressos, primeiro para as linhas ímpares ( n=1 ), depois para as linhas pares ( n=2 ), e então o total ( n=3 ).

Sun / Solaris

Eu tive vários relatórios que o awk padrão no Sun / Solaris tem problemas. Por favor, tente:

nawk -v OFS='\t' '{for (i=1;i<=NF;i++) {s[2-NR%2,i]+=$i;s[3,i]+=$i;};$1=$1;print} END{for (n=1;n<=3;n++) print s[n,1],s[n,2],s[n,3]}' foo.txt 

Ou:

/usr/xpg4/bin/awk -v OFS='\t' '{for (i=1;i<=NF;i++) {s[2-NR%2,i]+=$i;s[3,i]+=$i;};$1=$1;print} END{for (n=1;n<=3;n++) print s[n,1],s[n,2],s[n,3]}' foo.txt 

Ou:

/usr/xpg6/bin/awk -v OFS='\t' '{for (i=1;i<=NF;i++) {s[2-NR%2,i]+=$i;s[3,i]+=$i;};$1=$1;print} END{for (n=1;n<=3;n++) print s[n,1],s[n,2],s[n,3]}' foo.txt 
    
por 20.06.2015 / 00:01
3

Você quase certamente deseja usar awk para este, não sed . Aqui está um script awk que pode fazer isso:

awk '
    (NR%2) == 1 {
        odd_col_1 += $1;
        odd_col_2 += $2;
        odd_col_3 += $3;
        print $0;
    }
    (NR%2) == 0 {
        even_col_1 += $1;
        even_col_2 += $2;
        even_col_3 += $3;
        print $0;
    }
    END {
        print odd_col_1, odd_col_2, odd_col_3;
        print even_col_1, even_col_2, even_col_3;
        print odd_col_1+even_col_1, odd_col_2+even_col_2, odd_col_3 + even_col_3;
    }
' foo.txt

Isso aproveita a variável interna do número de registro "NR", a maneira como awk divide os arquivos de texto em campos e a construção "END".

    
por 20.06.2015 / 00:00
0

Bem, eu mesmo encontrei uma solução muito básica para esse problema. Mas gostaria que alguém fornecesse uma resposta melhor.

#remove even lines
sed -i '0~2d' foo.txt > oddlines

#oddlines sum
awk '{a=a+$1}{b=b+$2}{c=c+$3}END{print a,b,c}' oddlines > oddlines_sum

#remove even lines
sed -i '1~2d' foo.txt > evenlines

#evenlines sum
awk '{a=a+$1}{b=b+$2}{c=c+$3}END{print a,b,c}' evenlines > evenlines_sum

#combine 
cat evenlines_sum >> oddlines_sum

#for total sum of foo.txt
awk '{a=a+$1}{b=b+$2}{c=c+$3}END{print a,b,c}' foo.txt > foo_sum

#final output
cat oddlines_sum >> foo.txt
cat foo_sum >> foo.txt'

Eu sei que minha solução é muito básica. Mas eu tentei o meu melhor.

    
por 20.06.2015 / 00:01
0
sed '   1x;1s/^/654321/;1x;N;y/ /\n/;G;:t
        s/\([0-9]*\)\n*\n\(.*\)\(.\)/l+s /;tt
        p;$!d;g;s/./l&/g;s/$/fcl3l6+l2l5+l1l4+f/' file |
dc 2>/dev/null |sed '11,$N;/\n/N;s/[^0-9] */\t/g' file -

Isso deve funcionar para você. Ele funciona manipulando alguns pré-processamentos de macro para o dc calculator / compiler com sed in-stream.

Basicamente, sed diz a dc ( bc 's compilador - você deve tê-lo em um sistema Solaris) para manter o controle de 6 valores, para carregá-los uma vez a cada entrada linha, incrementa-os por coluna e, mais uma vez, salva os resultados. Na última linha de entrada, sed diz a dc para chamá-los de volta e imprimir todos os 6 valores para stdout. Para obter os totais da linha 13, tudo o que temos a fazer é acessar nossos totais armazenados novamente e adicioná-los:

l3l6+...f

Descarregamos stderr de dc para /dev/null porque na primeira linha quando tenta l oadar quaisquer valores de qualquer uma das matrizes [123456] , a matriz ainda estará vazia e será emitida avisos para esse efeito. Isso não tem nenhum significado porque, durante o resto do tempo, eles não estarão vazios, e nós os salvaremos / restauraremos quando necessário.

Por último, outro sed coloca tudo junto - ele anexa a saída de dc ao final de file e substitui todos os espaços por uma única guia de cada linha < em> (para o qual eu usei o \t escape aqui, mas que provavelmente deve ser um literal < tab > caractere no script real) .

OUTPUT

1       10      11
2       20      22
3       30      32
4       40      42
5       50      52
6       60      62
7       70      72
8       80      82
9       90      92
10      100     110
25      250     259
30      300     318
55      550     577
    
por 20.06.2015 / 22:37