django使用matplotlib绘统计图

毕业设计需要用matplotlib画图,记录一下学习过程吧。之前已经记录过关于matplotlib的安装和使用。这次将要在django里的views函数中使用。完整的代码在https://github.com/PegasusWang/Physics_web.git,顺便再熟悉下git的使用吧。

###简介
挺简单的一个小项目,首先在安卓端设计登录和注册窗口,以及题目显示Activity,安卓的后台用的是django,获取题目信息用get方法,用django返回json格式的数据。登录和注册用post发送用户名和密码,然后在views函数中验证。用户提交题目答案后,在web端接收后更新数据库,之后用matplotlib绘制答案统计图。

###设计matplotlibUtil模块
我需要用到俩种图,一种是柱状图(histogram),一种是饼图(pie chart),之前写过如何参考官方demo修改成自己需要的格式。为了方便就把这两个函数写进去一个python文件里:matplotlibUtil.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
Bar chart demo with pairs of bars grouped for easy comparison.
"""
import numpy as np
import matplotlib as mpl
mpl.use('Agg')    # for backend without gui
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']    # set default font for mac


def draw_histogram(a_num, b_num, c_num, d_num, n_groups, path):
    """Draw result.

    :param a_num: tuple of select A users.
                  eg: (12, 2, 34, 12, 78, 13).
    :param n_groups: The length of tuple a_num.
    :param path: The path images would save.
    """
    fig, ax = plt.subplots()
    index = np.arange(n_groups)
    bar_width = 0.2
    opacity = 0.4
    error_config = {'ecolor': '0.3'}

    rects1 = plt.bar(index, a_num, bar_width,
                     alpha=opacity,
                     color='b',
                     error_kw=error_config,
                     label='A')

    rects2 = plt.bar(index + bar_width, b_num, bar_width,
                     alpha=opacity,
                     color='r',
                     error_kw=error_config,
                     label='B')

    rects3 = plt.bar(index+bar_width*2, c_num, bar_width,
                     alpha=opacity,
                     color='b',
                     error_kw=error_config,
                     label='C')

    rects4 = plt.bar(index+bar_width*3, d_num, bar_width,
                     alpha=opacity,
                     color='g',
                     error_kw=error_config,
                     label='D')

    def autolabel(rects):
        # attach some text labels
        for rect in rects:
            height = rect.get_height()
            ax.text(rect.get_x()+rect.get_width()/2.0, 1.05*height,
                    '%d'%int(height), ha='center', va='bottom')
    autolabel(rects1)
    autolabel(rects2)
    autolabel(rects3)
    autolabel(rects4)


    plt.xlabel(u'题号')
    plt.ylabel(u'人数')
    plt.title(u'答题结果统计')
    #plt.xticks(index + bar_width, ('1', '2', '3', '4', '5', '6'))
    plt.xticks(index + bar_width, tuple(map(str, range(1, n_groups+1))))
    ax.set_ybound(0, 40)

    plt.tight_layout()
    #plt.show()
    plt.savefig(path)
    plt.clf()    # note: remember plt.clf() to clear buffer
    plt.close()


def draw_piechart(question_info, explode, path):
    """Draw pie chart of each question.

    :param: question_info is a list of users.  eg: [12, 23, 43, 13]
            means 12 people select A, 23 select B, 43 C, 13 D.
    """
    labels = 'A', 'B', 'C', 'D'
    colors = ['yellowgreen', 'gold', 'lightskyblue', 'lightcoral']
    #explode = (0, 0.1, 0, 0) # only "explode" the 2nd slice (i.e. 'Hogs')

    plt.pie(question_info, explode=explode, labels=labels, colors=colors,
                    autopct='%1.1f%%', shadow=True)
    # Set aspect ratio to be equal so that pie is drawn as a circle.
    plt.axis('equal')

    #plt.show()
    plt.savefig(path)
    plt.clf()    # note: remember plt.clf() to clear buffer
    plt.close()

已经把函数封装成了需要的形式,现在开始在views里边使用。

###在views函数中绘图
现在有了绘图模块就可以在views.py中直接调用了。

def show_result(request):
    """draw result image"""
    import matplotlibUtil
    from mysite.settings import MEDIA_ROOT

    # draw histogram
    n_groups = Question.objects.all().count()
    options = ('A', 'B', 'C', 'D')
    nums = []
    for option in options:
        num = []
        for tid in range(1, n_groups + 1):
            cnt = Result.objects.filter(t_id=tid, my_option=option).count()
            num.append(cnt)
        nums.append(num)
    histogram_path = MEDIA_ROOT + 'images/results/' + '0.png'
    print histogram_path
    try:
    matplotlibUtil.draw_histogram(nums[0], nums[1], nums[2], nums[3], 
                                  n_groups, histogram_path)
    except:
        return render(request, 'physics/result_image.html', {'images': None})


    # draw pie chart
    for tid in range(1, n_groups+1):
        explode = [0.0, 0.0, 0.0, 0.0]
        answer_num = ord(Question.objects.get(t_id=tid).t_answer.upper()) - ord('A')
        explode[answer_num] = 0.1
        explode = tuple(explode)

        question_info = []
        for each in options:
            cnt = Result.objects.filter(t_id=tid, my_option=each).count()
            question_info.append(cnt)

        chart_path = MEDIA_ROOT + 'images/results/' + str(tid) + '.png'
        print chart_path
        try:
            matplotlibUtil.draw_piechart(question_info, explode, chart_path)
        except:
            return render(request, 'physics/result_image.html', {'images': None})


    images = []
    for i in range(n_groups+1):
        images.append('/media/images/results/'+str(i)+'.png')
    return render(request, 'physics/result_image.html',
                  {'images': images})

然后就是模板中使用了,直接引用就可以了。我用的是一个模板自己改了一下。
最后的效果如下: