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'
$0
ist die aktuelle Zeile. Also füge ich es für jede Zeile hinzu total
, setze die Zeile auf die neue total
und dann ist das Trailing 1
eine awk-Verknüpfung - es druckt die aktuelle Zeile für jede wahre Bedingung und wird 1
als Bedingung als wahr ausgewertet.
print
auch 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.py
Fü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 +p
an jede Zeile der Eingabe, und dann vorbei das Ergebnis an den dc
Rechner , 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 -e0
Argument wird 0
auf den dc
Stapel 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.116s
fast 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_MAX
ist 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
x
ist die kumulierte Summe aller Zahlen aus der aktuellen Zeile und darüber.
n
ist die Nummer 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 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 sed
Ausdruck transformiert die Eingabe in:
a+=3;a
a+=4;a
a+=5;a
a+=8;a
Dies wird dann von ausgewertet bc
. Die a
Variable bc wird automatisch auf 0 initialisiert. Jede Zeile wird inkrementiert a
und dann explizit gedruckt.
real 0m5.642s
auf 1,3 Millionen Zeilen. sed ist wirklich langsam dabei.