Hallo zusammen,
ich hab in einem txt-File eine equity-Kurve über die letzten 10 Jahre, mit insgesamt rund 500.000 Zeilen.
Hier ein Ausschnitt:
...
21.08.12 21:19:59 76287,500000
21.08.12 21:24:59 76325,000000
21.08.12 21:29:59 76375,000000
21.08.12 21:34:59 76400,000000
21.08.12 21:39:59 76525,000000
21.08.12 21:44:59 76537,500000
21.08.12 21:49:59 76537,500000
21.08.12 21:54:59 76487,500000
21.08.12 21:59:59 76587,500000
22.08.12 08:04:59 76137,500000
22.08.12 08:09:59 76212,500000
22.08.12 08:14:59 76300,000000
22.08.12 08:19:59 76300,000000
22.08.12 08:24:59 76300,000000
...
Ich möchte nun folgendes mit einem Python-Programm machen:
1. Die Datensätze in jeweils eine Liste einlesen. Die erste Liste für das Datum (ohne Uhrzeit) und die zweite Liste für den Kontostand (ganze Zahl, ohne Komma, ausreichend)
2. Die Daten sollen so reduziert werden, dass für jedes Datum nur ein Wert, nämlich der letzte, genommen wird
Könnt Ihr mir mit einem Codebeispiel weiterhelfen?
Gruss
txt mit 500k Datensätzen einlesen
- __blackjack__
- User
- Beiträge: 14042
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@winnitouch: Das ist eine relativ einfache Aufgabe die auch ein Anfänger nach dem durcharbeiten eines Grundlagentutorials lösen können sollte. In der Pythonn-Dokumentation befindet sich beispielsweise ein solches Tutorial. Wo genau liegt denn Dein Problem bei der Umsetzung?
Warum sollen diese parallelen Daten in getrennten Listen gespeichert werden?
Warum sollen diese parallelen Daten in getrennten Listen gespeichert werden?
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
-
- User
- Beiträge: 21
- Registriert: Montag 25. März 2019, 22:33
Hallo blackjack,
ja, ich hab schon auch Tutorials gesehn und ausprobiert. Mein Problem war immer die Durchlaufzeit. Das Programm hängt und generiert kein Ouput minutenlang bis ich es "kill". Ich nehme an, dies liegt an der Menge der Daten.
2 separate Listen, weil ich im Anschluss gerne eine Grafik plotten will und gesehen hab, dass man für X- und Y- Achse am besten jeweils eine Liste übermittelt.
Gruss
ja, ich hab schon auch Tutorials gesehn und ausprobiert. Mein Problem war immer die Durchlaufzeit. Das Programm hängt und generiert kein Ouput minutenlang bis ich es "kill". Ich nehme an, dies liegt an der Menge der Daten.
2 separate Listen, weil ich im Anschluss gerne eine Grafik plotten will und gesehen hab, dass man für X- und Y- Achse am besten jeweils eine Liste übermittelt.
Gruss
Das ist ein Irrglaube. Folgendes Programm braucht auf meinem 6 Jahre alten Laptop 1.2 Sekunden um 1000000 Zeilen deiner Art einzulesen:
Es liegt also an deiner Programmierung, die wir immer noch nicht zu sehen bekommen haben.
Code: Alles auswählen
import csv
import tempfile
import time
fname = tempfile.mktemp()
with open(fname, "w") as outf:
for _ in range(1000_000):
outf.write("21.08.12 21:19:59 76287,500000\n")
start = time.time()
with open(fname) as inf:
reader = csv.reader(inf)
lines = list(reader)
print(len(lines))
print("elapsed:", time.time() - start)
-
- User
- Beiträge: 219
- Registriert: Donnerstag 21. Juli 2011, 07:01
- Wohnort: Stade / Hamburg
- Kontaktdaten:
dann arbeite doch mal zur Probe mit einem Ausschnitt der Gesamtdatenmenge. Etwa 5 Tage z.B.
Vielleicht kommst du dann zu einem Ergebnis.
Und wenn du den Code hier postest, gibt es sicher weitere Tipps. Und die Code-Tags bitte nicht vergessen.
Vielleicht kommst du dann zu einem Ergebnis.
Und wenn du den Code hier postest, gibt es sicher weitere Tipps. Und die Code-Tags bitte nicht vergessen.
- __blackjack__
- User
- Beiträge: 14042
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
Ich habe mal die vollen 500k Datensätze erstellt:
Wenn die Datensätze alle wie im Beispiel 5 Minuten auseinander liegen, komme ich übrigens nur ca. 5 Jahre in die Vergangenheit. 
Einlesen, inklusive Datum und Zahl parsen, und filtern nach letztem Datensatz pro Tag dauert hier ca. 3,3 Sekunden mit Python auf »Intel(R) Core(TM) i3-4170 CPU @ 3.70GHz«. Ist jetzt keine 6 Jahre alt wie __deets__'s Laptop, aber Oberliga ist das jetzt auch nicht gerade. Für das filtern habe ich ein `collections.OrderedDict` verwendet.
Python ist zwar ”langsam”, aber für die paar Daten schnell genug. Spasseshalber das Filtern in FreeBASIC:
Das braucht nur 0,5 Sekunden, ist aber hässlicher/umständlicher im Code und Plotten geht in Python auch einfacher/besser.
Code: Alles auswählen
#!/usr/bin/env python3
from datetime import datetime as DateTime, timedelta as TimeDelta
from random import randint
def main():
count = 500_000
delta = TimeDelta(minutes=5)
timestamp = DateTime.now() - delta * count
for _ in range(count):
print(f'{timestamp:%d.%m.%Y %H:%M:%S} {randint(70000, 79999)},000000')
timestamp += delta
if __name__ == '__main__':
main()

Einlesen, inklusive Datum und Zahl parsen, und filtern nach letztem Datensatz pro Tag dauert hier ca. 3,3 Sekunden mit Python auf »Intel(R) Core(TM) i3-4170 CPU @ 3.70GHz«. Ist jetzt keine 6 Jahre alt wie __deets__'s Laptop, aber Oberliga ist das jetzt auch nicht gerade. Für das filtern habe ich ein `collections.OrderedDict` verwendet.
Python ist zwar ”langsam”, aber für die paar Daten schnell genug. Spasseshalber das Filtern in FreeBASIC:
Code: Alles auswählen
Dim s As String, i As Integer, j As Integer, tmp As String
Dim pd As String, d As String, v As Integer
On Error Goto ErrorHandler
Open "test.txt" For Input As #1
Do Until Eof(1)
Line Input #1, s
i = InStr(s, " ")
If i = 0 Then Error 1000
d = Left(s, i)
i = InStrRev(s, " ")
If i = 0 Then Error 1001
j = InStrRev(s, ",")
If j = 0 And j <= i Then Error 1002
tmp = Mid(s, i + 1, j - i - 1)
For i = 1 To Len(tmp)
If InStr(Mid(tmp, i, 1), Any "0123456789") = 0 Then Error 1003
Next
v = ValInt(tmp)
If d <> pd Then
pd = d
Print d, v
End If
Loop
If d = pd And pd <> "" Then Print d, v
Close #1
End
ErrorHandler:
On Error Goto 0
Select Case Err
Case 2
Print "Datei konnte nicht gefunden werden!"
Case 3
Print "I/O Fehler!"
Case 1000
Print "Leerzeichen nach Datum nicht gefunden!"
Case 1001
Print "Leerzeichen vor Wert nicht gefunden!"
Case 1002
Print "Komma in Wert nicht gefunden!"
Case 1003
Print "Wert ist keine ganze Zahl!"
Case Else
Error Err
End Select
System 1
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
-
- User
- Beiträge: 21
- Registriert: Montag 25. März 2019, 22:33
Hallo,
also ich mach es mittlerweile so:
Vor allem die for-Schleife in Step 3 ist zeitintensiv, würde das evtl eleganter, schneller gehn?
also ich mach es mittlerweile so:
Code: Alles auswählen
#Werte aus txt in Liste schreiben
print ("Step 1: Rohdaten übernehmen...")
list_raw = open("datenfile.txt").readlines()
print ("...done")
#Werte in Liste umdrehen, damit ältester Eintrag zuerst
print ("Step 2: Werte in Liste drehen...")
list_raw = list_raw[::-1]
print ("...done")
#Finale Listen anlegen für Datum und Kontostand
list_date = []
list_cash = []
#Prüfe ob die ersten 8 Zeichen (Datum ohne Uhrzeit) schon in Finalliste enthalten
#Wenn nicht, hinzufügen. So werden Doppeleinträge entfernt.
print ("Step 3: Datum und Kontostand extrahieren und in eigene Listen schreiben...")
for i in list_raw:
if i[:8] not in list_date:
list_date.append(i[:8])
list_cash.append(i[18:])
list_cash = list_cash[::-1] #Älteste Element zuerst, jüngstes Element zum Schluss
list_date = list_date[::-1] #Älteste Element zuerst, jüngstes Element zum Schluss
print ("...done")
#Zeilenumbrüche am Ende entfernen
print ("Step 4: Zeilenumbrüche entfernen und Komma durch Punkt ersetzten...")
for i, line in enumerate(list_cash):
list_cash[i] = line.replace('\n', '')
#Komma durch Punkt ersetzen
for i, line in enumerate(list_cash):
list_cash[i] = line.replace(',', '.')
list_cash[i] = int(float(list_cash[i]))
print ("...done")
Wie hier gezeigt nimmt man für eine solche Aufgabe das csv Modul. Damit spart man sich die fragilen Indizes. Und das ganze String-gerödel. Und für einen Test auf schon enthalten sollte man eine geeignete Datenstruktur verwenden, wie zb eine Menge.
Es ist schlecht, dass Du Daten in zwei Listen parallel hältst. Dir scheint die Reihenfolge der Einträge wichtig zu sein, aber list_date benutzt Du gar nicht weiter?
Statt Listen zu ändern, erzeugt man in Python eine neue Liste, dann ist es auch unnötig, mit enumerate sich einen Index mitliefern zu lassen. die Umwandlungen würde man auch gleich in einem Schritt machen.
Statt Listen zu ändern, erzeugt man in Python eine neue Liste, dann ist es auch unnötig, mit enumerate sich einen Index mitliefern zu lassen. die Umwandlungen würde man auch gleich in einem Schritt machen.
Code: Alles auswählen
#Werte aus txt in Liste schreiben
print ("Step 1: Rohdaten übernehmen...")
with open("datenfile.txt") as lines:
lines = list(lines)
print ("...done")
#Finale Listen anlegen für Datum und Kontostand
dates = set()
cashes = []
for line in reversed(lines):
date, time, value = line.split()
if date not in dates:
dates.add(date)
caches.append(int(float(value.replace(',', '.'))))
caches = caches[::-1]
print ("...done")
-
- User
- Beiträge: 21
- Registriert: Montag 25. März 2019, 22:33
ok danke. Die zwei Listen pfleg ich parallel, da ich wie gesagt etwas später im Code noch eine Kennlinie plotte....und da geb ich eben X-Achse und Y-Achse jeweils als Liste rein. Deswegen ist auch die Reihenfolge in der Liste relevant (ältester Eintrag zuerst), sonst sieht die Kennlinie falsch aus.
Mein Code macht noch einiges mehr, was ich aktuell nicht gepostet habe, das mach ich mal, wenn ich fertig bin. Ich hab das Gefühl, da kann noch einiges optimiert werden
Bin halt kein Vollblut-Programmierer. Ich überleg mir, was für eine Teilaufgabe ich lösen möchte, google danach und adaptier brauchbare Beispiele. Meist sinds halt die einfachen Beispiele, die man natürlich auch viel eleganter lösen könnte, aber ich muss ja auch was übernehmen, was ich verstehe 
Mein Code macht noch einiges mehr, was ich aktuell nicht gepostet habe, das mach ich mal, wenn ich fertig bin. Ich hab das Gefühl, da kann noch einiges optimiert werden


- __blackjack__
- User
- Beiträge: 14042
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
Wie gesagt ich habe zum ausfiltern ein `collections.OrderedDict` genommen. Damit wird der Code IMHO deutlich einfacher. Man muss sich die gesehenen Datumsangaben nicht einzeln merken, nicht alle Zeilen erst komplett in den Speicher lesen um dan rückwärts darüber iterieren zu können und am Ende das Ergebnis deswegen noch einmal umdrehen. Man steckt einfach *alle* Datum/Wert-Paare in dieses Wörterbuch und dadurch bleibt ja automatisch das letzte erhalten. Und die Datumsangaben und die Werte kann man nach dem einlesen auch ganz einfach als getrennte Listen von dem Wörterbuch erhalten. Oder auch gemeinsam darüber iterieren wenn man weitere Verarbeitungsschritte hat, welche das jeweilige Paar benötigen.
Das CSV-Modul würde ich wahrscheinlich nicht verwenden, aber auch nicht mit festen Indexwerten arbeiten, sondern an Leerzeichen aufteilen wie Sirius3 das gemacht hat. Insbesondere das CSV-Modul und das Komma als Trenner würde ich hier als etwas undurchsichtigen Hack sehen, denn das sind ja keine durch Komma getrennte Spalten, sondern man zerteilt damit den Wert der letzten Spalte wo das Komma nicht als Trennzeichen sondern als Dezimalkomma verwendet wird. *Wenn* man dafür das CSV-Modul verwendet, sollte man das deutlich in einen Kommentar schreiben, damit Leser keinen falschen Eindruck von den Daten bekommen.
Das CSV-Modul würde ich wahrscheinlich nicht verwenden, aber auch nicht mit festen Indexwerten arbeiten, sondern an Leerzeichen aufteilen wie Sirius3 das gemacht hat. Insbesondere das CSV-Modul und das Komma als Trenner würde ich hier als etwas undurchsichtigen Hack sehen, denn das sind ja keine durch Komma getrennte Spalten, sondern man zerteilt damit den Wert der letzten Spalte wo das Komma nicht als Trennzeichen sondern als Dezimalkomma verwendet wird. *Wenn* man dafür das CSV-Modul verwendet, sollte man das deutlich in einen Kommentar schreiben, damit Leser keinen falschen Eindruck von den Daten bekommen.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
- __blackjack__
- User
- Beiträge: 14042
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
Ein kleines C-Programm (mit glib als Abhängigkeit) das Daten erzeugt, die mehr nach einem Kursverlauf aussehen:
Und das FreeBASIC-Programm um einen simplen Plot erweitert:
Beispielausgabe:

Code: Alles auswählen
#include <glib.h>
#include <glib/gprintf.h>
/** Number of records to generate. */
#define COUNT 500000
/** Interval between records. */
#define INTERVAL (G_TIME_SPAN_MINUTE * 5)
static void in_place_add(GDateTime **dt, GTimeSpan delta)
{
GDateTime *tmp = g_date_time_add(*dt, delta);
g_date_time_unref(*dt);
*dt = tmp;
}
int main(int argc, char const *argv[])
{
gint value = 70000;
GDateTime *timestamp = g_date_time_new_now_local();
in_place_add(×tamp, -(INTERVAL * COUNT));
for (int i = 0; i < COUNT; ++i) {
gchar *tmp = g_date_time_format(timestamp, "%d.%m.%Y %H:%M:%S");
g_printf("%s %d,000000\n", tmp, value);
g_free(tmp);
in_place_add(×tamp, INTERVAL);
value += g_random_int_range(-10, 11);
}
g_date_time_unref(timestamp);
return 0;
}
Code: Alles auswählen
Const MaxCount = 4096
Type DataSet
Count As Integer
Dates(MaxCount) As String
Values(MaxCount) As Integer
End Type
Sub DataSetAppend (ds As DataSet, d As String, v As Integer)
ds.Dates(ds.Count) = d
ds.Values(ds.Count) = v
ds.Count = ds.Count + 1
End Sub
Sub LoadData (ds As DataSet, filename As String)
Dim s As String, i As Integer, j As Integer, tmp As String
Dim prevD As String, d As String, v As Integer
ds.Count = 0
Open filename For Input As #1
Do Until Eof(1)
Line Input #1, s
i = InStr(s, " ")
If i = 0 Then Error 1000
d = Left(s, i - 1)
i = InStrRev(s, " ")
If i = 0 Then Error 1001
j = InStrRev(s, ",")
If j = 0 And j <= i Then Error 1002
tmp = Mid(s, i + 1, j - i - 1)
For i = 1 To Len(tmp)
If InStr(Mid(tmp, i, 1), Any "0123456789") = 0 Then Error 1003
Next
v = ValInt(tmp)
If d <> prevD Then
prevD = d
DataSetAppend ds, d, v
End If
Loop
If d = prevD And prevD <> "" Then DataSetAppend ds, d, v
Close #1
End Sub
Sub DataSetGetMinMax (ds As DataSet, ByRef min As Integer, ByRef max As Integer)
Dim i As Integer, v As Integer
min = &H7fffffffffffffff
max = &H8000000000000000
For i = 0 To ds.Count - 1
v = ds.Values(i)
If min > v Then min = v
If max < v Then max = v
Next
End Sub
Sub PlotData (ds As DataSet)
Dim i As Integer, min As Integer, max As Integer
Dim prevY As String, y As String
DataSetGetMinMax ds, min, max
Screen 20
Window (0, max)-(ds.Count, min)
For i = 0 To ds.Count - 2
Line (i, ds.Values(i))-(i + 1, ds.Values(i + 1))
y = Right(ds.Dates(i), 4)
If y <> prevY Then
prevY = y
Draw String (i, max), y, 6
End If
Next
GetKey
Screen 0
End Sub
On Error Goto ErrorHandler
Dim ds As DataSet
LoadData ds, "test.txt"
PlotData ds
End
ErrorHandler:
On Error Goto 0
Select Case Err
Case 2
Print "Datei konnte nicht gefunden werden!"
Case 3
Print "I/O Fehler!"
Case 1000
Print "Leerzeichen nach Datum nicht gefunden!"
Case 1001
Print "Leerzeichen vor Wert nicht gefunden!"
Case 1002
Print "Komma in Wert nicht gefunden!"
Case 1003
Print "Wert ist keine ganze Zahl!"
Case Else
Error Err
End Select
System 1

„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.