wake-up-neo.net

Python - Effiziente Methode zum Hinzufügen von Zeilen zum Datenrahmen

Aus dieser Frage und anderen scheint es nicht empfehlenswert zu sein, concat oder append zum Erstellen eines Pandas-Datenrahmens zu verwenden, da dieser jedes Mal den gesamten Datenrahmen neu kopiert. 

Mein Projekt beinhaltet das Abrufen einer kleinen Datenmenge alle 30 Sekunden. Dies könnte für ein 3-tägiges Wochenende dauern, sodass jemand leicht erwarten kann, dass mehr als 8000 Zeilen Zeile für Zeile erstellt werden. Was wäre der effizienteste Weg, um diesem Datenrahmen Zeilen hinzuzufügen?

13
Jarrod

Sie können Zeilen direkt zu einem DataFrame hinzufügen, indem Sie loc für einen nicht vorhandenen Index verwenden. Aus der Pandas Dokumentation :

In [119]: dfi
Out[119]: 
   A  B  C
0  0  1  0
1  2  3  2
2  4  5  4

In [120]: dfi.loc[3] = 5

In [121]: dfi
Out[121]: 
   A  B  C
0  0  1  0
1  2  3  2
2  4  5  4
3  5  5  5

Wie erwartet ist die Verwendung von loc wesentlich schneller als append (ca. 14x):

import pandas as pd
df = pd.DataFrame({"A": [1,2,3], "B": [1,2,3], "C": [1,2,3]})

%%timeit
df2 = pd.DataFrame({"A": [4], "B": [4], "C": [4]})
df.append(df2)

# 1000 loops, best of 3: 1.61 ms per loop

%%timeit
df.loc[3] = 4

# 10000 loops, best of 3: 113 µs per loop
17
sundance

Ich habe den df.loc[i] = [new_data]-Vorschlag dieser Antwort verwendet, aber ich habe> 500.000 Zeilen und das war sehr langsam.

Die Antworten sind zwar gut für die Frage des OPs, aber ich fand es effizienter, wenn es sich um eine große Anzahl von Zeilen (anstatt des vom OP beschriebenen Tricks) handelt, csvwriter zum Hinzufügen von Daten zu einem CSV-Objekt im Speicher zu verwenden. Verwenden Sie schließlich pandas.read_csv(csv), um die gewünschte DataFrame-Ausgabe zu generieren.

from io import BytesIO
from csv import writer 
import pandas as pd

output = BytesIO()
csv_writer = writer(output)

for row in iterable_object:
    csv_writer.writerow(row)

output.seek(0) # we need to get back to the start of the BytesIO
df = pd.read_csv(output)
return df

Dies war für ~ 500.000 Zeilen um das 1000-fache schneller und mit zunehmender Zeilenzahl wird die Geschwindigkeitsverbesserung nur noch größer (the df.loc[1] = [data] wird vergleichsweise viel langsamer).

Ich hoffe, das hilft jemandem, der Effizienz benötigt, wenn er mit mehr Zeilen als mit dem OP arbeitet.

27
Tom Harvey

Sie müssen das Problem in zwei Teile aufteilen:

  1. Die Daten werden alle 30 Sekunden effizient angenommen (gesammelt).
  2. Verarbeitung der Daten, sobald sie gesammelt wurden.

Wenn Ihre Daten kritisch sind (Sie können es sich nicht leisten, sie zu verlieren), senden Sie sie an eine Warteschlange und lesen Sie sie stapelweise aus der Warteschlange.

Die Warteschlange sorgt für eine zuverlässige (garantierte) Akzeptanz und dass Ihre Daten nicht verloren gehen.

Sie können die Daten aus der Warteschlange lesen und in einer Datenbank sichern.

Jetzt liest Ihre Python-App einfach aus der Datenbank und führt die Analyse in einem für die Anwendung sinnvollen Intervall durch - vielleicht möchten Sie stündliche Mittelwerte erstellen. In diesem Fall würden Sie das Skript jede Stunde ausführen, um die Daten aus der Datenbank abzurufen und die Ergebnisse möglicherweise in eine andere Datenbank/Tabelle/Datei zu schreiben.

Fazit: Teilen Sie das Sammeln und Analysieren von Teilen Ihrer Anwendung.

2
Burhan Khalid

Wenn Sie davon ausgehen, dass Ihr Datenrahmen in der richtigen Reihenfolge indexiert ist, können Sie:

Zuerst prüfen Sie, was der nächste Indexwert ist, um eine neue Zeile zu erstellen:

myindex = df.shape[0]+1 

Dann mit 'at' in jede gewünschte Spalte schreiben

df.at[myindex,'A']=val1
df.at[myindex,'B']=val2
df.at[myindex,'C']=val3
1
sparrow

Sundances Antwort mag in Bezug auf die Verwendung richtig sein, aber der Benchmark ist einfach falsch. Wie moobie richtig angedeutet, existiert in diesem Beispiel bereits ein Index 3, was den Zugriff wesentlich schneller macht als bei einem nicht existierenden Index. Schau dir das an:

%%timeit
test = pd.DataFrame({"A": [1,2,3], "B": [1,2,3], "C": [1,2,3]})
for i in range(0,1000):
    testrow = pd.DataFrame([0,0,0])
    pd.concat([test[:1], testrow, test[1:]])

2,15 s ± 88 ms pro Schleife (Mittelwert ± Standardabweichung von 7 Durchläufen, je 1 Schleife)

%%timeit
test = pd.DataFrame({"A": [1,2,3], "B": [1,2,3], "C": [1,2,3]})
for i in range(0,1000):
    test2 = pd.DataFrame({'A': 0, 'B': 0, 'C': 0}, index=[i+0.5])
    test.append(test2, ignore_index=False)
test.sort_index().reset_index(drop=True)

972 ms ± 14,4 ms pro Schleife (Mittelwert ± Standardabweichung von 7 Durchläufen, je 1 Schleife)

%%timeit
test = pd.DataFrame({"A": [1,2,3], "B": [1,2,3], "C": [1,2,3]})
for i in range(0,1000):
    test3 = [0,0,0]
    test.loc[i+0.5] = test3
test.reset_index(drop=True)

1,13 s ± 46 ms pro Schleife (Mittelwert ± Standardabweichung von 7 Durchläufen, je 1 Schleife)

Dies ist natürlich rein synthetisch, und ich habe diese Ergebnisse zugegebenermaßen nicht erwartet, aber es scheint, dass mit nicht existierenden Indizes .loc und .append verhalten sich ziemlich ähnlich. Lass das hier.

0
dasjanik