Einen leeren Pandas DataFrame erstellen und dann füllen?

461

Ich beginne hier mit den Pandas DataFrame-Dokumenten: http://pandas.pydata.org/pandas-docs/stable/dsintro.html

Ich möchte den DataFrame iterativ mit Werten in einer Zeitreihenberechnung füllen. Grundsätzlich möchte ich den DataFrame mit den Spalten A, B und Zeitstempelzeilen initialisieren, alle 0 oder alle NaN.

Ich würde dann Anfangswerte hinzufügen und diese Daten durchgehen, um beispielsweise die neue Zeile aus der vorherigen Zeile zu berechnen row[A][t] = row[A][t-1]+1.

Ich verwende derzeit den folgenden Code, aber ich finde ihn irgendwie hässlich und es muss eine Möglichkeit geben, dies direkt mit einem DataFrame zu tun, oder nur eine bessere Möglichkeit im Allgemeinen. Hinweis: Ich verwende Python 2.7.

import datetime as dt
import pandas as pd
import scipy as s

if __name__ == '__main__':
    base = dt.datetime.today().date()
    dates = [ base - dt.timedelta(days=x) for x in range(0,10) ]
    dates.sort()

    valdict = {}
    symbols = ['A','B', 'C']
    for symb in symbols:
        valdict[symb] = pd.Series( s.zeros( len(dates)), dates )

    for thedate in dates:
        if thedate > dates[0]:
            for symb in valdict:
                valdict[symb][thedate] = 1+valdict[symb][thedate - dt.timedelta(days=1)]

    print valdict
Matthias Kauer
quelle
6
Wachsen Sie niemals einen DataFrame! Es ist immer billiger, an eine Python-Liste anzuhängen und sie am Ende in einen DataFrame zu konvertieren, sowohl in Bezug auf Speicher als auch auf Leistung.
cs95
@ cs95 Was ist funktional anders zwischen .appendin pd und dem Anhängen einer Liste? Ich weiß, dass .appendin Pandas der gesamte Datensatz in ein neues Objekt kopiert wird. Funktioniert das Anhängen von Pythons anders?
Lamma
@Lamma Details finden Sie in meiner Antwort unten. Beim Anhängen an df wird jedes Mal ein neuer DataFrame im Speicher erstellt, anstatt den vorhandenen zu verwenden, was offen gesagt eine Verschwendung ist.
cs95

Antworten:

330

Hier einige Vorschläge:

Verwendung date_rangefür den Index:

import datetime
import pandas as pd
import numpy as np

todays_date = datetime.datetime.now().date()
index = pd.date_range(todays_date-datetime.timedelta(10), periods=10, freq='D')

columns = ['A','B', 'C']

Hinweis: Wir könnten einen leeren DataFrame (mit NaNs) einfach durch Schreiben erstellen :

df_ = pd.DataFrame(index=index, columns=columns)
df_ = df_.fillna(0) # with 0s rather than NaNs

Verwenden Sie ein numpy-Array, um diese Art von Berechnungen für die Daten durchzuführen:

data = np.array([np.arange(10)]*3).T

Daher können wir den DataFrame erstellen:

In [10]: df = pd.DataFrame(data, index=index, columns=columns)

In [11]: df
Out[11]: 
            A  B  C
2012-11-29  0  0  0
2012-11-30  1  1  1
2012-12-01  2  2  2
2012-12-02  3  3  3
2012-12-03  4  4  4
2012-12-04  5  5  5
2012-12-05  6  6  6
2012-12-06  7  7  7
2012-12-07  8  8  8
2012-12-08  9  9  9
Andy Hayden
quelle
2
pd.date_range () funktioniert bei mir nicht. Ich habe es mit DateRange versucht (aus der automatischen Vervollständigung von Eclipse), aber das funktioniert mit Zeichenfolgen als Datumsformat, oder? Der Gesamtansatz funktioniert jedoch (ich habe den Index in etwas anderes geändert).
Matthias Kauer
2
date_range ist eine Factory-Funktion zum Erstellen von Datetime-Indizes und war eine neue Funktion in 0.8.0 . Ich würde definitiv empfehlen, auf die neueste stabile Version (0.9.1) zu aktualisieren. Es gibt viele Fehlerkorrekturen und neue Funktionen. :)
Andy Hayden
26
Nach meinen Erfahrungen ist das Erstellen eines Datenrahmens mit der erforderlichen Größe, der mit NaNs gefüllt ist, und das anschließende Auffüllen mit Werten viel langsamer als das Erstellen eines Datenrahmens mit indexx 0Dimensionen ( columns = []) und das Anhängen einer Spalte in jeder Schleife einer Schleife. Ich meine df[col_name] = pandas.Series([...])in einer Schleife, die durch Spaltennamen iteriert. Im ersteren Fall braucht nicht nur die Speicherzuweisung Zeit, sondern das Ersetzen von NaNs durch neue Werte scheint extrem langsam zu sein.
Deeenes
5
@deeenes auf jeden Fall. Diese Antwort sollte dies wahrscheinlich klarer machen - Sie möchten sehr selten (wenn überhaupt) einen leeren Datenrahmen (von NaNs) erstellen.
Andy Hayden
1
Gemäß dieser Antwort stackoverflow.com/a/30267881/2302569 Sie müssen das Ergebnis von fillna zuweisen oder param inplace = True übergeben
JayJay
169

Wenn Sie einfach einen leeren Datenrahmen erstellen und ihn später mit einigen eingehenden Datenrahmen füllen möchten, versuchen Sie Folgendes:

newDF = pd.DataFrame() #creates a new dataframe that's empty
newDF = newDF.append(oldDF, ignore_index = True) # ignoring index is optional
# try printing some data from newDF
print newDF.head() #again optional 

In diesem Beispiel verwende ich dieses Pandas-Dokument , um einen neuen Datenrahmen zu erstellen und dann Anhängen zu verwenden Datenrahmen , um mit Daten von oldDF in die newDF zu schreiben.

Wenn ich weiterhin neue Daten aus mehr als einer alten DD an diese neue DD anhängen muss, verwende ich einfach eine for-Schleife, um über pandas.DataFrame.append () zu iterieren.

geekidharsh
quelle
14
Bitte beachten Sie, dass append(und in ähnlicher Weise concat) jedes Mal der gesamte Datensatz in ein neues Objekt kopiert wird. Daher kann und wird das Iterieren und Anhängen zu einem erheblichen Leistungseinbruch führen. Weitere Informationen finden Sie unter: pandas.pydata.org/pandas-docs/stable/merging.html
MoustafaAAtta
4
@MoustafaAAtta Welche Alternativen gibt es, um iterativ Daten an den Datenrahmen anzuhängen?
MysteryGuy
2
@ MoustafaAAtta Ist Fred Antwort in diesem Beitrag: stackoverflow.com/questions/10715965/… besser in dieser Hinsicht?
MysteryGuy
@MoustafaAAtta Sie können möglicherweise nur Zeilen an einen Datenrahmen anhängen. Es wird weiterhin ein neues Objekt erstellt, aber für kleinere Datensätze kann dies hilfreich sein. pandas.pydata.org/pandas-docs/stable/user_guide/…
geekidharsh
135

Der richtige Weg ™, um einen DataFrame zu erstellen

TLDR; (Lies einfach den fetten Text)

Die meisten Antworten hier zeigen Ihnen, wie Sie einen leeren DataFrame erstellen und ausfüllen, aber niemand wird Ihnen sagen, dass dies eine schlechte Sache ist.

Hier ist mein Rat: Warten Sie, bis Sie sicher sind, dass Sie alle Daten haben, mit denen Sie arbeiten müssen. Verwenden Sie eine Liste, um Ihre Daten zu sammeln, und initialisieren Sie dann einen DataFrame, wenn Sie bereit sind.

data = []
for a, b, c in some_function_that_yields_data():
    data.append([a, b, c])

df = pd.DataFrame(data, columns=['A', 'B', 'C'])

Es ist immer billiger, an eine Liste anzuhängen und einen DataFrame auf einmal zu erstellen, als einen leeren DataFrame (oder einen von NaNs) zu erstellen und immer wieder daran anzuhängen. Listen beanspruchen außerdem weniger Speicher und sind eine viel leichtere Datenstruktur , anhängen und entfernen können (falls erforderlich).

Der andere Vorteil dieser Methode ist, dass dtypesautomatisch abgeleitet wird (anstatt zuzuweisenobject allen zuzuweisen).

Der letzte Vorteil ist das a RangeIndexautomatisch für Ihre Daten erstellt wird , sodass Sie sich weniger Sorgen machen müssen (werfen Sie einen Blick auf die Armen appendund locMethoden unten, Sie werden in beiden Elementen sehen, die eine angemessene Behandlung des Index erfordern).


Dinge, die Sie NICHT tun sollten

append oder concat innerhalb einer Schleife

Hier ist der größte Fehler, den ich von Anfängern gesehen habe:

df = pd.DataFrame(columns=['A', 'B', 'C'])
for a, b, c in some_function_that_yields_data():
    df = df.append({'A': i, 'B': b, 'C': c}, ignore_index=True) # yuck
    # or similarly,
    # df = pd.concat([df, pd.Series({'A': i, 'B': b, 'C': c})], ignore_index=True)

Der Speicher wird für jede appendoder jede concatOperation neu zugewiesen . Wenn Sie dies mit einer Schleife verbinden, erhalten Sie eine quadratische Komplexitätsoperation . Von demdf.append Dokumentseite :

Das iterative Anhängen von Zeilen an einen DataFrame kann rechenintensiver sein als eine einzelne Verkettung. Eine bessere Lösung besteht darin, diese Zeilen an eine Liste anzuhängen und die Liste dann gleichzeitig mit dem ursprünglichen DataFrame zu verketten.

Der andere Fehler, der damit verbunden df.appendist, besteht darin, dass Benutzer häufig vergessen, dass das Anhängen keine In-Place-Funktion ist. Daher muss das Ergebnis zurück zugewiesen werden. Sie müssen sich auch um die dtypes kümmern:

df = pd.DataFrame(columns=['A', 'B', 'C'])
df = df.append({'A': 1, 'B': 12.3, 'C': 'xyz'}, ignore_index=True)

df.dtypes
A     object   # yuck!
B    float64
C     object
dtype: object

Der Umgang mit Objektspalten ist niemals eine gute Sache, da Pandas Operationen an diesen Spalten nicht vektorisieren können. Sie müssen dies tun, um das Problem zu beheben:

df.infer_objects().dtypes
A      int64
B    float64
C     object
dtype: object

loc innerhalb einer Schleife

Ich habe auch gesehen loc, wie an einen DataFrame angehängt wurde, der leer erstellt wurde:

df = pd.DataFrame(columns=['A', 'B', 'C'])
for a, b, c in some_function_that_yields_data():
    df.loc[len(df)] = [a, b, c]

Nach wie vor haben Sie nicht jedes Mal die benötigte Speichermenge vorab zugewiesen, sodass der Speicher jedes Mal neu vergrößert wird, wenn Sie eine neue Zeile erstellen . Es ist genauso schlimm wieappend und noch hässlicher.

Leerer Datenrahmen von NaNs

Und dann wird ein DataFrame mit NaNs und allen damit verbundenen Einschränkungen erstellt.

df = pd.DataFrame(columns=['A', 'B', 'C'], index=range(5))
df
     A    B    C
0  NaN  NaN  NaN
1  NaN  NaN  NaN
2  NaN  NaN  NaN
3  NaN  NaN  NaN
4  NaN  NaN  NaN

Es erstellt wie die anderen einen DataFrame aus Objektspalten.

df.dtypes
A    object  # you DON'T want this
B    object
C    object
dtype: object

Das Anhängen hat immer noch alle Probleme wie die oben genannten Methoden.

for i, (a, b, c) in enumerate(some_function_that_yields_data()):
    df.iloc[i] = [a, b, c]

Der Beweis ist im Pudding

Das Timing dieser Methoden ist der schnellste Weg, um festzustellen, wie stark sie sich in Bezug auf Speicher und Nutzen unterscheiden.

Geben Sie hier die Bildbeschreibung ein

Benchmarking-Code als Referenz.

cs95
quelle
6
Listenanhang sollte der beste Weg für diese Art von Frage sein
YOBEN_S
9
Dies muss millionenfach verbessert werden. Wachsen Sie niemals einen Datenrahmen!
Buggy
3
@ user3293236 Schade, dass Sie jedes Mal, wenn Sie eine alte Frage beantworten, von unten beginnen müssen;)
cs95
2
Dies ist eines der Dinge, die ich am meisten hasse. So oft sieht man das 𝒓𝒆𝒂𝒍 𝒄𝒐𝒓𝒓𝒆𝒄𝒕 𝒂𝒏𝒔𝒘𝒆𝒓, das mit wenigen Stimmen irgendwo unten bleibt und nie akzeptiert wird. Ich vermisse Code mit 𝚍𝚏 = 𝚙𝚍.𝙳𝚊𝚝𝚊𝙵𝚛𝚊𝚖𝚎 ([]), um einen leeren Pandas-Datenrahmen zu erstellen. Diese Antwort positiv bewerten. Hervorragende Erklärung, @ cs95!
Jonathan
1
Dies steht buchstäblich in der Dokumentation. "Das iterative Anhängen von Zeilen an einen DataFrame kann rechenintensiver sein als eine einzelne Verkettung. Eine bessere Lösung besteht darin, diese Zeilen an eine Liste anzuhängen und die Liste dann gleichzeitig mit dem ursprünglichen DataFrame zu verketten." pandas.pydata.org/pandas-docs/version/0.21/generated/…
Endolith
132

Initialisieren Sie einen leeren Frame mit Spaltennamen

import pandas as pd

col_names =  ['A', 'B', 'C']
my_df  = pd.DataFrame(columns = col_names)
my_df

Fügen Sie einem Frame einen neuen Datensatz hinzu

my_df.loc[len(my_df)] = [2, 4, 5]

Vielleicht möchten Sie auch ein Wörterbuch übergeben:

my_dic = {'A':2, 'B':4, 'C':5}
my_df.loc[len(my_df)] = my_dic 

Fügen Sie Ihrem vorhandenen Frame einen weiteren Frame hinzu

col_names =  ['A', 'B', 'C']
my_df2  = pd.DataFrame(columns = col_names)
my_df = my_df.append(my_df2)

Leistungsüberlegungen

Wenn Sie Zeilen innerhalb einer Schleife hinzufügen, berücksichtigen Sie Leistungsprobleme. Bei etwa den ersten 1000 Datensätzen ist die Leistung von "my_df.loc" besser, wird jedoch allmählich langsamer, indem die Anzahl der Datensätze in der Schleife erhöht wird.

Wenn Sie vorhaben, innerhalb einer großen Schleife dünner zu werden (z. B. 10 Millionen Datensätze oder so), ist es besser, eine Mischung aus diesen beiden zu verwenden. Füllen Sie einen Datenrahmen mit iloc, bis die Größe ungefähr 1000 erreicht, hängen Sie ihn dann an den ursprünglichen Datenrahmen an und leeren Sie den temporären Datenrahmen. Dies würde Ihre Leistung um das Zehnfache steigern.

Afshin Amiri
quelle
my_df = my_df.append(my_df2)funktioniert bei mir nur, wenn ich es spezifiziere ignore_index=True.
Nasif Imtiaz Ohi
0

Angenommen, ein Datenrahmen mit 19 Zeilen

index=range(0,19)
index

columns=['A']
test = pd.DataFrame(index=index, columns=columns)

Spalte A konstant halten

test['A']=10

Beibehaltung von Spalte b als Variable, die durch eine Schleife gegeben ist

for x in range(0,19):
    test.loc[[x], 'b'] = pd.Series([x], index = [x])

Sie können das erste x in pd.Series([x], index = [x])durch einen beliebigen Wert ersetzen

Ajay Ohri
quelle