Mittelwerte über die Tage in einer Liste berechnen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Allmentux
User
Beiträge: 6
Registriert: Sonntag 14. Januar 2018, 13:36

Hallo!

Nachdem ich schon einige Tipps hier im Forum aufgeschnappt habe, habe ich diesmal eine größere Frage wozu ich mich entschlossen habe mich hier anzumelden. Folgendes habe ich vor:

Ich möchte eine Liste aus einer csv-Datei einlesen. Diese hat je drei Einträge (Datum im Unix-Zeitformat, Preis, Handelsvolumen) und dann einen Zeilenumbruch. Bei der Liste handelt es sich um historische Kurse von Kryptowährungen wie Bitcoin. Die Liste besteht selber aus etwas mehr als 10 Mio Zeilen.

Ich möchte jetzt immer nur das 0. und 1. Element (Datum und Preis) einer Reihe einlesen und in einer neuen Liste abspeichern.
Danach soll das Programm über n Einträge eines Tages (n ist immer unterschiedlich) den Mittelwert bilden.
Ein Tag dabei ist die Differenz zwischen dem 0. Datums Eintrag bis 86400 Sekunden (also ein Tag) vergangen sind.

Ergebniss soll sein das ich von Tag 1 den Mittelwert, dann von Tag 2 den Mittelwert usw. erhalte..idealerweise einschränkbar nach Jahren.

Anbei zeige ich euch den Code den ich jetzt schon habe. So ganz funktionieren tut das noch nicht...
..bin ich mit dem Code auf dem richtigen Weg oder sollte ich das vll ganz anders auffahren?

Code: Alles auswählen

import csv
import datetime
import time
#from decimal import *

ausgabealles = []
datumliste = []
preisliste = []
preisaddition = []
mittelwertliste = []
summe = 0
anzahltage = 0
eintraegeprotag = 0
startzeit = time.time()

with open('bitfinexUSD.csv','r') as eingabe:
    rohdaten = csv.reader(eingabe)

    for row in rohdaten:
        datum = int(row[0])
        preis = row[1]
        volumen = row[2]

        datumliste.append(datum)
        preisliste.append(preis)

        datum2 = datetime.datetime.fromtimestamp(int(datum)).strftime(
            '%Y-%m-%d %H:%M:%S')  # umwandlung in menschliches format
        ausgabe = datum2 + ',' + preis[:10] + '\n'
        print(ausgabe)
        ausgabealles.append(ausgabe)

    endzeit = time.time()
    dauer = endzeit - startzeit
    print("Die Verarbeitung hat", dauer[:10] / 60, "Minuten gedauert")

with open('ausgabedatei.csv', 'w+', newline='\n') as ausgabedatei:
    writer = csv.writer(ausgabedatei)
    writer.writerow(ausgabealles)

    for elem in datumliste:

        if datum - datumliste[0] <= 86400:
            eintraegeprotag = len(datumliste)
            print("gehst du hier rein?? ja tu ich!!")

        else:
            print("Ein Tag ist vergangen")
            eintraegeprotag = 0
            preisliste = 0
            datumsliste = 0
            #continue

        #Berechnung des Mittelwertes
        for elem in preisliste[0:]:
            summe += float(elem)

        mittelwert = float(summe) / eintraegeprotag
        print(eintraegeprotag, " Elemente /", "Mittelwert: ", mittelwert)
        mittelwertliste.append(mittelwert)
        anzahltage += 1
        summe = 0

with open('mittelwerte.csv', 'w+', newline='\n') as mittelwertedatei:
    writer = csv.writer(mittelwertedatei)
    writer.writerow(mittelwertliste, anzahltage)

# Alle Zeiten UTC+0 !!!
# 1388448000 31.12.2013
# 1372636800 1.7.2013
# 1470009600 1.8.2016
# 1472601600 31.8.2016
# 1483228800 1.1.2017
# 1512000000 30.11.2017
narpfel
User
Beiträge: 643
Registriert: Freitag 20. Oktober 2017, 16:10

Moin,

ohne mir jetzt deinen Code genauer angesehen zu haben, aber das, was du beschreibst, hört sich so an, als wäre `pandas` quasi schon eine fertige Lösung. Stichwort wäre `DataFrame.groupby`.
Sirius3
User
Beiträge: 17703
Registriert: Sonntag 21. Oktober 2012, 17:20

@Allmentux: wie narpfel schon geschrieben hat, böte sich pandas ideal dafür an. Ganz allgemein will man nicht suchen müssen, wo Variablen initialisiert oder benutzt werden. Daher sollte man Variablen erst initalisieren, wenn man sie auch braucht. Dein Code sieht im Moment so aus: 1. initialisiere Variable. 2. tu irgendetwas ganz anderes 3. benutze Variable. 4. Setze Variable zurück. Ein erster Schritt, wäre, Funktionen zu schreiben, die genau eine Aufgabe haben; lesen der Daten; Gruppieren der Daten nach Datum; bilden des Mittelwertes; schreiben des Ergebnisses. Später, wenn Du das so in Funktionen aufgeteilt hast, kann man aus den Funktionen Generatoren machen, itertools.groupby benutzen, etc.
Allmentux
User
Beiträge: 6
Registriert: Sonntag 14. Januar 2018, 13:36

Danke für die schnelle Rückmeldung!

Ich werde mich mal in pandas einarbeiten und meinen Code besser strukturieren :)
Aber dazu werde ich erst am WE kommen.
Allmentux
User
Beiträge: 6
Registriert: Sonntag 14. Januar 2018, 13:36

Hier ist schonmal ein Kleiner Minimal-Code, Mit panda geht es deutlich fixer!

Allerdings wurmt mich gerade ein Typenfehler :roll:

Code: Alles auswählen

import pandas as pd
import time

startzeit = time.time()

df = pd.read_csv('bitfinexUSD.csv', nrows = 20, header = None, names = ['Datum','Kurs','Volumen'])
print(df[['Datum','Kurs']])
df.to_csv('ausgabedatei.csv')

endzeit = time.time()
dauer = endzeit - startzeit
zeit = dauer / 3600
str(zeit)
print("Die Verarbeitung hat", zeit[:4], "Sekunden gedauert")
Hier ist die Variable "zeit" erst ein float und soll ein ein str umgewandelt werden, damit ich diese für die Ausgabe Kürzen kann.
Leider ignoriert Python die Typumwandlung schlicht.

Code: Alles auswählen

/usr/bin/python3.6 /.../panda.py
         Datum       Kurs
0   1364767668   93.25000
1   1364767669   93.30000
2   1364767693  100.00000
3   1364767694  100.00000
4   1364767696  100.00000
5   1364767699  100.00000
6   1364767701  100.00000
7   1364767705  100.00000
8   1364767709  100.00000
9   1364767711  100.00000
10  1364767767   93.30000
11  1364767879   93.35000
12  1364767880   93.37227
13  1364767880   93.38158
14  1364767881   93.47000
15  1364767937   93.47000
16  1364767992   93.03001
17  1364767993   93.03000
18  1364768801   93.04399
19  1364768810   93.04399
Traceback (most recent call last):
  File "/.../panda.py", line 16, in <module>
    print("Die Verarbeitung hat", zeit[:4], "Sekunden gedauert")
TypeError: 'float' object is not subscriptable

Process finished with exit code 1
Laut dem pyCharm Debugger ist die Variable zeit noch in der print-Codezeile ein float.
Obwohl doch eine Zeile vorher eine Str-Umwandlung war? Weiß jemand Rat?
eckhard
User
Beiträge: 33
Registriert: Montag 14. Dezember 2015, 10:06
Wohnort: Karlsruhe

@Allmentux: str(zeit) ändeert nicht den float Wert, auf den zeit zeigt, sondern gibt diesen zurück.
zeit = str(zeit) wird hier helfen. Allerdings gibt es audch die Möglichkeit, mit % oder format eine
Ausgabeformatierung zu machen.
Eckhard
Sirius3
User
Beiträge: 17703
Registriert: Sonntag 21. Oktober 2012, 17:20

@Allmentux: Du wandelst die Zahl zwar in einen String um, wirfst aber das Ergebnis sofort wieder weg. Um Zahlen für die Ausgabe zu formatieren, benutzt man format, `zeit` ist in Stunden, so dass Dein Text auch falsch ist:

Code: Alles auswählen

dauer = endzeit - startzeit
print("Die Verarbeitung hat {:.3f} Stunden gedauert".format(dauer / 3600))
Allmentux
User
Beiträge: 6
Registriert: Sonntag 14. Januar 2018, 13:36

@ Eckhard und Sirius

So habe ich das jetzt gemacht und es funktioniert.
Aber: Nur wenn ich eine feste Zeilenzahl vorgebe. Lösche ich das nrows-Argument, bleibt das Programm bei Stop1 stehen.
Scheinbar erkennt panda nicht das Zeilenende?

Code: Alles auswählen

import pandas as pd
import time

startzeit = time.time()

df = pd.read_csv('bitfinexUSD.csv', nrows=20, header = None, names = ['Datum','Kurs','Volumen'])
df.drop(df.columns[[2]], axis=1, inplace=True)

print(df[['Datum','Kurs']])
print("Stop1")

df.to_csv('ausgabedatei.csv')
print("Stop2")

endzeit = time.time()
dauer = endzeit - startzeit
print("Stop3")

print("Die Verarbeitung hat {:.4f} Minuten gedauert".format(dauer/60))
Allmentux
User
Beiträge: 6
Registriert: Sonntag 14. Januar 2018, 13:36

€ Es hält nicht an, es dauert nur länger:

Code: Alles auswählen

[10182252 rows x 2 columns]
Stop1
Stop2
Stop3
Das Durchsehen hat 0.2452 Minuten gedauert
Die gesamte Verarbeitung hat 2.6418 Minuten gedauert
Weiß einer wie ich den Speicherschritt beschleunigen kann?
Allmentux
User
Beiträge: 6
Registriert: Sonntag 14. Januar 2018, 13:36

So funktioniert das gerade ganz gut:

Code: Alles auswählen

import pandas as pd
import time

df = pd.read_csv('bitfinexUSD.csv', nrows = 100, header = None, names = ['Datum','Kurs','Volumen'])
df.drop(df.columns[[2]], axis=1, inplace=True)
df['Datum'] = pd.to_datetime(df['Datum'], unit='s')

print(df[['Datum','Kurs']])
df.to_csv('ausgabedatei.csv')
Nur wie stelle ich es jetzt an, das Ich z.b. mit der mean-Funktion von panda einen Mittelwert aller n Zeilen über einen Tag bilde und dies in einer neuen Liste zusammenfasse?
narpfel
User
Beiträge: 643
Registriert: Freitag 20. Oktober 2017, 16:10

@Allmentux: Hast du `DataFrame.groupby` probiert? Wenn ja: Was war das Ergebnis und was passt daran nicht?

Wenn du `df.Volumen` immer `drop`st, dann brauchst du das gar nicht erst zu laden (Parameter `usecols` für `pd.read_csv`); und anstatt manuell `to_datetime` auf `df.Datum` aufzurufen, kannst du (je nach Format, in dem die Daten gespeichert sind), `parse_dates` benutzen. Die Dokumentation ist da sehr ausführlich.
Antworten