Konvertieren von Django QuerySet in Pandas DataFrame

88

Ich werde ein Django QuerySet DataFramewie folgt in ein Pandas konvertieren :

qs = SomeModel.objects.select_related().filter(date__year=2012)
q = qs.values('date', 'OtherField')
df = pd.DataFrame.from_records(q)

Es funktioniert, aber gibt es einen effizienteren Weg?

Franco Mariluis
quelle
Hallo @FrancoMariluis, tut mir leid, dass dies kein Thema ist: Verwenden Sie Pandas in Django-Projekten? Sie zeigen Grafiken mit "Plotten mit matplotlib" über Django-Webanwendungen. Ist eine gültige Lösung für Sie? Vielen Dank.
Dani Herrera
Hallo, für das Anzeigen von Grafiken in Django verwende ich Django-Chartit, was gut funktioniert, aber ich denke darüber nach, Matplotlib zu verwenden, was mir mehr Flexibilität geben würde
Franco Mariluis
Sieht ziemlich einfach aus und funktioniert. Besondere Bedenken?
Dmitry Shevchenko
Was ist falsch daran, wie du es jetzt hast? Haben Sie ein besonderes Anliegen?
Burhan Khalid
Dies war mein erster (und einziger!) Ansatz, aber da ich für Pandas ziemlich neu bin, wollte ich sehen, ob es einen anderen Weg gibt, aber dies scheint ein guter zu sein.
Franco Mariluis

Antworten:

84
import pandas as pd
import datetime
from myapp.models import BlogPost

df = pd.DataFrame(list(BlogPost.objects.all().values()))
df = pd.DataFrame(list(BlogPost.objects.filter(date__gte=datetime.datetime(2012, 5, 1)).values()))

# limit which fields
df = pd.DataFrame(list(BlogPost.objects.all().values('author', 'date', 'slug')))

Das Obige ist, wie ich das gleiche mache. Die nützlichste Ergänzung ist die Angabe der Felder, an denen Sie interessiert sind. Wenn es sich nur um eine Teilmenge der verfügbaren Felder handelt, an denen Sie interessiert sind, würde dies meiner Meinung nach zu einer Leistungssteigerung führen.

lexuell
quelle
36
Die Verwendung von 'list ()' scheint veraltet zu sein (ich bin auf Pandas 0.12). Verwenden DataFrame.from_records()funktioniert besser, dh df = pd.DataFrame.from_records(BlogPost.objects.all().values()).
Gregoltsov
1
Es wäre klarer, wenn dies die Namen aus der OP-Frage verwenden würde. Zum Beispiel wird BlogPostangenommen , dasselbe wie seine sein SomeModel?
Hack-R
Hallo, gibt es eine Möglichkeit, eine Spalte auszuschließen, die Sie im Datenrahmen nicht benötigen?
Willower
18

Django Pandas löst dies ziemlich ordentlich: https://github.com/chrisdev/django-pandas/

Aus der README:

class MyModel(models.Model):
    full_name = models.CharField(max_length=25)
    age = models.IntegerField()
    department = models.CharField(max_length=3)
    wage = models.FloatField()

from django_pandas.io import read_frame
qs = MyModel.objects.all()
df = read_frame(qs)
David Watson
quelle
9
Wie geht Django Pandas mit großen Datenmengen um? github.com/chrisdev/django-pandas/blob/master/django_pandas/… Diese Zeile macht mir Angst, weil ich denke, dass dies bedeutet, dass der gesamte Datensatz auf einmal in den Speicher geladen wird.
Adam Barnes
@Ada So erstellen Sie einen DataFrame mit den angegebenen Feldnamen:df = read_frame(qs, fieldnames=['age', 'wage', 'full_name'])
Gathide
Für diejenigen unter Ihnen in dieser wunderbaren Zukunft, die sich fragen, worüber ich gerade gesprochen habe, ist hier ein dauerhafterer Link zur Quelle zu der Zeit: github.com/chrisdev/django-pandas/blob/…
Adam Barnes
8

Das Konvertieren des Abfragesatzes in values_list () ist speichereffizienter als in values ​​() direkt. Da die Methode values ​​() eine Abfragemenge der Liste von dict (Schlüssel: Wertepaare) zurückgibt, gibt values_list () nur die Liste von Tupeln (reine Daten) zurück. Dies spart etwa 50% Speicherplatz. Sie müssen lediglich die Spalteninformationen festlegen, wenn Sie pd.DataFrame () aufrufen.

Methode 1:
    queryset = models.xxx.objects.values ​​("A", "B", "C", "D")
    df = pd.DataFrame (Liste (Abfragesatz)) ## verbraucht viel Speicher
    #df = pd.DataFrame.from_records (Abfragesatz) ## funktioniert, ändert sich jedoch nicht wesentlich an der Speichernutzung

Methode 2:
    queryset = models.xxx.objects.values_list ("A", "B", "C", "D")
    df = pd.DataFrame (Liste (Abfragesatz), Spalten = ["A", "B", "C", "D"]) ## Dies spart 50% Speicher
    #df = pd.DataFrame.from_records (Abfragesatz, Spalten = ["A", "B", "C", "D"]) ## Es funktioniert nicht. Absturz mit Datentyp ist Queryset nicht Liste.

Ich habe dies in meinem Projekt mit> 1 Million Zeilendaten getestet, der Spitzenspeicher ist von 2G auf 1G reduziert.

Shengyang Wang
quelle
2

Aus der Django-Perspektive (mit der ich nicht vertraut bin pandas) ist dies in Ordnung. Meine einzige Sorge ist, dass bei einer sehr großen Anzahl von Datensätzen Speicherprobleme auftreten können. Wenn dies der Fall wäre, wäre etwas in der Art dieses speichereffizienten Abfragesatz-Iterators erforderlich. (Das geschriebene Snippet muss möglicherweise neu geschrieben werden, damit Sie es intelligent verwenden können. .values())

David Eyk
quelle
@ GregoryGoltsovs Idee, zu verwenden .from_records()und nicht zu verwenden list(), beseitigt die Bedenken hinsichtlich der Speichereffizienz.
Kochfelder
1
Das Problem der Speichereffizienz liegt auf der Django-Seite. .values()Gibt ein ValuesQuerySetErgebnis zurück, das die Ergebnisse zwischenspeichert. Für ein ausreichend großes Dataset ist es also ziemlich speicherintensiv.
David Eyk
1
Ahh ja. Sie würden in den queryset indizieren haben und verwenden , .from_recordsohne die Liste Verständnis beiden Speicherfresser zu eliminieren. zB pd.DataFrame.from_records(qs[i].__dict__ for i in range(qs.count())). Aber "_state"wenn Sie fertig sind, bleibt Ihnen diese nervige Kolumne. qs.values()[i]ist viel schneller und sauberer, aber ich denke, es wird zwischengespeichert.
Kochfelder
0

Sie können möglicherweise model_to_dict verwenden

import datetime
from django.forms import model_to_dict
pallobjs = [ model_to_dict(pallobj) for pallobj in PalletsManag.objects.filter(estado='APTO_PARA_VENTA')] 
df = pd.DataFrame(pallobjs)
df.head()
Pjl
quelle