Como eu gero um total cumulativo de execução dos números em um arquivo de texto?

8

Eu tenho um arquivo de texto com 2 milhões de linhas. Cada linha tem um inteiro positivo. Eu estou tentando formar uma espécie de tabela de freqüência.

Arquivo de entrada:

3
4
5
8

O resultado deve ser:

3
7
12
20

Como faço para fazer isso?

    
por user110327 02.02.2017 / 09:26

10 respostas

17

com awk :

awk '{total += $0; $0 = total}1'

$0 é a linha atual. Então, para cada linha, eu adiciono ao total , defino a linha para o novo total , e então o trailing 1 é um atalho do awk - ele imprime a linha atual para cada condição verdadeira, e 1 como condição avaliada como verdadeira.

    
por muru 02.02.2017 / 09:40
9

Em um script python:

#!/usr/bin/env python3
import sys

f = sys.argv[1]; out = sys.argv[2]

n = 0

with open(out, "wt") as wr:
    with open(f) as read:
        for l in read:
            n = n + int(l); wr.write(str(n)+"\n")

Para usar

  • Copie o script em um arquivo vazio, salve-o como add_last.py
  • Execute-o com o arquivo de origem e o arquivo de saída de destino como argumentos:

    python3 /path/to/add_last.py <input_file> <output_file>
    

Explicação

O código é bastante legível, mas em detalhes:

  • Abra o arquivo de saída para gravar os resultados

    with open(out, "wt") as wr:
    
  • Abra o arquivo de entrada para leitura por linha

    with open(f) as read:
        for l in read:
    
  • Leia as linhas, adicionando o valor da nova linha ao total:

    n = n + int(l)
    
  • Escreva o resultado no arquivo de saída:

    wr.write(str(n)+"\n")
    
por Jacob Vlijm 02.02.2017 / 09:38
9

Apenas por diversão

$ sed 'a+p' file | dc -e0 -
3
7
12
20

Isso funciona por a ppending +p para cada linha da entrada e, em seguida, passando o resultado para a calculadora dc , onde

   +      Pops two values off the stack, adds them, and pushes the result.
          The precision of the result is determined only by the values  of
          the arguments, and is enough to be exact.

então

   p      Prints  the  value on the top of the stack, without altering the
          stack.  A newline is printed after the value.

O argumento -e0 envia 0 para a pilha dc para inicializar a soma.

    
por steeldriver 03.02.2017 / 02:47
8

No Bash:

#! /bin/bash

file="YOUR_FILE.txt"

TOTAL=0
while IFS= read -r line
do
    TOTAL=$(( TOTAL + line ))
    echo $TOTAL
done <"$file"
    
por Julen Larrucea 02.02.2017 / 09:42
6

Para imprimir somas parciais de inteiros dadas na entrada padrão, uma por linha:

#!/usr/bin/env python3
import sys

partial_sum = 0
for n in map(int, sys.stdin):
    partial_sum += n
    print(partial_sum)

Exemplo executável .

Se por algum motivo o comando for muito lento; você poderia usar o programa C:

#include <stdint.h>
#include <ctype.h>
#include <stdio.h>

int main(void)
{
  uintmax_t cumsum = 0, n = 0;
  for (int c = EOF; (c = getchar()) != EOF; ) {
    if (isdigit(c))
      n = n * 10 + (c - '0');
    else if (n) { // complete number
      cumsum += n;
      printf("%ju\n", cumsum);
      n = 0;
    }
  }
  if (n)
    printf("%ju\n", cumsum + n);
  return feof(stdin) ? 0 : 1;
}

Para criar e executar, digite:

$ cc cumsum.c -o cumsum
$ ./cumsum < input > output

Exemplo executável .

UINTMAX_MAX é 18446744073709551615 .

O código C é várias vezes mais rápido que o comando awk na minha máquina para o arquivo de entrada gerado por:

#!/usr/bin/env python3
import numpy.random
print(*numpy.random.random_integers(100, size=2000000), sep='\n')
    
por jfs 02.02.2017 / 21:32
5

Você provavelmente quer algo assim:

sort -n <filename> | uniq -c | awk 'BEGIN{print "Number\tFrequency"}{print $2"\t"$1}'

Explicação do comando:

  • sort -n <filename> | uniq -c classifica a entrada e retorna uma tabela de frequência
  • | awk 'BEGIN{print "Number\tFrequency"}{print $2"\t"$1}' transforma a saída em um formato mais agradável

Exemplo:
Arquivo de entrada list.txt :

4
5
3
4
4
2
3
4
5

O comando:

$ sort -n list.txt | uniq -c | awk 'BEGIN{print "Number\tFrequency"}{print $2"\t"$1}'
Number  Frequency
2   1
3   2
4   4
5   2
    
por Wayne_Yux 02.02.2017 / 09:43
5

Você pode fazer isso no vim. Abra o arquivo e digite as seguintes teclas:

qaqqayiwj@"<C-a>@aq@a:wq<cr>

Observe que <C-a> é, na verdade, ctrl-a e <cr> é retorno de carro , ou seja, o botão "Enter".

Veja como isso funciona. Primeiramente, queremos limpar o registro 'a' para que não tenha efeitos colaterais na primeira vez. Isso é simplesmente qaq . Então fazemos o seguinte:

qa                  " Start recording keystrokes into register 'a'
  yiw               " Yank this current number
     j              " Move down one line. This will break the loop on the last line
      @"            " Run the number we yanked as if it was typed, and then
        <C-a>       " increment the number under the cursor *n* times
             @a     " Call macro 'a'. While recording this will do nothing
               q    " Stop recording
                @a  " Call macro 'a', which will call itself creating a loop

Depois que essa macro recursiva é executada, simplesmente chamamos :wq<cr> para salvar e sair.

    
por DJMcMayhem 02.02.2017 / 19:03
5

One-liner Perl:

$ perl -lne 'print $sum+=$_' input.txt                                                                
3
7
12
20

Com 2,5 milhões de linhas de números, demora cerca de 6,6 segundos a processar:

$ time perl -lne 'print $sum+=$_' large_input.txt > output.txt                                        
    0m06.64s real     0m05.42s user     0m00.09s system

$ wc -l large_input.txt
2500000 large_input.txt
    
por Sergiy Kolodyazhnyy 02.02.2017 / 22:32
3

Um simples one-liner de Bash:

x=0 ; while read n ; do x=$((x+n)) ; echo $x ; done < INPUT_FILE

x é a soma acumulada de todos os números da linha atual e acima.
n é o número na linha atual.

Fazemos o loop de todas as linhas n de INPUT_FILE e adicionamos seu valor numérico à nossa variável x e imprimimos essa soma durante cada iteração.

Bash é um pouco lento aqui, você pode esperar que isso corra em torno de 20-30 segundos para um arquivo com 2 milhões de entradas, sem imprimir a saída para o console (que é ainda mais lento, independentemente do método que você usa) .

    
por Byte Commander 02.02.2017 / 22:40
3

Semelhante à resposta do @ steeldriver, mas com o% de bc um pouco menos arcanoso:

sed 's/.*/a+=&;a/' input | bc

O interessante sobre bc (e dc ) é que eles são calculadoras de precisão arbitrária, portanto, nunca vão transbordar ou sofrem falta de precisão sobre inteiros.

A expressão sed transforma a entrada em:

a+=3;a
a+=4;a
a+=5;a
a+=8;a

Isso é avaliado por bc . A variável a bc é inicializada automaticamente como 0. Cada linha incrementa a e depois a imprime explicitamente.

    
por Digital Trauma 03.02.2017 / 03:30