Conversione di Django QuerySet in panda DataFrame


90

Vado a convertire un Django QuerySet in un panda DataFramecome segue:

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

Funziona, ma esiste un modo più efficiente?


Ciao @FrancoMariluis, mi dispiace per questo fuori tema: stai usando i panda nei progetti django. Puoi mostrare la grafica utilizzando "Stampa con matplotlib" tramite applicazioni web django. È una soluzione valida per te? Grazie.
dani herrera

Ciao, per mostrare la grafica in Django sto usando django-chartit, che funziona bene, ma sto pensando di usare matplotlib, che mi darebbe maggiore flessibilità
Franco Mariluis

Sembra piuttosto semplice e funziona. Qualche preoccupazione particolare?
Dmitry Shevchenko

Cosa c'è di sbagliato nel modo in cui ce l'hai adesso? Hai una preoccupazione particolare?
Burhan Khalid

Questo è stato il mio primo (e unico!) Approccio, ma poiché sono abbastanza nuovo per i panda, volevo vedere se c'era un altro modo, ma questo sembra essere buono.
Franco Mariluis

Risposte:


87
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')))

Quanto sopra è come faccio la stessa cosa. L'aggiunta più utile è specificare i campi a cui sei interessato. Se è solo un sottoinsieme dei campi disponibili a cui sei interessato, immagino che questo darebbe un aumento delle prestazioni.


37
L'uso di 'list ()' sembra essere stato deprecato (sono su Panda 0.12). L'utilizzo DataFrame.from_records()funziona meglio, ad es df = pd.DataFrame.from_records(BlogPost.objects.all().values()).
gregoltsov

2
Sarebbe più chiaro se questo usasse i nomi della domanda OP. Ad esempio, BlogPostdovrebbe essere uguale al suo SomeModel?
Hack-R

Ciao, c'è un modo per escludere una colonna che non ti serve nel dataframe?
Willower

19

Django Pandas risolve questo problema piuttosto ordinatamente: https://github.com/chrisdev/django-pandas/

Dal 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)

10
In che modo Django Pandas gestisce grandi set di dati? github.com/chrisdev/django-pandas/blob/master/django_pandas/… Questa riga mi spaventa, perché penso che significhi che l'intero set di dati verrà caricato in memoria in una volta.
Adam Barnes

@Ada Per creare un DataFrame utilizzando nomi di campi specificati:df = read_frame(qs, fieldnames=['age', 'wage', 'full_name'])
Gathide

Per quelli di voi in questo meraviglioso futuro che si stanno chiedendo di cosa stavo parlando, ecco un link più permanente alla fonte in quel momento: github.com/chrisdev/django-pandas/blob/…
Adam Barnes

9

Converti il ​​set di query su values_list () sarà più efficiente in termini di memoria rispetto a values ​​() direttamente. Poiché il metodo values ​​() restituisce un set di query di elenco di dict (chiave: coppie di valori), values_list () restituisce solo l'elenco di tuple (dati puri). Risparmierà circa il 50% di memoria, è sufficiente impostare le informazioni sulla colonna quando si chiama pd.DataFrame ().

Metodo 1:
    queryset = models.xxx.objects.values ​​("A", "B", "C", "D")
    df = pd.DataFrame (list (queryset)) ## consuma molta memoria
    #df = pd.DataFrame.from_records (queryset) ## funziona ma non cambia molto sull'utilizzo della memoria

Metodo 2:
    queryset = models.xxx.objects.values_list ("A", "B", "C", "D")
    df = pd.DataFrame (list (queryset), columns = ["A", "B", "C", "D"]) ## questo farà risparmiare il 50% di memoria
    #df = pd.DataFrame.from_records (queryset, columns = ["A", "B", "C", "D"]) ## Non funziona. Se si è verificato un arresto anomalo con il tipo di dati, il set di query non è l'elenco.

L'ho testato sul mio progetto con> 1 milione di righe di dati, il picco di memoria è stato ridotto da 2G a 1G.


2

Dal punto di vista di Django (non ho familiarità con pandas) questo va bene. La mia unica preoccupazione è che se hai un numero molto elevato di record, potresti incorrere in problemi di memoria. Se così fosse, sarebbe necessario qualcosa sulla falsariga di questo iteratore queryset efficiente in termini di memoria . (Lo snippet così come è scritto potrebbe richiedere una riscrittura per consentirne un uso intelligente .values()).


L'idea di @ GregoryGoltsov di utilizzare .from_records()e non utilizzare list()eliminerà il problema dell'efficienza della memoria.
piani cottura

1
La preoccupazione per l'efficienza della memoria è sul lato Django. .values()restituisce un ValuesQuerySetche memorizza nella cache i risultati, quindi per un set di dati sufficientemente grande, sarà piuttosto dispendioso in termini di memoria.
David Eyk

1
Ah sì. Dovresti indicizzare nel set di query e utilizzare .from_recordssenza la comprensione dell'elenco per eliminare entrambi i maiali di memoria. es pd.DataFrame.from_records(qs[i].__dict__ for i in range(qs.count())). Ma ti rimane quella fastidiosa "_state"colonna quando hai finito. qs.values()[i]è molto più veloce e più pulito, ma penso che si memorizzi nella cache.
piani cottura

1

Forse puoi usare model_to_dict

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()
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.