Перетворення Django QuerySet у pandas DataFrame


91

Я збираюся перетворити набір Django QuerySet на панду DataFrameнаступним чином:

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

Це працює, але чи є більш ефективний спосіб?


Привіт @FrancoMariluis, вибачте за це поза темою: чи використовуєте ви панди в проектах django. Ви показуєте графіку за допомогою "Складання графіку за допомогою matplotlib" через веб-програми django. Чи є дійсним рішенням для вас? Дякую.
dani herrera

Привіт, для показу графіки в Django я використовую django-chartit, який чудово працює, але я думаю про використання matplotlib, що дало б мені більшу гнучкість
Franco Mariluis

Виглядає досить просто, і це працює. Якісь особливі занепокоєння?
Дмитро Шевченко

Що поганого в тому, як у вас це зараз? Ви особливо турбуєтесь?
Бурхан Халід

Це був мій перший (і єдиний!) Підхід, але оскільки я досить новий для панд, я хотів подивитися, чи був інший спосіб, але, схоже, це хороший.
Франко Марілуа

Відповіді:


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

Вище сказано, як я роблю те саме. Найбільш корисним доповненням є вказівка, які поля вас цікавлять. Якщо це лише підмножина доступних полів, які вас цікавлять, то це, на мою думку, підвищить продуктивність.


37
Використання 'list ()', здається, застаріло (я на пандах 0,12). Використання DataFrame.from_records()працює краще, тобто df = pd.DataFrame.from_records(BlogPost.objects.all().values()).
Грегольцов

2
Було б зрозуміліше, якби тут використовувались назви із запитання OP. Наприклад, BlogPostмає бути таким самим, як і його SomeModel?
Hack-R

Привіт, чи є спосіб виключити стовпець, який вам не потрібен, у фреймі даних?
Willower

19

Django Pandas вирішує це досить акуратно: https://github.com/chrisdev/django-pandas/

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

11
Як Django Pandas має справу з великими наборами даних? github.com/chrisdev/django-pandas/blob/master/django_pandas/... Цей рядок мене лякає, бо, на мою думку, це означає, що весь набір даних буде завантажений відразу в пам'ять.
Адам Барнс,

@Ada Щоб створити DataFrame, використовуючи вказані імена полів:df = read_frame(qs, fieldnames=['age', 'wage', 'full_name'])
Gathide

Для тих з вас у цьому дивовижному майбутньому, хто цікавиться, про що я був, ось більш постійне посилання на джерело на той час: github.com/chrisdev/django-pandas/blob/…
Адам Барнс,

9

Перетворення набору запитів на values_list () буде ефективнішим для пам'яті, ніж безпосередньо у values ​​(). Оскільки метод values ​​() повертає набір запитів зі списку dict (ключ: пари значень), values_list () повертає лише список кортежу (чисті дані). Це заощадить близько 50% пам'яті, потрібно лише встановити інформацію про стовпець під час виклику pd.DataFrame ().

Спосіб 1:
    queryset = models.xxx.objects.values ​​("A", "B", "C", "D")
    df = pd.DataFrame (list (queryset)) ## споживає багато пам'яті
    #df = pd.DataFrame.from_records (набір запитів) ## працює, але особливих змін у використанні пам'яті немає

Спосіб 2:
    queryset = models.xxx.objects.values_list ("A", "B", "C", "D")
    df = pd.DataFrame (список (набір запитів), стовпці = ["A", "B", "C", "D"]) ## це заощадить 50% пам'яті
    #df = pd.DataFrame.from_records (набір запитів, стовпці = ["A", "B", "C", "D"]) ## Це не працює. Збій з типом даних є набором запитів, а не списком.

Я перевірив це на своєму проекті, що містить> 1 мільйон рядків даних, пік пам'яті зменшено з 2G до 1G.


2

З точки зору Джанго (я не знайомий pandas), це нормально. Мене турбує лише те, що якщо у вас дуже велика кількість записів, у вас можуть виникнути проблеми з пам’яттю. Якби це було так, потрібно було б щось на зразок цього ефективного ітератора запитів набору пам’яті . (Описаний фрагмент може вимагати певного переписування, щоб ви могли розумно використовувати його .values()).


@ Ідея Грегорія Гольцова використовувати, .from_records()а не використовувати list(), усуне занепокоєння щодо ефективності пам'яті.
конфорки

1
Проблема ефективності пам'яті стоїть на стороні Django. .values()повертає a, ValuesQuerySetякий кешує результати, тому для досить великого набору даних це буде досить великим обсягом пам'яті.
Девід Ейк,

1
Ага, так. Вам доведеться проіндексувати набір запитів і використовувати .from_recordsбез розуміння списку, щоб усунути обидва скачки пам'яті. напр pd.DataFrame.from_records(qs[i].__dict__ for i in range(qs.count())). Але у вас залишиться ця надокучлива "_state"колонка, коли закінчите. qs.values()[i]набагато швидше і чистіше, але я думаю, що це кешування.
конфорки

1

Можливо, ви можете використовувати 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()
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.