Conversión de Django QuerySet a pandas DataFrame

90

Voy a convertir un Django QuerySet en pandas de la DataFramesiguiente manera:

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

Funciona, pero ¿hay alguna forma más eficiente?

Franco Mariluis
fuente
Hola @FrancoMariluis, perdón por esto fuera del tema: ¿estás usando pandas en proyectos de django? Muestra gráficos usando "Trazado con matplotlib" a través de aplicaciones web de django. ¿Es una solución válida para ti? Gracias.
dani herrera
Hola, para mostrar gráficos en Django estoy usando django-chartit, que funciona bien, pero estoy pensando en usar matplotlib, lo que me daría más flexibilidad
Franco Mariluis
Parece bastante sencillo y funciona. ¿Alguna preocupación en particular?
Dmitry Shevchenko
¿Qué hay de malo en la forma en que lo tienes ahora? ¿Tiene alguna inquietud particular?
Burhan Khalid
Este fue mi primer (¡y único!) Enfoque, pero como soy bastante nuevo en pandas, quería ver si había otra forma, pero esta parece ser una buena.
Franco Mariluis

Respuestas:

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

Lo anterior es cómo hago lo mismo. La adición más útil es especificar los campos que le interesan. Si sólo está interesado en un subconjunto de los campos disponibles, supongo que esto aumentaría el rendimiento.

lexual
fuente
37
El uso de 'list ()' parece haber quedado obsoleto (estoy en pandas 0.12). Usar DataFrame.from_records()funciona mejor, es decir df = pd.DataFrame.from_records(BlogPost.objects.all().values()).
gregoltsov
2
Sería más claro si esto usara los nombres de la pregunta OP. Por ejemplo, ¿se BlogPostsupone que es el mismo que el suyo SomeModel?
Hack-R
Hola, ¿hay alguna forma de excluir una columna que no necesita en el marco de datos?
Willower
19

Django Pandas resuelve esto bastante bien: https://github.com/chrisdev/django-pandas/

Del 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
fuente
10
¿Cómo trata Django Pandas con grandes conjuntos de datos? github.com/chrisdev/django-pandas/blob/master/django_pandas/… Esta línea me asusta, porque creo que significa que todo el conjunto de datos se cargará en la memoria a la vez.
Adam Barnes
@Ada Para crear un DataFrame usando nombres de campo especificados:df = read_frame(qs, fieldnames=['age', 'wage', 'full_name'])
Gathide
Para aquellos de ustedes en este maravilloso futuro que se preguntan sobre lo que estaba hablando, aquí hay un enlace más permanente a la fuente en ese momento: github.com/chrisdev/django-pandas/blob/…
Adam Barnes
9

Convertir el conjunto de consultas en lista_valores () será más eficiente en memoria que en valores () directamente. Dado que el método values ​​() devuelve un conjunto de consultas de una lista de dict (pares clave: valor), values_list () solo devuelve una lista de tuplas (datos puros). Ahorrará aproximadamente un 50% de memoria, solo necesita configurar la información de la columna cuando llame a pd.DataFrame ().

Método 1:
    queryset = models.xxx.objects.values ​​("A", "B", "C", "D")
    df = pd.DataFrame (list (queryset)) ## consume mucha memoria
    #df = pd.DataFrame.from_records (queryset) ## funciona pero no hay muchos cambios en el uso de la memoria

Método 2:
    queryset = models.xxx.objects.values_list ("A", "B", "C", "D")
    df = pd.DataFrame (lista (conjunto de consultas), columnas = ["A", "B", "C", "D"]) ## esto ahorrará 50% de memoria
    #df = pd.DataFrame.from_records (conjunto de consultas, columnas = ["A", "B", "C", "D"]) ## No funciona. Se bloqueó con el tipo de datos, el conjunto de consultas no está en la lista.

Probé esto en mi proyecto con> 1 millón de datos de filas, la memoria máxima se reduce de 2G a 1G.

shengyang wang
fuente
2

Desde la perspectiva de Django (no estoy familiarizado pandas) esto está bien. Mi única preocupación es que si tiene una gran cantidad de registros, puede tener problemas de memoria. Si este fuera el caso, sería necesario algo similar a este iterador de conjunto de consultas de memoria eficiente . (El fragmento tal como está escrito puede requerir una reescritura para permitir un uso inteligente de .values()).

David Eyk
fuente
La idea de @ GregoryGoltsov de usar .from_records()y no usar list()eliminará la preocupación por la eficiencia de la memoria.
placas
1
La preocupación por la eficiencia de la memoria está en el lado de Django. .values()devuelve un ValuesQuerySetque almacena en caché los resultados, por lo que para un conjunto de datos lo suficientemente grande, será bastante intensivo en memoria.
David Eyk
1
Ahh si. Tendría que indexar en el conjunto de consultas y usar .from_recordssin la comprensión de la lista para eliminar ambos acaparadores de memoria. ej pd.DataFrame.from_records(qs[i].__dict__ for i in range(qs.count())). Pero te quedas con esa "_state"columna molesta cuando terminas. qs.values()[i]es mucho más rápido y limpio, pero creo que almacena en caché.
placas
1

Tal vez puedas usar 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()
Pjl
fuente