Matplotlib无法在Django上渲染多个轮廓图
每当(至少)两个人尝试在我的应用程序中生成轮廓图时,其中至少一个会收到随机错误,具体取决于第一个人设法绘制的距离。(“未知元素o”,“
ContourSet必须为在当前的轴中”只是两种可能性)
以下是可能会产生错误的缩减测试,如果您尝试一次在2个或多个选项卡中加载此页面,则第一个将正确呈现,而第二个将产生错误。(我发现执行此操作的最简单方法是用鼠标中键几次单击chrome中的刷新页面按钮)
views.py
def home(request):
return render(request, 'home.html', {'chart': _test_chart()})
def _test_chart():
import base64
import cStringIO
import matplotlib
matplotlib.use('agg')
from matplotlib.mlab import bivariate_normal
import matplotlib.pyplot as plt
import numpy as np
from numpy.core.multiarray import arange
delta = 0.5
x = arange(-3.0, 4.001, delta)
y = arange(-4.0, 3.001, delta)
X, Y = np.meshgrid(x, y)
Z1 = bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = (Z1 - Z2) * 10
fig = plt.figure(figsize=(10, 5))
plt.contour(X, Y, Z, 10, colors='k')
jpg_image_buffer = cStringIO.StringIO()
fig.savefig(jpg_image_buffer)
array = base64.b64encode(jpg_image_buffer.getvalue())
jpg_image_buffer.close()
return array
home.html(仅这一行就足够了)
<img src="data:image/png;base64,{{ chart }}" />
我尝试使用mpld3代替它来处理图像的生成,但是这仍然会产生不同的错误,因此我知道它绝对不是保存图形,而是更多地生成图形。我也尝试过使用aThreadPool
和Threading
无济于事,据我所知,似乎在matplotlib中创建等高线图无法支持多个实例,这些实例将永远无法用于网站…
我现在能想到的唯一清晰的解决方案是,用其他我真的不想做的东西替换matplotlib。
有没有一种方法可以使用matplotlib生成对我有用的轮廓图?
-
首先,让我开始说,通过调用
_test_chart
几个线程可以更轻松地重现from threading import Thread for i in xrange(2): Thread(target=_test_chart).start()
执行上述操作后,一个将按预期工作,而第二个将崩溃。
这样做的简单原因是pyplot模块不是为多线程设计的,因此,两个图表在尝试绘制时会混淆它们的数据。
可以用mdboom更好地解释
…
pyplot用于在命令行方便地进行绘制,并保持全局状态。例如,当您说plt.figure()时,它会将图形添加到全局列表中,然后将“当前图形”指针设置为最近创建的图形。然后,随后的绘图命令将自动写入该图形。显然,这不是线程安全的…有两种方法可以解决此问题,
-
强制在不同的过程中绘制这些图表。
for i in xrange(2):
pool = Pool(processes=1)
pool.apply(_test_chart)
尽管这将起作用,但是您会发现性能显着下降,因为创建流程和生成图表所花的时间通常一样长(我认为这是不可接受的!)
-
真正的解决方案是使用Matplotlib的OO接口模块,该模块然后将允许您使用正确的对象-本质上,这适用于处理子图而不是图。对于问题中的给定示例,这类似于以下内容
def _test_chart2():
delta = 0.5 x = arange(-3.0, 4.001, delta) y = arange(-4.0, 3.001, delta) X, Y = np.meshgrid(x, y) Z1 = bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) Z2 = bivariate_normal(X, Y, 1.5, 0.5, 1, 1) Z = (Z1 - Z2) * 10 fig = figure(figsize=(10, 5)) ax1 = fig.add_subplot(111) extents = [x.min(), x.max(), y.min(), y.max()] im = ax1.imshow(Z, interpolation='spline36', extent=extents, origin='lower', aspect='auto', cmap=cm.jet) ax1.contour(X, Y, Z, 10, colors='k') jpg_image_buffer = cStringIO.StringIO() fig.savefig(jpg_image_buffer) array = base64.b64encode(jpg_image_buffer.getvalue()) jpg_image_buffer.close() return array
-