Mengonversi Django QuerySet ke pandas DataFrame

91

Saya akan mengubah Django QuerySet menjadi panda DataFramesebagai berikut:

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

Berhasil, tetapi apakah ada cara yang lebih efisien?

Franco Mariluis
sumber
Hai @FrancoMariluis, maaf tentang ini di luar topik: apakah Anda menggunakan panda ke dalam proyek django. Anda menampilkan grafik menggunakan "Merencanakan dengan matplotlib" melalui aplikasi web django. Apakah solusi yang valid untuk Anda? Terima kasih.
dani herrera
Hai, untuk menampilkan grafik di Django saya menggunakan django-chartit, yang berfungsi dengan baik, tetapi saya berpikir untuk menggunakan matplotlib, yang akan memberi saya lebih banyak fleksibilitas
Franco Mariluis
Terlihat sangat mudah, dan berhasil. Ada masalah khusus?
Dmitry Shevchenko
Apa yang salah dengan caramu mendapatkannya sekarang? Apakah Anda memiliki perhatian khusus?
Burhan Khalid
Ini adalah pendekatan pertama saya (dan satu-satunya!), Tetapi karena saya cukup baru mengenal panda, saya ingin melihat apakah ada cara lain, tetapi ini sepertinya cara yang bagus.
Franco Mariluis

Jawaban:

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

Di atas adalah bagaimana saya melakukan hal yang sama. Penambahan yang paling berguna adalah menentukan bidang mana yang Anda minati. Jika hanya sebagian dari bidang yang tersedia yang Anda minati, saya rasa ini akan memberikan peningkatan kinerja.

leksual
sumber
37
Menggunakan 'list ()' sepertinya sudah tidak digunakan lagi (Saya menggunakan pandas 0.12). Menggunakan DataFrame.from_records()bekerja lebih baik, yaitu df = pd.DataFrame.from_records(BlogPost.objects.all().values()).
gregoltsov
2
Akan lebih jelas jika menggunakan nama-nama pertanyaan OP. Misalnya, apakah BlogPostharus sama dengan miliknya SomeModel?
Hack-R
Hai, apakah ada cara untuk mengecualikan kolom yang tidak Anda perlukan di dataframe?
Willower
19

Django Panda memecahkan ini dengan agak rapi: https://github.com/chrisdev/django-pandas/

Dari 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
sumber
11
Bagaimana Django Pandas menangani kumpulan data besar? github.com/chrisdev/django-pandas/blob/master/django_pandas/… Baris ini membuat saya takut, karena menurut saya itu berarti seluruh dataset akan dimuat ke dalam memori sekaligus.
Adam Barnes
@Ada Untuk membuat DataFrame menggunakan nama bidang yang ditentukan:df = read_frame(qs, fieldnames=['age', 'wage', 'full_name'])
Gathide
Bagi Anda di masa depan yang luar biasa ini yang bertanya-tanya dengan apa yang saya alami
Adam Barnes
9

Mengonversi queryset pada values_list () akan lebih menghemat memori daripada pada values ​​() secara langsung. Karena metode values ​​() mengembalikan queryset dari daftar dict (key: value pairs), values_list () hanya mengembalikan daftar tuple (data murni). Ini akan menghemat sekitar 50% memori, hanya perlu mengatur informasi kolom ketika Anda memanggil pd.DataFrame ().

Metode 1:
    queryset = models.xxx.objects.values ​​("A", "B", "C", "D")
    df = pd.DataFrame (list (queryset)) ## menghabiskan banyak memori
    #df = pd.DataFrame.from_records (queryset) ## berfungsi tetapi tidak banyak perubahan pada penggunaan memori

Metode 2:
    queryset = models.xxx.objects.values_list ("A", "B", "C", "D")
    df = pd.DataFrame (list (queryset), kolom = ["A", "B", "C", "D"]) ## ini akan menghemat 50% memori
    #df = pd.DataFrame.from_records (queryset, kolom = ["A", "B", "C", "D"]) ## Tidak berfungsi. Crash dengan datatype is queryset not list.

Saya menguji ini pada proyek saya dengan> 1 juta baris data, memori puncak berkurang dari 2G menjadi 1G.

shengyang wang
sumber
2

Dari perspektif Django (saya tidak paham pandas) ini bagus. Satu-satunya kekhawatiran saya adalah jika Anda memiliki jumlah record yang sangat besar, Anda mungkin mengalami masalah memori. Jika ini masalahnya, sesuatu yang sejalan dengan iterator queryset hemat memori ini akan diperlukan. (Cuplikan seperti yang tertulis mungkin memerlukan beberapa penulisan ulang untuk memungkinkan penggunaan cerdas Anda .values()).

David Eyk
sumber
Ide @ GregoryGoltsov untuk menggunakan .from_records()dan tidak menggunakan list()akan menghilangkan masalah efisiensi memori.
kompor
1
Perhatian efisiensi memori ada di sisi Django. .values()mengembalikan hasil ValuesQuerySetyang di-cache, jadi untuk kumpulan data yang cukup besar, ini akan membutuhkan banyak memori.
David Eyk
1
Ahh iya. Anda harus mengindeks ke dalam queryset dan menggunakan .from_recordstanpa pemahaman daftar untuk menghilangkan kedua memori hog. mis pd.DataFrame.from_records(qs[i].__dict__ for i in range(qs.count())). Tapi Anda ditinggalkan dengan "_state"kolom yang mengganggu itu ketika Anda selesai. qs.values()[i]jauh lebih cepat dan lebih bersih, tapi saya pikir itu cache.
kompor
1

Anda mungkin bisa menggunakan 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
sumber