Daten nach jeder Iteration in txt oder csv Datei speichern

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
Particledust
User
Beiträge: 15
Registriert: Samstag 1. Februar 2020, 09:17

Guten Tag,

man betrachte das folgende einfache Beispiel:

Code: Alles auswählen

x1, x2, x3 = 0, 0, 0
index = 0

for i in range(3):
    index += 1
    x1 += 2
    x2 += 2
    x3 += 2
    
Das Ziel ist es, die erzeugten Werte index, x1, x2, x3 in eine txt oder csv Datei zu speichern. Dies sollte dann wie folgt aussehen:

Code: Alles auswählen

Index    Data1    Data2    Data3    
1        2        2        2        
2        4        4        4        
3        6        6        6        
Im Internet finde ich nur Methoden bei denen die Werte nach jeder Iteration in eine Liste oder Array gespeichert werden. Erst wenn die for-Schleife beendet ist, werden die Daten mit pandas.DataFrame in eine txt/csv datei umgewandelt.
In meinem Fall ist dies nicht möglich. Ich habe mehr als 10^7 Iterationen und habe nicht genügend Arbeitsspeicher um alle Werte in einer Liste/Array zwischen zu speichern.

Daher meine Frage: Ist es möglich die Werte index, x1, x2, x3 nach jeder Iteraion der for-Schleife in eine txt/csv Datei zu schreiben, ohne dass ich diese in einer Liste/Array zwischen speichern muss?
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Particledust: `DataFrame.to_csv()` hat ein `mode`-Argument, man kann die Datei also auch zum anhängen weiterer Daten öffnen statt sie zu überschreiben.

`index` ist in dem Beispiel ein bisschen redundant, weil immer gleich `i`.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Nachtrag: Man kann `to_csv()` auch ein zum schreiben geöffnetes Dateiobjekt übergeben, dann muss nicht jedes mal die Datei geöffnet und geschlossen werden.

Allerdings sehe ich nicht den Sinn ein `DataFrame`-Objekt für jede einzelne Zeile zu erzeugen. Das ist doch ziemlicher Overkill. Selbst ein Numpy-Array würde ich nur in Betracht ziehen wenn damit tatsächlich etwas sinnvolles gemacht wird was mit einer Liste nicht ginge.

Hier das Beispiel mal mit dem `csv`-Modul aus der Standardbibliothek:

Code: Alles auswählen

#!/usr/bin/env python3
import csv


def main():
    row = [0] * 3
    with open("test.csv", "w", encoding="ascii", newline="") as file:
        writer = csv.writer(file)
        writer.writerow(
            ["Index", *(f"Data{i}" for i in range(1, len(row) + 1))]
        )
        for index in range(3):
            row = [x + 2 for x in row]
            writer.writerow([index + 1, *row])


if __name__ == "__main__":
    main()
Und noch nicht ganz so ernst gemeint: 13 Datensätze/Zeilen ist doch nicht viel. Wie viel mehr Zeilen sind es denn?

Code: Alles auswählen

In [490]: 10^7                                                                  
Out[490]: 13
😜
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Particledust
User
Beiträge: 15
Registriert: Samstag 1. Februar 2020, 09:17

`DataFrame.to_csv()` hat ein `mode`-Argument, man kann die Datei also auch zum anhängen weiterer Daten öffnen statt sie zu überschreiben.
mode = 'a' wird zum Anhängen verwendet. Ich habe versuch, dass zu implementieren aber leider funktioniert es nicht.

Code: Alles auswählen

Import pandas as pd
Import os

x1, x2, x3, x4 = 0, 0, 0, 0
index = 0
for i in range(3):
    x1 += 2
    x2 += 2
    x3 += 2
    x4 += 2
    df=pd.DataFrame([i + 1,x1,x2,x3,x4],columns=['Index','Data1','Data2','Data3','Data4'])
    df.to_csv('data.csv',index=False,mode='a')
Ich erhalte die Fehlermeldung:

Code: Alles auswählen

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
~/anaconda3/lib/python3.7/site-packages/pandas/core/internals/managers.py in create_block_manager_from_blocks(blocks, axes)
   1677                 blocks = [
-> 1678                     make_block(values=blocks[0], placement=slice(0, len(axes[0])))
   1679                 ]

~/anaconda3/lib/python3.7/site-packages/pandas/core/internals/blocks.py in make_block(values, placement, klass, ndim, dtype, fastpath)
   3266 
-> 3267     return klass(values, ndim=ndim, placement=placement)
   3268 

~/anaconda3/lib/python3.7/site-packages/pandas/core/internals/blocks.py in __init__(self, values, placement, ndim)
    127                 "Wrong number of items passed {val}, placement implies "
--> 128                 "{mgr}".format(val=len(self.values), mgr=len(self.mgr_locs))
    129             )

ValueError: Wrong number of items passed 1, placement implies 5

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
<ipython-input-43-0d8c3526372c> in <module>
     11     x3 += 2
     12     x4 += 2
---> 13     df=pd.DataFrame([index,x1,x2,x3,x4],columns=['Index','Data1','Data2','Data3','Data4'])
     14     df.to_csv('data.csv',index=False,mode='a')

~/anaconda3/lib/python3.7/site-packages/pandas/core/frame.py in __init__(self, data, index, columns, dtype, copy)
    462                     mgr = arrays_to_mgr(arrays, columns, index, columns, dtype=dtype)
    463                 else:
--> 464                     mgr = init_ndarray(data, index, columns, dtype=dtype, copy=copy)
    465             else:
    466                 mgr = init_dict({}, index, columns, dtype=dtype)

~/anaconda3/lib/python3.7/site-packages/pandas/core/internals/construction.py in init_ndarray(values, index, columns, dtype, copy)
    211         block_values = [values]
    212 
--> 213     return create_block_manager_from_blocks(block_values, [columns, index])
    214 
    215 

~/anaconda3/lib/python3.7/site-packages/pandas/core/internals/managers.py in create_block_manager_from_blocks(blocks, axes)
   1686         blocks = [getattr(b, "values", b) for b in blocks]
   1687         tot_items = sum(b.shape[0] for b in blocks)
-> 1688         construction_error(tot_items, blocks[0].shape[1:], axes, e)
   1689 
   1690 

~/anaconda3/lib/python3.7/site-packages/pandas/core/internals/managers.py in construction_error(tot_items, block_shape, axes, e)
   1717         raise ValueError("Empty data passed with indices specified.")
   1718     raise ValueError(
-> 1719         "Shape of passed values is {0}, indices imply {1}".format(passed, implied)
   1720     )
   1721 

ValueError: Shape of passed values is (5, 1), indices imply (5, 5)
Hast du eine Idee wo der Fehler liegt?
Benutzeravatar
sparrow
User
Beiträge: 4187
Registriert: Freitag 17. April 2009, 10:28

Also ich bin ja vom Dorf und Pandas ist bestimmt für manches gut, aber ist das hier nicht ein bischen mit Kanonen auf Spatzen (Achtung Wortwitz!) zu schießen?
__blackjack__ hat dir doch schon eine Lösung gezeigt.

Edit: und seit wann kommt "Import" eigentlich am Parser vorbei? Der Code läuft doch so gar nicht.
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Particledust: Der Fehler ist beim erstellen des `DataFrame`: Du gibst Werte für eine Spalte an, aber fünf Spaltenüberschriften. Lösung: Kein DataFrame verwenden. Wie gesagt würde ich nicht einmal ein Numpy-Array verwenden wenn das nicht auch tatsächlich sinnvoll für irgend etwas verwendet wird.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Particledust
User
Beiträge: 15
Registriert: Samstag 1. Februar 2020, 09:17

@_blackjack_

Ich habe deinen Lösungsvorschlag implementiert aber festgestellt, dass ich erst Zugriff auf die Daten habe, wenn die Schleife beendet ist.

Code: Alles auswählen

import csv

row = [0] * 3
with open("bsp.csv", "w", encoding="ascii", newline="") as file:
    
    writer = csv.writer(file)
    writer.writerow(["Index", 'Data1', 'Data2', 'Data3'])
    
    for index in range(5):
        
        for j in range(10**8):  
        
            'Computation'
            
    row = [1,1,1]
    writer.writerow([index , *row])
Wenn man den obigen Code laufen lässt, wird die bsp.csv Datei erst nach Ende der Schleife die Daten enthalten. Klickt man auf die bsp.csv während die Schleife läuft, so ist diese leer.

In meinem Fall läuft die Schleife jedoch über mehrere Monate. Ich möchte zu einem beliebigen Zeitpunkt Zugriff auf die Daten haben (um diese z.B Grafisch darzustellen um einen möglichen Trend zu beobachten).

Hast du eine Idee wie man den Code modifizieren kann um das Problem zu lösen?

Grüsse

Ps: @ sparrow du hast natürlich Recht. Import muss kleingeschrieben werden, damit der Code (aus Beitrag 4) läuft.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Dann öffne die Datei jeweils zum "a"nhängen.

Code: Alles auswählen

import csv

def append_row(row):
    with open("bsp.csv", "a", encoding="ascii", newline="") as output:
        writer = csv.writer(output)
        writer.writerow(row)

append_row(["Index", 'Data1', 'Data2', 'Data3'])
for index in range(5):
    for j in range(10**8):  
        'Computation'
    row = [1,1,1]
    append_row([index , *row])
einfachTobi
User
Beiträge: 491
Registriert: Mittwoch 13. November 2019, 08:38

Läuft die Schleife gewollte mehrere Monate oder dauert es einfach so lange? Meist kann unter Ausnutzung der Möglichkeiten von Numpy etc. gewaltig Zeit sparen. Was genau machst du in der Schleife?
Antworten