Django + Matplotlib:实现数据分析显示与下载为PDF或SVG
写作背景
首先,数据分析在当前的信息时代中扮演着重要的角色。随着数据量的增加和复杂性的提高,人们对于数据分析的需求也越来越高。
其次,笔者也确确实实曾经接到过一个这样的开发需求,甲方是一个医疗方面的科研团队,有相当大的一些关于癌症治疗方面的医疗数据,通过这些数据可以分析出很多东西,最重要的是将数据通过一些科学计算后转换为可视化的数据图,并且要根据用户的检索数据实时分析展示,并且可保存为pdf或svg格式的源文件,可提供后期印刷一类的二次修改使用;最终就选择了Django和Matplotlib这两个强大的库来实现数据分析的显示和下载功能。
本文将只会浅显的探讨如何使用Django和Matplotlib,并不会对数据分析方面的知识做过多的介绍!
环境搭建
这里我假设你已经会使用django,并且已经使用django搭建起了一个可运行的demo app。
Matplotlib的安装使用和其他的Python第三方包一致,只需要通过pip命令即可快速安装!
pip install matplotlib
代码示例
1、假如我们拥有一个如下代码所示的django 类视图
# demo/views.py
from django.shortcuts import render
from django.views.generic import View
class HomeView(View):
"""
HomeView类是一个继承自View的视图类,用于处理与主页相关的请求。
Attributes:
template_name (str): 展示主页所使用的模板名称。
"""
template_name = "demo/home.html"
def get(self, request, *args, **kwargs):
return render(request, self.template_name)
2、url如下所示
# 项目mysite的根urls.py
from django.urls import path
from. import views
urlpatterns = [
path('', views.HomeView.as_view(), name='index'),
]
3、demo/templates/demo目录下home.html代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
</html>
这只是一个空白的html页面,最后用来显示分析图!
那么,根据以上代码,我们可以通过django的runserver命令运行项目后看到该页面!
Matplotlib使用
1、在demo/views.py当中引入如下包
import base64
from io import BytesIO
from matplotlib.figure import Figure
from django.http import FileResponse
代码解释:
import base64 是我们导入了Python的base64模块,该模块提供了对 Base64 编码格式的支持。Base64 是一种用于编码二进制数据的方式,它将每3个字节的数据编码为4个字符,因此会将数据的大小增加约33%。这种编码格式常用于在文本中进行二进制数据的表示,尤其是在需要将二进制数据通过电子邮件等文本通道进行传输的情况下。通过导入 base64 模块,我们可以使用该模块提供的函数和类来进行 Base64 编码和解码操作。
from io import BytesIO 这一行代码是在 Python 中导入 BytesIO 类。BytesIO 类是 Python 标准库 io 模块中的一个类,用于在内存中处理二进制数据流。
BytesIO 类实现了一个缓冲区,这个缓冲区可以像文件一样进行读写操作,但实际上是在内存中进行的。因此,它可以在内存中处理和操作二进制数据,而不需要将数据写入磁盘。
通过使用 BytesIO 类,我们可以将二进制数据存储在内存中,并像处理文件一样对其进行读取、写入和其他操作。这对于处理大量数据或在内存中执行文件操作更加高效和灵活。
from matplotlib.figure import Figure 是导入matplotlib 库中的 Figure 类,Figure 类是 matplotlib 库中的一个核心类,用于创建一个图形窗口(figure window)。这个窗口可以包含各种图形元素,如坐标轴(axes)、曲线(lines)、文本(text)等。
通过导入 Figure 类,我们可以创建一个空的图形窗口,并在其中添加各种图形元素。这对于自定义图形的外观和布局非常有用。
from django.http import FileResponse是导入django为我们提供的一个响应对象,关于其作用django的官网对其有详细说明,如下所示:
2、实现在页面中显示分析图的需求
首先,我们在HomeView类中新增一个get_fig的方法,让其生成一个分析图对象:
class HomeView(View):
...
def get_fig(self):
"""
获取一个图形和轴对象
返回:
fig (matplotlib.figure.Figure): 图形对象
ax (matplotlib.axes.Axes): 轴对象
"""
fig = Figure(figsize=(5, 5)) # 创建一个大小为 5x5 的图形对象
ax = fig.subplots() # 在图形对象上创建一个子图对象
ax.plot([1, 2, 3, 4]) # 在轴对象上绘制一条线
# 标题
fig.suptitle("Hello world") # 设置图形的标题
# 坐标轴
ax.set_ylabel("Y zhou", fontdict={"size": 16, "color": "red", "weight": "bold", "family": "serif"}) # 设置轴对象的 y 轴标签
ax.set_xlabel("X zhou") # 设置轴对象的 x 轴标签
return fig, ax # 返回图形对象和轴对象
有了该图形对象之后,我们就可以用base64包将该图形对象在内存中转换为png格式的字节流,为此我们再在该视图类下新增一个img_base64_to_file的方法,让它接收一个图形对象fig作为参数!
class HomeView(View):
...
def img_base64_to_file(self, fig):
"""
将图像保存为base64编码的文件
参数:
fig:要保存的图像对象
返回值:
img:包含base64编码图像的HTML标签
"""
buf = BytesIO() # 创建字节流用于保存图像
fig.savefig(buf, format="png") # 将图像保存为png格式的字节流
data = base64.b64encode(buf.getbuffer()).decode("ascii") # 将字节流转换为base64编码的字符串
img = f"<img src='data:image/png;base64,{data}'/>" # 构建包含base64编码图像的HTML标签
return img # 返回HTML标签
做完以上两步之后,其实只需要将img_base64_to_file方法的返回值传递到get请求的上下文当中,在django的模版中通过模版过滤器safe渲染即可!其他动态筛选逻辑以及其他操作即可通过django的相关功能去实现!代码如下:
class HomeView(View):
...
def get(self, request, *args, **kwargs):
fig, ax = self.get_fig()
img = self.img_base64_to_file(fig)
return render(request, self.template_name, {"img": img})
静态html模版中如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
{{ img|safe }}
</body>
</html>
最终渲染图
3、实现将分析图保存为pdf或svg格式
要实现该需求其实也非常简单,只需要在该视图类中新增一个post的请求,使用django的FileResponse将数据流保存到本地即可,其中需要关注的只是fig图形对象中的savefig方法的format参数的变化,也就是保存文件的格式,这个函数本身就是支持导出pdf和svg格式的!代码如下:
class HomeView(View):
...
def download_response(self, fig, filename:str):
"""
将给定的图形对象 fig 保存为 pdf 格式的文件并下载。
参数:
self (object): 类实例对象
fig (matplotlib.figure.Figure): 绘制好的图形对象
filename (str): 保存的文件名,包含扩展名
返回值:
FileResponse: 用于文件下载的响应对象
"""
buf = BytesIO()
fig.savefig(buf, format="pdf")
buf.seek(0)
return FileResponse(
buf,
as_attachment=True,
filename=f"{filename}.pdf"
)
def post(self, request, *args, **kwargs):
fig,ax = self.get_fig()
return self.download_response(fig, "demo")
在html模版中通过post的表单请求即可下载,代码如下:
<body>
{{ img|safe }}
<form action="" method="post">
{% csrf_token %}
submit: <input type="submit" value="下载">
</form>
</body>
最终,通过点击下载按钮,发起一个post请求就实现了下载到本地的功能,当然你想动态控制下载格式的话,还可以通过django的表单系统开放给用户,让用户自由选择所需要的下载格式,需要注意的是format参数要和文件后缀格式保持一致!
作者开源商城项目,欢迎大家fork学习,同步开发教程持续更新
开源地址:https://gitee.com/xingfugz/bayke-shop