Django + Matplotlib (2)

김주언·2022년 8월 13일
0

Django

목록 보기
9/9
post-thumbnail

데이터 시각화하기

웹페이지에 시각화한 데이터를 보여주기

pandas 라이브러리 설치

pip install pandas
데이터 프레임을 생성하고 생성한 데이터 프레임은 맷플롯립으로 그래프를 생성하는데 사용한다.

plotting 은 utils.py 파일에서 다루고, 유틸파일을 뷰 파일에서 사용하도록 한다

utils.py 에 함수 추가하기

import base64
import uuid
from .models import *
from io import BytesIO
from matplotlib import pyplot as plt


def generate_code():
    return str(uuid.uuid4()).replace('-', '').upper()[:12]


def get_key(res_by):
    if res_by == '#1':
        key = 'transaction_id'
    elif res_by == '#2':
        key = 'created'
    elif res_by == '#3':
        key = 'customer'
    elif res_by == '#4':
        key = 'total_price'

    return key


def get_graph():
    buffer = BytesIO()
    plt.savefig(buffer, format='png')
    buffer.seek(0)
    image_png = buffer.getvalue()
    graph = base64.b64encode(image_png)
    graph = graph.decode('utf-8')
    buffer.close()
    return graph


def get_chart(chart_type, data, results_by, **kwargs):
    plt.switch_backend('AGG')
    fig = plt.figure(figsize=(10, 4))
    key = get_key(results_by)
    d = data.groupby(key, as_index=False)['total_price'].agg('sum')

    if chart_type == '#1':
        print("Bar Graph")
        plt.bar(d[key], d['total_price'])
    elif chart_type == '#2':
        print("Pie chart")
        plt.pie(data=d, x='total_price', labels=d[key])
    elif chart_type == '#3':
        print("Line graph")
        plt.plot(d[key], d['total_price'], color='gray', marker='o', linestyle='dashed')
    else:
        print("차트 타입이 선택되지 않았습니다")


    plt.tight_layout()
    chart = get_graph()

    return chart

get_chart() 함수가 실행 시

  1. pyplot.switch_backend('AGG')
    화면에 플롯팅되는 것을 막는다. 차트를 이미지로 넘길 것이다.

  2. fig 변수
    차트의 차원을 정의한다.

  3. key = get_key(results_by)
    사용자의 선택에 따라 키 변수를 설정한다. 폼에서의 RESULTS_CHOICE로 받은 내용이다

  4. d = data.groupby(key, as_index=False)['total_price'].agg('sum')
    총액을 사용하는 키를 이용하여 데이터를 그룹화한다.

  5. 사용자 선택에 따라 차트가 플롯팅된다.

  6. pyplot.tight_layout()
    차트의 사이즈를 fig 설정에 따라 맞춘다

  7. 차트 변수를 get_graph() 함수를 이용하여 초기화하고 반환한다. 이 메소드는 버퍼를 생성하며 시작된다.

  8. 차트는 버퍼에 이미지로 저장된다. 버퍼 내용은 base64.b64encode() 에 의해서 인코딩되고 디코딩되어 반환된다.


views.py 파일 수정

import pandas as pd
from django.shortcuts import render
from django.views.generic import ListView
from django.contrib import messages
from .forms import SalesSearchForm
from .models import *

# Create your views here.
from .utils import get_chart


def sales(request):
    sales_df = None
    chart = None
    no_data = None
    search_form = SalesSearchForm(request.POST or None)

    if request.method == 'POST':
        date_from = request.POST.get('date_from')
        date_to = request.POST.get('date_to')
        chart_type = request.POST.get('chart_type')
        results_by = request.POST.get('results_by')
        print(date_from, date_to, chart_type)
        sales_qs = Sale.objects.filter(created__date__lte=date_to, created__date__gte=date_from)

        if len(sales_qs) > 0:
            sales_df = pd.DataFrame(sales_qs.values())
            print(sales_df)

            sales_df['created'] = sales_df['created'].apply(lambda x: x.strftime('%d/%m/%Y'))
            sales_df.rename({'customer_id': 'customer', 'salesman_id': 'salesman', 'id': 'sales_id'}, axis=1,
                            inplace=True)

            chart = get_chart(chart_type, sales_df, results_by)
            sales_df = sales_df.to_html()
        else:
            messages.warning(request, "Apparently no data available...")

    context = {'search_form': search_form,
               'sales_df': sales_df,
               'chart': chart, }
    return render(request, 'sales.html', context)
  1. POST 요청 받았는지 확인 후, 받았다면 그 값을 변수에 할당한다.

  2. 모든 sales를 날짜에 따라 필터링한다. 필터링 후 데이터가 존재한다면 sales 쿼리세트 값을 이용하여 데이터 프레임을 생성한다.

  3. 날짜 포매팅

  4. 데이터 프레임 열 이름 수정하기

  5. 차트를 utils.py에 정의되어 있는get_chart() 를 사용하여 초기화한다. get_chart() 는 차트타입, 영업 데이터프레임, results by values를 변수로 받는다.

  6. 영업 데이터를 HTML 포맷으로 변환하여 데이터를 웹 페이지에서 나타낼 수 있도록 한다.


sales.html파일 수정

데이터 프레임과 차트를 나타낼 수 있도록 HTML을 수정한다.

{% extends 'base.html' %}
{% load static %}
{% load crispy_forms_tags %}

{% block title %}
    Home
{% endblock title %}
{% block content %}
    {% for message in messages %}
        <div role="alert" class="alert alert-warning">
            {{ message }}
        </div>
    {% endfor %}
    <form action="" method="post">
        {% csrf_token %}
        {{ search_form|crispy }}
        <button class="btn btn-primary mt-3" type="submit">Search</button>
    </form>
    <hr>
    {% if sales_df %}
        <b>Sales Dataframe</b>
        {{ sales_df|safe }}
        <hr>
        <hr>
        <b>Chart</b>
        <img src="data:image/png;base64, {{ chart|safe }}" alt="" id="img">
    {% endif %}<br>
{% endblock content %}

DB 변경사항 적용

python manage.py makemigrations
python manage.py migrate

profile
학생 점심을 좀 차리시길 바랍니다

0개의 댓글