Преобразование 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 ГБ.

Shengyang Wang
источник
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()
Pjl
источник