Ich habe eine Textdatei mit 2 Millionen Zeilen. Jede Zeile hat eine positive ganze Zahl. Ich versuche so etwas wie eine Frequenztabelle zu bilden.
Eingabedatei:
3
4
5
8
Die Ausgabe sollte sein:
3
7
12
20
Wie mache ich das?
Ich habe eine Textdatei mit 2 Millionen Zeilen. Jede Zeile hat eine positive ganze Zahl. Ich versuche so etwas wie eine Frequenztabelle zu bilden.
Eingabedatei:
3
4
5
8
Die Ausgabe sollte sein:
3
7
12
20
Wie mache ich das?
Antworten:
Mit awk:
awk '{total += $0; $0 = total}1'
$0ist die aktuelle Zeile. Also füge ich es für jede Zeile hinzu total, setze die Zeile auf die neue totalund dann ist das Trailing 1eine awk-Verknüpfung - es druckt die aktuelle Zeile für jede wahre Bedingung und wird 1als Bedingung als wahr ausgewertet.
printauch verwendet werden?
print total}statt$0 = total}1
{print(total += $0)}
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")
add_last.pyFühren Sie es mit der Quelldatei und der Zielausgabedatei als Argumente aus:
python3 /path/to/add_last.py <input_file> <output_file>
Der Code ist ziemlich lesbar, aber im Detail:
Öffnen Sie die Ausgabedatei, um Ergebnisse zu schreiben
with open(out, "wt") as wr:
Öffnen Sie die Eingabedatei zum Lesen pro Zeile
with open(f) as read:
for l in read:
Lesen Sie die Zeilen und addieren Sie den Wert der neuen Zeile zur Gesamtsumme:
n = n + int(l)
Schreiben Sie das Ergebnis in die Ausgabedatei:
wr.write(str(n)+"\n")
Nur zum Spaß
$ sed 'a+p' file | dc -e0 -
3
7
12
20
Dies funktioniert , indem eine ppending +pan jede Zeile der Eingabe, und dann vorbei das Ergebnis an den dcRechner , 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 -e0Argument wird 0auf den dcStapel verschoben, um die Summe zu initialisieren.
real 0m4.234s
In Bash:
#! /bin/bash
file="YOUR_FILE.txt"
TOTAL=0
while IFS= read -r line
do
TOTAL=$(( TOTAL + line ))
echo $TOTAL
done <"$file"
real 0m53.116sfast eine Minute, auf 1,3 Millionen Zeilen :)
So drucken Sie Teilsummen von Ganzzahlen, die auf der Standardeingabe angegeben sind, eine pro Zeile:
#!/usr/bin/env python3
import sys
partial_sum = 0
for n in map(int, sys.stdin):
partial_sum += n
print(partial_sum)
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 Folgendes ein, um es zu erstellen und auszuführen:
$ cc cumsum.c -o cumsum
$ ./cumsum < input > output
UINTMAX_MAXist 18446744073709551615.
Der C-Code ist um ein Vielfaches schneller als der Befehl awk auf meinem Computer für die Eingabedatei, die generiert wird von:
#!/usr/bin/env python3
import numpy.random
print(*numpy.random.random_integers(100, size=2000000), sep='\n')
accumulate()itertool
Sie möchten wahrscheinlich so etwas:
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}' verwandelt den Ausgang in ein schöneres FormatBeispiel:
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
Sie können dies in vim tun. Öffnen Sie die Datei und geben Sie die folgenden Tastenanschläge ein:
qaqqayiwj@"<C-a>@aq@a:wq<cr>
Beachten Sie, dass <C-a>tatsächlich ctrl-a ist, und <cr>ist Carriage Return , dh die Enter - Taste.
So funktioniert das Zunächst möchten wir das Register 'a' löschen, damit es beim ersten Mal 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>zum Speichern und Beenden auf.
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
real 0m0.908s, ganz nett.
Ein einfacher Bash Einzeiler:
x=0 ; while read n ; do x=$((x+n)) ; echo $x ; done < INPUT_FILE
xist die kumulierte Summe aller Zahlen aus der aktuellen Zeile und darüber.
nist die Nummer in der aktuellen Zeile.
Wir durchlaufen alle Zeilen nvon INPUT_FILEund addieren ihren numerischen Wert zu unserer Variablen xund drucken diese Summe während jeder Iteration.
Bash ist hier allerdings etwas langsam. Sie können davon ausgehen, dass dies für eine Datei mit 2 Millionen Einträgen etwa 20 bis 30 Sekunden dauert, ohne dass die Ausgabe auf die Konsole gedruckt wird (was unabhängig von der verwendeten Methode sogar noch langsamer ist).
Ähnlich wie bei @ steeldrivers Antwort, jedoch mit dem etwas weniger arkanen bc:
sed 's/.*/a+=&;a/' input | bc
Das Schöne an bc(und dc) ist, dass es sich um willkürliche Präzisionsrechner handelt, die also niemals überlaufen oder bei ganzen Zahlen an Präzision leiden.
Der sedAusdruck transformiert die Eingabe in:
a+=3;a
a+=4;a
a+=5;a
a+=8;a
Dies wird dann von ausgewertet bc. Die aVariable bc wird automatisch auf 0 initialisiert. Jede Zeile wird inkrementiert aund dann explizit gedruckt.
real 0m5.642sauf 1,3 Millionen Zeilen. sed ist wirklich langsam dabei.