Conversion de Django QuerySet en pandas DataFrame

90

Je vais convertir un Django QuerySet en pandas DataFramecomme suit:

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

Cela fonctionne, mais y a-t-il un moyen plus efficace?

Franco Mariluis
la source
Salut @FrancoMariluis, désolé pour ce sujet hors sujet: utilisez-vous des pandas dans des projets Django. Vous affichez des graphiques en utilisant "Tracer avec matplotlib" via les applications web django. Une solution valable pour vous? Merci.
dani herrera
Salut, pour avoir montré des graphiques dans Django, j'utilise django-chartit, qui fonctionne bien, mais je pense à utiliser matplotlib, ce qui me donnerait plus de flexibilité
Franco Mariluis
Ça a l'air assez simple et ça marche. Des préoccupations particulières?
Dmitry Shevchenko
Quel est le problème avec la façon dont vous l'avez maintenant? Avez-vous une préoccupation particulière?
Burhan Khalid
C'était ma première (et la seule!) Approche, mais comme je suis assez nouveau pour les pandas, je voulais voir s'il y avait une autre façon, mais cela semble être une bonne.
Franco Mariluis

Réponses:

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

Voici comment je fais la même chose. L'ajout le plus utile est de spécifier les champs qui vous intéressent. Si ce n'est qu'un sous-ensemble des champs disponibles qui vous intéresse, cela donnerait une amélioration des performances, j'imagine.

lexuel
la source
37
L'utilisation de 'list ()' semble être obsolète (je suis sur pandas 0.12). L'utilisation DataFrame.from_records()fonctionne mieux, c'est à dire df = pd.DataFrame.from_records(BlogPost.objects.all().values()).
gregoltsov
2
Ce serait plus clair si cela utilisait les noms de la question OP. Par exemple, est-il BlogPostcensé être le même que le sien SomeModel?
Hack-R
Salut, existe-t-il un moyen d'exclure une colonne dont vous n'avez pas besoin dans le dataframe?
Willower
19

Django Pandas résout cela plutôt proprement: https://github.com/chrisdev/django-pandas/

Depuis le 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
la source
10
Comment Django Pandas gère-t-il les grands ensembles de données? github.com/chrisdev/django-pandas/blob/master/django_pandas/... Cette ligne me fait peur, car je pense que cela signifie que l'ensemble de données sera chargé en mémoire à la fois.
Adam Barnes
@Ada Pour créer un DataFrame en utilisant les noms de champs spécifiés:df = read_frame(qs, fieldnames=['age', 'wage', 'full_name'])
Gathide
Pour ceux d'entre vous dans ce merveilleux avenir qui se demandent de quoi je parlais, voici un lien plus permanent vers la source de l'époque: github.com/chrisdev/django-pandas/blob/…
Adam Barnes
9

Convertir le jeu de requêtes sur values_list () sera plus efficace en mémoire que sur values ​​() directement. Puisque la méthode values ​​() renvoie un ensemble de requêtes de liste de dict (paires clé: valeur), values_list () renvoie uniquement la liste de tuple (données pures). Cela économisera environ 50% de mémoire, il suffit de définir les informations de la colonne lorsque vous appelez pd.DataFrame ().

Méthode 1:
    queryset = models.xxx.objects.values ​​("A", "B", "C", "D")
    df = pd.DataFrame (list (queryset)) ## consomme beaucoup de mémoire
    #df = pd.DataFrame.from_records (queryset) ## fonctionne mais pas beaucoup de changement sur l'utilisation de la mémoire

Méthode 2:
    queryset = models.xxx.objects.values_list ("A", "B", "C", "D")
    df = pd.DataFrame (list (queryset), columns = ["A", "B", "C", "D"]) ## cela économisera 50% de mémoire
    #df = pd.DataFrame.from_records (queryset, columns = ["A", "B", "C", "D"]) ## Cela ne fonctionne pas. Crashed with datatype is queryset not list.

J'ai testé cela sur mon projet avec> 1 million de données de lignes, la mémoire de pointe est réduite de 2G à 1G.

shengyang wang
la source
2

Du point de vue de Django (je ne suis pas familier avec pandas) c'est très bien. Ma seule préoccupation est que si vous avez un très grand nombre d'enregistrements, vous risquez de rencontrer des problèmes de mémoire. Si tel était le cas, quelque chose du genre de cet itérateur de jeu de requêtes efficace en mémoire serait nécessaire. (L'extrait tel qu'il est écrit peut nécessiter une réécriture pour permettre votre utilisation intelligente de .values()).

David Eyk
la source
L'idée de @ GregoryGoltsov d'utiliser .from_records()et de ne pas utiliser list()éliminera le problème d'efficacité de la mémoire.
plaques de cuisson
1
Le problème d'efficacité de la mémoire est du côté de Django. .values()renvoie un ValuesQuerySetqui met en cache les résultats, donc pour un ensemble de données assez grand, cela va être assez gourmand en mémoire.
David Eyk
1
Ahh oui. Vous devrez indexer le jeu de requêtes et l' utiliser .from_recordssans la compréhension de la liste pour éliminer les deux problèmes de mémoire. par exemple pd.DataFrame.from_records(qs[i].__dict__ for i in range(qs.count())). Mais vous vous retrouvez avec cette "_state"colonne ennuyeuse lorsque vous avez terminé. qs.values()[i]est beaucoup plus rapide et plus propre, mais je pense qu'il met en cache.
plaques de cuisson
1

Vous pouvez peut-être utiliser 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
la source