Come faccio a generare un totale cumulativo in esecuzione dei numeri in un file di testo?

8

Ho un file di testo con 2 milioni di righe. Ogni riga ha un numero intero positivo. Sto cercando di formare una sorta di tabella delle frequenze.

File di input:

3
4
5
8

L'output dovrebbe essere:

3
7
12
20

Come faccio a fare questo?

    
posta user110327 02.02.2017 - 09:26

10 risposte

17

Con awk :

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

$0 è la riga corrente. Quindi, per ogni riga, lo aggiungo a total , imposta la riga sul nuovo total , quindi il% finale di coda% co_de è un collegamento awk: stampa la riga corrente per ogni condizione vera e 1 come condizione restituisce true.

    
risposta data muru 02.02.2017 - 09:40
9

In uno 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")

Per utilizzare

  • Copia lo script in un file vuoto, salvalo come add_last.py
  • Eseguilo con il file sorgente e il file di destinazione come argomento:

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

Spiegazione

Il codice è piuttosto leggibile, ma in dettaglio:

  • Apri il file di output per scrivere i risultati

    with open(out, "wt") as wr:
    
  • Apri il file di input per leggere per riga

    with open(f) as read:
        for l in read:
    
  • Leggi le righe, aggiungendo il valore della nuova linea al totale:

    n = n + int(l)
    
  • Scrivi il risultato nel file di output:

    wr.write(str(n)+"\n")
    
risposta data Jacob Vlijm 02.02.2017 - 09:38
9

Solo per divertimento

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

Funziona a che pende +p a ogni riga dell'input e quindi passa il risultato alla calcolatrice dc dove

   +      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.

poi

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

L'argomento -e0 inserisce 0 nello stack dc per inizializzare la somma.

    
risposta data steeldriver 03.02.2017 - 02:47
8

In Bash:

#! /bin/bash

file="YOUR_FILE.txt"

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

Per stampare somme parziali di numeri interi sullo standard input una per riga:

#!/usr/bin/env python3
import sys

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

Esempio eseguibile .

Se per qualche motivo il comando è troppo lento; potresti usare il programma 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;
}

Per crearlo ed eseguirlo, digita:

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

Esempio eseguibile .

UINTMAX_MAX è 18446744073709551615 .

Il codice C è molte volte più veloce del comando awk sulla mia macchina per il file di input generato da:

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

Probabilmente vuoi qualcosa di simile:

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

Spiegazione del comando:

  • sort -n <filename> | uniq -c ordina l'input e restituisce una tabella di frequenza
  • | awk 'BEGIN{print "Number\tFrequency"}{print $2"\t"$1}' trasforma l'output in un formato più bello

Esempio:
File di input list.txt :

4
5
3
4
4
2
3
4
5

Il 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
    
risposta data Wayne_Yux 02.02.2017 - 09:43
5

Puoi farlo in vim. Apri il file e digita le seguenti sequenze di tasti:

[email protected]"<C-a>@[email protected]:wq<cr>

Tieni presente che <C-a> è in realtà ctrl-a e <cr> è ritorno a capo , ovvero il pulsante di invio.

Ecco come funziona. Prima di tutto, vogliamo cancellare il registro 'a' in modo che non abbia effetti collaterali per la prima volta. Questo è semplicemente qaq . Quindi facciamo quanto segue:

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

Dopo aver eseguito questa macro ricorsiva, chiamiamo semplicemente :wq<cr> per salvare e chiudere.

    
risposta data DJMcMayhem 02.02.2017 - 19:03
5

Perl one-liner:

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

Con 2,5 milioni di righe di numeri, occorrono circa 6,6 secondi per l'elaborazione:

$ 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
    
risposta data Sergiy Kolodyazhnyy 02.02.2017 - 22:32
3

Un semplice Bash one-liner:

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

x è la somma cumulata di tutti i numeri dalla riga corrente e superiore.
n è il numero nella riga corrente.

Effettuiamo il looping su tutte le linee n di INPUT_FILE e aggiungiamo il loro valore numerico alla nostra variabile x e stampiamo tale somma durante ogni iterazione.

Bash è un po 'lento qui, però, puoi aspettarti che funzioni per circa 20-30 secondi per un file con 2 milioni di voci, senza stampare l'output sulla console (che è ancora più lento, indipendentemente dal metodo che usi) .

    
risposta data Byte Commander 02.02.2017 - 22:40
3

Simile alla risposta di @ steeldriver, ma con un po 'meno arcano bc invece:

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

La cosa bella di bc (e dc ) è che sono calcolatrici di precisione arbitrarie, quindi non esagereranno mai o soffriranno di mancanza di precisione sugli interi.

L'espressione sed trasforma l'input in:

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

Questo viene quindi valutato da bc . La variabile a bc è autoinizializzata su 0. Ogni riga incrementa a , quindi stampa esplicitamente.

    
risposta data Digital Trauma 03.02.2017 - 03:30

Leggi altre domande sui tag