Reduzindo a complexidade dos dados no arquivo de texto

2

Eu tenho este arquivo:

  1  2
  2  7
  3  4
  4  7
  5  3
  6  7
  7  1
  8  2
  9  4

E minha saída desejada é

 1 13
 2 17
 3 7

Na minha entrada eu tenho 9 linhas e quero reduzi-lo a três linhas, preservando o valor total da segunda coluna. Por exemplo 1 na 1 ª coluna representa 1,2,3 e 13 na 2 ª coluna, 1 ª linha representam adição (2 + 4 + 7) e assim por diante .. Alguma idéia? pode ser usando awk / perl ou qualquer outra ferramenta linux.

    
por Dani 04.07.2016 / 20:07

5 respostas

5

Aqui está uma solução awk :

awk '{ s+=$2; if (!(NR%3)) { k++; print k,s; s=0 } };
     END { if (NR%3) { k++; print k, s } }' file.txt

Ele ignora a primeira coluna, preferindo gerar em k como um número de linha de saída. A segunda coluna é somada em s e a cada três linhas ( (NR % 3) == 0 ) é emitida e o acumulador é reconfigurado. Finalmente, se tivermos quaisquer linhas que sobram, apresentamos a soma restante.

Saída do arquivo de exemplo

1 13
2 17
3 7

Apenas para completar, aqui está uma versão DRY que usa uma função para manipular o código repetido do módulo -3 e blocos END:

awk 'function outsum() { print ++k,s; s=0 };
     { s+=$2; if (!(NR%3)) { outsum() } };
     END { if (NR%3) { outsum() } }' file.txt
    
por 04.07.2016 / 22:14
3

Solução de Perl:

perl -lane '
    $s += $F[1];
    print(join "\t", ++$l, $s), $s = 0
        if 0 == $. % 3 || eof;
' input-file
  • -n lê a linha de entrada por linha
  • -a divide cada linha no espaço em branco no array @F
  • $s é usado como variável mantendo a soma
  • $. é uma variável especial que contém o número da linha de entrada
  • $l é o número da linha de saída
por 04.07.2016 / 21:10
1

Isso talvez vá para codegolf.SE . Aqui está um folheto sem perl , awk ou sed :

paste <(for i in $(seq 1 0.33333333334 $(A=$(wc -l input.dat | cut -d ' ' -f 1); echo $A/3+1 | bc)); do echo $i/1 | bc; done) <(tr -s ' ' < input.dat | cut -d ' ' -f 3) | datamash -g 1 sum 2

Em detalhes

O lado esquerdo

for i in $(seq 1 0.33333333334 $(A=$(wc -l input.dat | cut -d ' ' -f 1); echo $A/3+1 | bc)); do echo $i/1 | bc; done

Produz uma lista como (é responsável pelo número real de linhas no arquivo de entrada):

1
1
1
2
2
2
3
3
3

E o lado direito

tr -s ' ' < input.dat | cut -d ' ' -f 3

Chops primeira coluna do arquivo de entrada deixando:

2
7
4
7
3
7
1
2
4

paste os combina de volta e datamash faz o grupo por .

    
por 04.07.2016 / 21:20
1

Ainda outro oneliner com sed e dc :

sed 's/ *[^ ]*//' < input.dat | tr "\n" " " | sed 's/\([^ ]\+\) *\([^ ]\+\) *\([^ ]\+\)/  ++p/g' | dc | cat -n

Explicação:

sed 's/ *[^ ]*//' < input.dat

mata a primeira coluna; um pouco mais robusto que cut contra espaços repetidos

tr "\n" " "

Transforma todas as novas linhas em espaços, colocando tudo em uma linha

sed 's/\([^ ]\+\) *\([^ ]\+\) *\([^ ]\+\)/
dc
++p/g'

Substitui três tokens delimitados por espaços por si mesmos seguidos por ++p .

cat -n

Alimenta a saída em dc , a calculadora RPN; cada número é colocado na pilha, e a cada três você tem os comandos + , + e p ( + significa somar os dois números no topo da pilha, p imprime a pilha). Isso nos dá a segunda coluna de saída.

sed 's/ *[^ ]*//' < input.dat | tr "\n" " " | sed 's/\([^ ]\+\) *\([^ ]\+\) *\([^ ]\+\)/  ++p/g' | dc | cat -n

Reescreve tudo adicionando os números de linha.

    
por 04.07.2016 / 23:46
0

Aqui está uma versão que usa apenas comandos shell. Dividei-o em várias linhas, mas não há motivos para que você não pudesse juntar tudo como um verso (foi assim que começou):

(
    s=0 k=1 n=0
    while read x v
    do
        s=$((s+v)) n=$((n+1))
        if [[ n -eq 3 ]]
        then
            echo $k $s
            k=$((k+1)) n=0 s=0
        fi
    done
    [[ s -gt 0 ]] && echo $k $s
) <file.txt
( s=0 k=1 n=0; while read x v; do s=$((s+v)) n=$((n+1)); if [[ n -eq 3 ]]; then echo $k $s; k=$((k+1)) n=0 s=0; fi; done; [[ s -gt 0 ]] && echo $k $s ) <file.txt
    
por 04.07.2016 / 23:01