Преобразование Django QuerySet в pandas DataFrame


93

Я собираюсь преобразовать Django QuerySet в pandas 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, который дал бы мне больше гибкости,
Франко Марилуис

Выглядит довольно просто, и это работает. Есть какие-то особые опасения?
Дмитрий Шевченко

Что не так с тем, как у вас это сейчас? Вас что-то беспокоит?
Бурхан Халид

Это был мой первый (и единственный!) Подход, но, поскольку я новичок в pandas, я хотел посмотреть, есть ли другой способ, но он кажется хорошим.
Франко Марилуис,

Ответы:


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

Выше показано, как я делаю то же самое. Наиболее полезным дополнением является указание, какие поля вас интересуют. Если это только подмножество доступных полей, которые вас интересуют, то это, как я полагаю, дало бы прирост производительности.


38
Использование list () кажется устаревшим (я использую pandas 0.12). Использование DataFrame.from_records()работ лучше, то есть df = pd.DataFrame.from_records(BlogPost.objects.all().values()).
gregoltsov

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/ ...
Адам Барнс,

10

Преобразование набора запросов в 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 (queryset) ## работает, но без особых изменений в использовании памяти

Способ 2:
    queryset = models.xxx.objects.values_list ("A", "B", "C", "D")
    df = pd.DataFrame (list (queryset), columns = ["A", "B", "C", "D"]) ## это сэкономит 50% памяти
    #df = pd.DataFrame.from_records (queryset, columns = ["A", "B", "C", "D"]) ## Это не работает. Сбой с типом данных - это набор запросов, а не список.

Я тестировал это в своем проекте с данными> 1 миллиона строк, пиковая память уменьшена с 2 ГБ до 1 ГБ.


2

С точки зрения Django (с которой я не знаком pandas) это нормально. Меня беспокоит только то, что если у вас очень большое количество записей, вы можете столкнуться с проблемами памяти. Если бы это было так, то потребовалось бы что-то вроде этого итератора набора запросов с эффективным использованием памяти . (Фрагмент в том виде, в каком он был написан, может потребовать некоторой переписывания, чтобы его можно было использовать с умом .values()).


Идея @GregoryGoltsov использовать .from_records()и не использовать list()устранит проблему эффективности памяти.
hobs

1
Проблема эффективности памяти находится на стороне Django. .values()возвращает, ValuesQuerySetкоторый кэширует результаты, поэтому для достаточно большого набора данных это будет довольно интенсивно для памяти.
Дэвид Эйк

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

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.