Wie generiere ich eine laufende Summe der Zahlen in einer Textdatei?

8

Ich habe eine Textdatei mit 2 Millionen Zeilen. Jede Zeile hat eine positive ganze Zahl. Ich versuche, eine Art Frequenztabelle zu bilden.

Eingabedatei:

3
4
5
8

Ausgabe sollte sein:

3
7
12
20

Wie gehe ich vor?

    
user110327 02.02.2017, 09:26

10 Antworten

17

Mit awk :

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

$0 ist die aktuelle Zeile. Also füge ich sie für jede Zeile zum total hinzu, setze die Zeile auf den neuen total und dann ist das nachfolgende 1 eine Abkürzung - es druckt die aktuelle Zeile für jede wahre Bedingung und 1 als Bedingung wird als wahr ausgewertet.

    
muru 02.02.2017, 09:40
9

In einem Python-Skript:

#!/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")

So verwenden Sie

  • Kopieren Sie das Skript in eine leere Datei und speichern Sie es als add_last.py .
  • Führen Sie es mit der Quelldatei und der Zielausgabedatei als Argumente aus:

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

Erläuterung

Der Code ist eher lesbar, aber im Detail:

  • Ausgabedatei zum Schreiben von Ergebnissen öffnen

    with open(out, "wt") as wr:
    
  • Eingabedatei zum Lesen von pro Zeile

    öffnen
    with open(f) as read:
        for l in read:
    
  • Lesen Sie die Zeilen und addieren Sie den Wert der neuen Zeile zur Summe:

    n = n + int(l)
    
  • Schreibe das Ergebnis in die Ausgabedatei:

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

Nur zum Spaß

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

Dies funktioniert, indem ein +p in jede Zeile der Eingabe pausiert und das Ergebnis dann an den Rechner dc übergeben wird, wo

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

dann

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

Das Argument -e0 drückt 0 auf den Stapel dc , um die Summe zu initialisieren.

    
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"
    
Julen Larrucea 02.02.2017 09:42
6

So drucken Sie Teilsummen von Ganzzahlen, die in der Standardeingabe pro Zeile angegeben werden:

#!/usr/bin/env python3
import sys

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

Durchführbares Beispiel .

Wenn der Befehl aus irgendeinem Grund zu langsam ist; Sie könnten das C-Programm verwenden:

#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;
}

Geben Sie zum Erstellen und Ausführen Folgendes ein:

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

Durchführbares Beispiel .

UINTMAX_MAX ist 18446744073709551615 .

Der C-Code ist um ein Vielfaches schneller als der Befehl awk auf meiner Maschine für die von:

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

Wahrscheinlich möchten Sie so etwas wie:

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

Erläuterung des Befehls:

  • sort -n <filename> | uniq -c sortiert die Eingabe und gibt eine Frequenztabelle zurück
  • | awk 'BEGIN{print "Number\tFrequency"}{print $2"\t"$1}' macht den Ausgang zu einem schöneren Format

Beispiel:
Eingabedatei list.txt :

4
5
3
4
4
2
3
4
5

Der Befehl:

$ 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
    
Wayne_Yux 02.02.2017 09:43
5

Sie können dies in vim tun. Öffnen Sie die Datei und geben Sie die folgenden Tastenanschläge ein:

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

Beachten Sie, dass <C-a> tatsächlich Strg-a ist und <cr> Wagenrücklauf ist, d. h. die Eingabetaste.

So funktioniert das. Zunächst einmal möchten wir das Register 'a' löschen, damit es beim ersten Durchlauf keine Nebenwirkungen hat. Das ist einfach qaq . Dann machen wir folgendes:

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

Nachdem dieses rekursive Makro ausgeführt wurde, rufen wir einfach :wq<cr> auf, um zu speichern und zu beenden.

    
DJMcMayhem 02.02.2017 19:03
5

Perl-Einzeiler:

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

Bei 2,5 Millionen Zahlenzeilen dauert die Verarbeitung etwa 6,6 Sekunden:

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

Ein einfacher Bash-Einzeiler:

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

x ist die kumulierte Summe aller Zahlen aus der aktuellen Zeile und darüber.
n ist die Zahl in der aktuellen Zeile.

Wir durchlaufen alle Zeilen n von INPUT_FILE und addieren ihren numerischen Wert zu unserer Variablen x und drucken diese Summe während jeder Iteration.

Bash ist hier etwas langsam. Man kann davon ausgehen, dass dies bei einer Datei mit 2 Millionen Einträgen etwa 20 bis 30 Sekunden dauert, ohne dass die Ausgabe auf die Konsole gedruckt wird (was noch langsamer ist, unabhängig von der von Ihnen verwendeten Methode). .

    
Byte Commander 02.02.2017 22:40
3

Ähnlich wie bei @ steeldriver, aber stattdessen mit dem etwas weniger arkanen bc :

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

Das Schöne an bc (und dc ) ist, dass es sich um Rechner mit beliebiger Genauigkeit handelt, so dass es niemals zu einem Überlauf oder einem Mangel an Genauigkeit über Ganzzahlen kommt.

Der Ausdruck sed transformiert die Eingabe in:

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

Dies wird dann von bc ausgewertet. Die Variable a bc wird automatisch auf 0 initialisiert. Jede Zeile erhöht a und gibt sie dann explizit aus.

    
Digital Trauma 03.02.2017 03:30

Tags und Links