Laufzeit von Dataframe Aufruf

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
Martin5032
User
Beiträge: 6
Registriert: Dienstag 10. Dezember 2019, 18:13

Hey Leute ich habe ein Programm geschrieben und der großteil der Laufzeit wird in einer Zeile verbracht:

ich habe mal ein ausführbaren Beispielcode geschrieben in dem alles Andere weggelassen wurde und nur das nötigste steht:

Code: Alles auswählen

import pandas as pd

labels= ["Apfel", "Banane", "Kartoffel", "Bier"]
size_1 =10000
size_2 = 4


output= pd.DataFrame(0,index=range(size_1),columns=labels)
liste= pd.DataFrame(20,index=labels,columns=["Menge", "Preis"])

for i in range(size_1):
	#im orginal werden in jeder äußeren iteration neue Megen und Preise für jedes Produkt berechnet
	#dann den Produkten im input Dataframe zugewiesen
	#dann wird das input Dataframe nach aufsteigenden Preisen sortiert 
    for t in range (size_2):
    	#dann hier schrittweise verrechnet wonach in der nächsten Zeile das Ergebnis des Berechnugsschritts in den output geschrieben werden soll  
        output.loc[i,labels[t]]=liste.iloc[t,0] #<----------------------- es geht um diese Zeile, sie ist für 80% meiner Laufzeit verantwortlich
die output Zuweisung mit Label[t] wird gemacht da durch das sortieren die Reihenfolge der Produkte nicht mehr übereinstimmt. (ist hier im Beispiel nicht der Fall)

ich habe das auch schonmal mit iterrows() aufgebaut das war aber nicht nennenswert schneller.

In meiner Anwendung bin ich im Bereich von 100.000 Iterationen und 20 Produkten sodass die Laufzeit bei knapp 115 Sekunden landet.

Hat jemand eine Idee wie ich diese Zuweisung schneller/effizienter machen kann ?
Benutzeravatar
__blackjack__
User
Beiträge: 13107
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Martin5032: Ich bin von der Beschreibung ein bisschen verwirrt — was ist denn `input` aus dem Kommentar? `liste` im Code? Wenn das nach Preis sortiert wird, dann stimmt doch die Reihenfolge des Index nicht mehr mit der Reihenfolge in `labels` überein und das müsste Unsinn sein was da gemacht wird.

Wenn die Reihenfolge dagegen gleich bleibt, kann man sich die innere Schleife sparen.

Code: Alles auswählen

#!/usr/bin/env python3
import pandas as pd


def main():
    labels = ["Apfel", "Banane", "Kartoffel", "Bier"]
    output = pd.DataFrame(0, index=range(10_000), columns=labels)
    liste = pd.DataFrame(20, index=labels, columns=["Menge", "Preis"])

    for i in range(len(output)):
        output.iloc[i] = liste.loc[:, "Menge"]


if __name__ == "__main__":
    main()
In jedem Fall sollte man `size_2` nicht an einen magischen Wert binden sondern an ``len(labels)``. Dann weiss der Leser sofort wovon der Wert abhängig ist und man kann auch keinen Fehler machen wenn man die `labels` mal in der Länge ändert.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Martin5032
User
Beiträge: 6
Registriert: Dienstag 10. Dezember 2019, 18:13

Ah da hast du Recht da habe ich Mist gebaut. Danke für den Hinweis !

so Sollte es Sinn ergeben:

Code: Alles auswählen

import pandas as pd

labels= ["Apfel", "Banane", "Kartoffel", "Bier"]
size_1 =10000



output= pd.DataFrame(0,index=range(size_1),columns=labels)
inventar= pd.DataFrame(20,index=labels,columns=["Menge", "Preis"])

for i in range(size_1):
	#im orginal werden in jeder äußeren iteration neue Megen und Preise für jedes Produkt berechnet
	#die dann den Produkten im inventar Dataframe zugewiesen werden 
	#danach wird das inventar Dataframe nach aufsteigenden Preisen sortiert 
	#und ein zweite neue Liste mit der neuen Label-Reihenfolge gebildet. bspw.
        labels_neu = ["Kartoffel", "Banane", "Apfel", "Bier"]
        for t in range (len(labels)):
            #dann wird hier schrittweise weiter gerechnet wonach in der nächsten Zeile das Ergebnis des Berechnugsschritts in den output geschrieben werden soll  
            output.loc[i,labels_neu[t]]=inventar.iloc[t,0] #<----------------------- es geht um diese Zeile, sie ist für 80% meiner Laufzeit verantwortlich
Zeil ist es die Laufzeit zu minimieren.
Benutzeravatar
__blackjack__
User
Beiträge: 13107
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Martin5032: Warum wird das denn umsortiert?

Und wie sieht es mit der Laufzeit aus wenn Du das einfach mal gar nicht mit einem `output`-Dataframe machst, sondern mit einem Wörterbuch das auf Listen abbildet und erst hinterher in einen Dateframe umgewandelt wird? Insgesamt sieht das nicht nach einer Aufgabe für einen Dataframe aus.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Martin5032
User
Beiträge: 6
Registriert: Dienstag 10. Dezember 2019, 18:13

Hey Leute,
ich habe selber eine Lösung gefunden und ich habe auch das Beispiel nochmal etwas verbessert vielleicht wird damit etwas klarer worauf ich hinaus wollte.

@__blackjack__ : Das Sortieren wurde durchgeführt um nacher die Produkte in der Reihenfolge vom günstigsten bis zum teuersten zu verarbeiten.
Die Reihenfolge ist in meinen Programm wichtig weil die Verarbeitung der einzelnen Produkte von einander abhängt.

Durch den Austausch der Pandas-Dataframes durch Numpy-Arrays konnte ich die Berechung um ein vielfaches beschleunigen. Ich hatte zwar zuvor mal gelesen das die Numpy-Vorteile auch
in Pandas enthalten sind aber da habe ich anscheinend etwas falsch verstanden.

Beide Beispiele können so ausgeführt werden.

Hier ist die alte Implementierung ausschließlich mit Dataframes:

Code: Alles auswählen

import pandas as pd
import time
start_1=time.time() # Messung Startzeit

# initialisieren der Beispiel-Komponenten
labels= ["Apfel", "Banane", "Kartoffel", "Bier"]
Menge=[12,25,14,50]
Preis=[11,2,100,45]
size_1 =10000

#Aufbau in Dataframes für einfache Handhabung / Verwaltung
output= pd.DataFrame(0,index=range(size_1),columns=labels)
inventar= pd.DataFrame(0,index=labels,columns=["Menge", "Preis"])
inventar.iloc[:,0]=Menge
inventar.iloc[:,1]=Preis

#Berechnung:
i=0
for i in range(size_1):
	#im orginal werden in jeder äußeren iteration neue Megen und Preise für jedes Produkt berechnet
	#die dann den Produkten im inventar Dataframe zugewiesen werden 
	
        sort = inventar.sort_values(by='Preis')		#sortieren nach aufsteigendem Preis
        labels_neu = sort.index				#neue Reihenfolge der inventar Labels
        
        t=0
        for t in range (len(labels)):
            #hier würde im Programm schrittweise das sortierte Inventar (sort) verarbeitet. In der nächsten Zeile soll dann das Ergebnis des Schrittes in das Output-Dataframe geschrieben werden
            output.loc[i,labels_neu[t]]=inventar.iloc[t,0]

end_1 =time.time()	#Messung End-Zeit  
print(end_1-start_1)
print(output)
Diese Berechnung braucht bei mir ca. 8 Sekunden für einen vollständigen Durchlauf.

Hier ist die neue Implementierung mit Umwandlung in Numpy-Arrays und einer Rücktransformation in ein Pandas-Dataframe :

Code: Alles auswählen

import pandas as pd
import time
start_1=time.time() # Messung Startzeit


# initialisieren der Beispiel-Komponenten
labels= ["Apfel", "Banane", "Kartoffel", "Bier"]
idx=[0,1,2,3]		#die Idx-Variable dient als Index innerhalb des Numpy Array und wird benutzt um nach dem Sortieren die Wertepaare(Mege,Preis) wieder den entsprechenden Produktnamen zu zuordnen.
Menge=[12,25,14,50]
Preis=[11,2,100,45]
size_1 =10000


#Aufbau in Dataframes für einfache Handhabung / Verwaltung
output= pd.DataFrame(0,index=range(size_1),columns=labels)
inventar= pd.DataFrame(0,index=labels,columns=["Menge", "Preis","idx"])
inventar.iloc[:,0]=Menge
inventar.iloc[:,1]=Preis
inventar.iloc[:,2]=idx

#Umwandlung in numpy-Arrays
out = output.to_numpy(dtype='int32')
inv = inventar.to_numpy(dtype='int32')

#Berechnung:
i=0
for i in range(size_1):
	#im orginal werden in jeder äußeren iteration neue Megen und Preise für jedes Produkt berechnet
	#die dann den Produkten im inventar Dataframe zugewiesen werden  
	
        sort= inv[inv[:,1].argsort()] 		#sortieren nach aufsteigendem Preis
        label_neu= sort[:,2]			#neue Reihenfolge der inventar Labels (hier mit den idx-Werten in der 3.ten Spalte von inv festgehalten)
        
        t=0
        for t in range (len(labels)):
            #hier würde im Programm schrittweise das sortierte inventar (sort) verarbeitet. In der nächsten Zeile soll dann das Ergebnis des Schrittes in das out-Array geschrieben werden
            out[i,label_neu[t]]=sort[t,0]
            
output= pd.DataFrame(out,index=range(size_1),columns=labels) 	#Rücktransformation in ein Pandas-Dataframe

end_1 =time.time() #Messung End-Zeit  
print(end_1-start_1)
print(output)


Dieser Code braucht nur ca. 0,062 Sekunden bzw. 62 Millisekunden und gibt ein identisches Ergebnis aus !
Die Numpy Variante ist also ca. 129 mal schneller
einfachTobi
User
Beiträge: 491
Registriert: Mittwoch 13. November 2019, 08:38

`i=0` und `t=0` kannst du dir sparen.
Auch wenn du mit Numpy gerade schneller rechnest, scheint mir irgendwas nicht ganz stimmig an dem Konzept. Meistens macht man etwas falsch, wenn man über DataFrames oder Arrays in for-Schleifen iteriert. Das ist in der Regel langsam und nur selten wirklich erforderlich.
Was genau geschieht mit dem Preis? Was wird wie neu berechnet? Und wie sind die Daten dabei voneinander abhängig? Wie werden die neuen Einträge dann "verarbeitet" (Vorgehen äußere und innere Schleife)? Kannst du uns die beiden Funktionen dazu auch noch zeigen?
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

`inv[:,1].argsort()` liefert Dir bereits `label_neu`, idx ist also unnötig und kann weg.
Welche Art „Verarbeitung” machst Du denn?
Antworten