文章来源:Python Django+SQL+Pandas+Pyecharts自建在线数据分析平台(一)
作者:ccpic
感谢:感谢作者 ccpic 分享的优质内容,本网页主要用于学习知识的存档备份,欢迎点击原网页支持作者。
(一)需求分析&技术实现
(二)初步搭建Django环境
(三)页面布局&Django模板
(四)SQL+Pandas初步处理数据
(五)前端表单交互
(六)Ajax异步传参与加载
(七)前端数据格式的处理
(八)DataTables接管前端表格
(十)静态图表的展示
(十一)“导出数据至Excel”功能
(十二)添加和配置缓存
(十三)用户登录系统
(十四)部署Django至生产环境
Echarts是一个由百度开源的纯JS的数据可视化库,而Pyecharts可以理解为一个Echarts的Python封装版本。我觉得Pyecharts的存在是很有必要的,因为Python作为近年来数据领域最常用的语言,数据处理后正好利用其无缝衔接到数据可视化这个后续步骤。
另外让人感到亲切的一点是,Echarts和Pyecharts都是国人开发和开源的。
在Pyecharts的官方文档中,就有和Django整合的例子:
虽然此例子中用的2种实现方式——渲染整体模板和rest API类视图,都不太适合我们这个项目。但是我们可以看出Pyecharts和Django整合的基本套路:
- 前端html部分准备一个空白的DOM
- JS部分用echarts初始化命令渲染这个元素
- 后端准备数据
- 使用数据用Pyecharts生成图表对象
- 利用Pyechart全局API dump_options()生成图表对象json格式的全局options
- 前端JS部分用AJAX通信获取后端json
- AJAX调用json成功后使用.setOption方法刷新图表元素呈现可视化结果
明白了以上这些步骤,可以在我们的项目里有条不紊地实现任何Pyecharts交互图表。本章的目标为创建一个条形图和折线图的组合图,呈现我们query方法返回的整体市场趋势及同比增长率。以下每个具体步骤都与上方对应:
1、在display.html模板内加入下方代码创造一个布局片段包含一个id为bar_total_trend的空白DOM:
<div class="ui tab segment active" data-tab="total"> ... <h3 class="ui header"> <div class="content"> 定义市场总量趋势 <div class="sub header">柱状折线复合图</div> </div> </h3> <div class="ui divider"></div> <div class="ui container"> <div id="bar_total_trend" style="width:1000px; height:600px;"></div> </div> </div>
|
2、在filter.html的JS部分(注意因为Django模板的继承特性,他们是互通的,也就是filter.html的JS代码能控制display.html的DOM),初始化上面的DOM元素为echarts对象:
<script type="text/javascript"> $("#AJAX_get").click(function () { ... var chart = echarts.init(document.getElementById('bar_total_trend'), 'white', {renderer: 'canvas'}); chart.showLoading({ text : '正在加载数据' }); ... }) </script>
|
3&4&5、后端views.py编写一个prepare_chart方法,可以根据字符串图表类型准备数据以及Pyecharts的图表对象:
from .charts import *
D_TRANS = { 'MAT': '滚动年', 'QTR': '季度', 'Value': '金额', 'Volume': '盒数', 'Volume (Counting Unit)': '最小制剂单位数', '滚动年': 'MAT', '季度': 'QTR', '金额': 'Value', '盒数': 'Volume', '最小制剂单位数': 'Volume (Counting Unit)' }
def prepare_chart(df, chart_type, form_dict, ): label = D_TRANS[form_dict['PERIOD_select'][0]] + D_TRANS[form_dict['UNIT_select'][0]]
if chart_type == 'bar_total_trend': df_abs = df.sum(axis=1) df_abs.index = df_abs.index.strftime("%Y-%m") df_abs = df_abs.to_frame() df_abs.columns = [label] df_gr = df_abs.pct_change(periods=4) df_gr.dropna(how='all', inplace=True) df_gr.replace([np.inf, -np.inf, np.nan], '-', inplace=True) chart = echarts_stackbar(df=df_abs, df_gr=df_gr ) return chart.dump_options() else: return None
|
这部分最后的.dump_options()是重点,返回的不是图表对象,而是json格式的图表全局设置。
因为views.py已经有点臃肿了,我们在相同目录新建一个charts.py,把生成Pyecharts图表对象的方法都写到这里。本例里只写了个柱状图和线图的组合:
from pyecharts.charts import Line, Bar from pyecharts import options as opts
def echarts_stackbar(df, df_gr=None, datatype='ABS' ) -> Bar:
axislabel_format = '{value}' max = df[df>0].sum(axis=1).max() min = df[df<=0].sum(axis=1).min() if datatype in ['SHARE', 'GR']: df = df.multiply(100).round(2) axislabel_format = '{value}%' max = 100 min = 0 if df_gr is not None: df_gr = df_gr.multiply(100).round(2)
if df.empty is False: stackbar = ( Bar() .add_xaxis(df.index.tolist()) ) for i, item in enumerate(df.columns): stackbar.add_yaxis(item, df[item].values.tolist(), stack='总量', label_opts=opts.LabelOpts(is_show=False), z_level=1, ) if df_gr is not None: stackbar.extend_axis( yaxis=opts.AxisOpts( name="同比增长率", type_="value", axislabel_opts=opts.LabelOpts(formatter="{value}%"), ) ) stackbar.set_global_opts( legend_opts=opts.LegendOpts(pos_top='5%', pos_left='10%', pos_right='60%'), toolbox_opts=opts.ToolboxOpts(is_show=True), tooltip_opts=opts.TooltipOpts(trigger='axis', axis_pointer_type='cross', ), xaxis_opts=opts.AxisOpts(type_='category', boundary_gap=True, axislabel_opts=opts.LabelOpts(rotate=90), splitline_opts=opts.SplitLineOpts(is_show=False, linestyle_opts=opts.LineStyleOpts( type_='dotted', opacity=0.5, ) ) ), yaxis_opts=opts.AxisOpts(max_=max, min_=min, type_="value", axislabel_opts=opts.LabelOpts(formatter=axislabel_format), splitline_opts=opts.SplitLineOpts(is_show=True, linestyle_opts=opts.LineStyleOpts( type_='dotted', opacity=0.5, ) ) ), ) if df_gr is not None: line = ( Line() .add_xaxis(xaxis_data=df_gr.index.tolist()) .add_yaxis( series_name="同比增长率", yaxis_index=1, y_axis=df_gr.values.tolist(), label_opts=opts.LabelOpts(is_show=False), linestyle_opts=opts.LineStyleOpts(width=3), symbol_size=8, itemstyle_opts=opts.ItemStyleOpts(border_width=1, border_color='', border_color0='white'), z_level=2 ) ) else: stackbar = (Bar())
if df_gr is not None: return stackbar.overlap(line) else: return stackbar
|
6&7、在views.py的query方法里的context增加之前步骤生成的图表options,切记一定要整合在query方法内,如果另起炉灶,读取数据的步骤要重复一遍,等于性能减半:
def query(request): ...
bar_total_trend = json.loads(prepare_chart(pivoted, 'bar_total_trend', form_dict))
context = { ... 'bar_total_trend': bar_total_trend }
return HttpResponse(json.dumps(context, ensure_ascii=False), content_type="application/json charset=utf-8")
|
可以看看这时的query方法返回了什么,可以看到下方大段的echarts options:

回到filter.html的JS AJAX部分,在回调函数部分加入以下代码:
<script type="text/javascript"> $("#AJAX_get").click(function (event) { ... $.ajax({ ... success: function (ret) { chart.clear(); chart.setOption(ret['bar_total_trend']); chart.hideLoading() }, ... }); }) </script>
|
我们的第一个交互图表就诞生了:
