Convertendo Django QuerySet em pandas DataFrame

93

Vou converter um Django QuerySet em um pandas da DataFrameseguinte maneira:

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

Funciona, mas existe uma maneira mais eficiente?

Franco Mariluis
fonte
Olá @FrancoMariluis, desculpe por isso fora do tópico: você está usando pandas em projetos django. Você mostra os gráficos usando "Plotting with matplotlib" via aplicativos da web django. É uma solução válida para você? Obrigado.
dani herrera
Olá, por mostrar gráficos em Django, estou usando django-chartit, que funciona bem, mas estou pensando em usar matplotlib, o que me daria mais flexibilidade
Franco Mariluis
Parece bastante simples e funciona. Alguma preocupação em particular?
Dmitry Shevchenko de
O que há de errado com a maneira como você está agora? Você tem uma preocupação particular?
Burhan Khalid de
Esta foi a minha primeira (e única!) Abordagem, mas como sou bastante novo para os pandas, queria ver se havia outra forma, mas esta parece ser uma boa.
Franco Mariluis de

Respostas:

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

A descrição acima é como eu faço a mesma coisa. A adição mais útil é especificar em quais campos você está interessado. Se for apenas um subconjunto dos campos disponíveis em que você está interessado, imagino que isso aumentaria o desempenho.

lexual
fonte
38
O uso de 'list ()' parece ter se tornado obsoleto (estou no pandas 0.12). Usando DataFrame.from_records()funciona melhor, ou seja df = pd.DataFrame.from_records(BlogPost.objects.all().values()).
gregoltsov
2
Ficaria mais claro se usasse os nomes da pergunta do OP. Por exemplo, é BlogPostsuposto ser igual ao dele SomeModel?
Hack-R
Olá, existe uma maneira de excluir uma coluna de que você não precisa no dataframe?
Willower
19

Django Pandas resolve isso muito bem: https://github.com/chrisdev/django-pandas/

Do 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
fonte
11
Como o Django Pandas lida com grandes conjuntos de dados? github.com/chrisdev/django-pandas/blob/master/django_pandas/… Esta linha me assusta, porque acho que significa que todo o conjunto de dados será carregado na memória de uma vez.
Adam Barnes
@Ada Para criar um DataFrame usando nomes de campo especificados:df = read_frame(qs, fieldnames=['age', 'wage', 'full_name'])
Gathide
Para aqueles de vocês neste futuro maravilhoso que estão se perguntando sobre o que eu estava falando, aqui está um link mais permanente para a fonte na época: github.com/chrisdev/django-pandas/blob/…
Adam Barnes
10

Converter o queryset em values_list () será mais eficiente em memória do que em values ​​() diretamente. Visto que o método values ​​() retorna um queryset de lista de dict (chave: pares de valor), values_list () retorna apenas uma lista de tupla (dados puros). Ele vai economizar cerca de 50% da memória, só precisa definir as informações da coluna quando você chamar pd.DataFrame ().

Método 1:
    queryset = models.xxx.objects.values ​​("A", "B", "C", "D")
    df = pd.DataFrame (list (queryset)) ## consome muita memória
    #df = pd.DataFrame.from_records (queryset) ## funciona, mas não muda muito no uso de memória

Método 2:
    queryset = models.xxx.objects.values_list ("A", "B", "C", "D")
    df = pd.DataFrame (list (queryset), colunas = ["A", "B", "C", "D"]) ## isso economizará 50% da memória
    #df = pd.DataFrame.from_records (queryset, colunas = ["A", "B", "C", "D"]) ## Não funciona. Crashed with data type is queryset not list

Eu testei isso em meu projeto com> 1 milhão de linhas de dados, o pico de memória foi reduzido de 2G para 1G.

Shengyang Wang
fonte
2

Da perspectiva do Django (não estou familiarizado pandas), isso é bom. Minha única preocupação é que, se você tiver um número muito grande de registros, poderá ter problemas de memória. Se fosse esse o caso, algo na linha deste iterador queryset com eficiência de memória seria necessário. (O snippet conforme escrito pode exigir alguma reescrita para permitir o seu uso inteligente .values()).

David Eyk
fonte
A ideia de @GregoryGoltsov de usar .from_records()e não usar list()vai eliminar a preocupação com a eficiência da memória.
horas de
1
A preocupação com a eficiência da memória está do lado do Django. .values()retorna um ValuesQuerySetque armazena resultados em cache, portanto, para um conjunto de dados grande o suficiente, vai consumir bastante memória.
David Eyk
1
Ahh sim. Você teria que indexar no queryset e usar .from_recordssem a compreensão da lista para eliminar ambos os devoradores de memória. por exemplo pd.DataFrame.from_records(qs[i].__dict__ for i in range(qs.count())). Mas você fica com aquela "_state"coluna irritante quando terminar. qs.values()[i]é muito mais rápido e limpo, mas acho que armazena em cache.
horas
1

Você pode 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
fonte