Matplotlib无法在Django上渲染多个轮廓图 [英] Matplotlib can't render multiple contour plots on Django

查看:406
本文介绍了Matplotlib无法在Django上渲染多个轮廓图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

无论何时(至少)2个人尝试在我的应用程序中生成一个轮廓图,至少其中一个将根据第一个人设计绘制的距离来收到一个随机错误。(未知元素o, ContourSet必须在当前Axes中只是其中的两种可能性)



以下是可以产生错误的剪切测试,如果您尝试加载此页面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')
从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_i mage_buffer = cStringIO.StringIO()
fig.savefig(jpg_image_buffer)

array = base64.b64encode(jpg_image_buffer.getvalue())
jpg_image_buffer.close()
返回数组

home.html(只有这一行就够了)

 < img src =data:image / png; base64,{{chart}}/> 

我已经尝试使用mpld3来处理图像的生成,这仍然产生不同的错误,所以我知道它绝对不是保存数字,而是更多的代数。我也尝试使用一个 ThreadPool 线程无效,从我可以看出,似乎创建一个matplotlib中的轮廓图不能支持多个不能为网站工作的实例...



我现在可以想到的唯一清晰的解决方案是用其他东西替换matplotlib我真的不想这样做。



有没有办法用matplotlib生成轮廓图,可以为我工作?

解决方案

首先,让我开始说一下,通过调用 _test_chart 可以更容易地再现线程

 从线程导入线程
在xrange(2)中:
线程(target = _test_chart ).start()

执行上述操作,一个可以按需工作,而第二个将会崩溃。






简单的原因是pyplot模块不是为多线程,因此这两个图表在他们试图绘制时会收到他们的数据。



这可以通过 mdboom


... pyplot用于在命令行中方便绘图,并保持全局状态。例如,当你说plt.figure()时,它将图形添加到一个全局列表中,然后将当前图形指针设置为最近创建的图形。然后随后的绘图命令自动写入该图。显然,这不是线程安全的...


有两种方法来解决这个问题,


  1. 强制这些图表在不同的流程中绘制。





$($)

$ b $ / $ c $ $ c>

虽然这将工作,你会发现有一个明显的性能下降,因为它通常需要一样长的时间来创建过程,因为它将生成图表(我没有想到可以接受!)


  1. 真正的解决方案是使用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.b6 4encode(jpg_image_buffer.getvalue())
jpg_image_buffer.close()

返回数组


Whenever (at least) 2 people try to generate a contour plot in my application, at least one of them will receive a random error depending on how far the first person managed to draw.. ("unknown element o", "ContourSet must be in current Axes" are just two of the possibilities)

The following is a cut down test that can produce the error, if you try to load this page in 2 or more tabs at once, the first will render correctly whilst the second will produce an error. (Easiest way I found to do this was to click the refresh page button in chrome with the middle mouse button a couple times)

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 (just this one line is enough)

<img src="data:image/png;base64,{{ chart }}" />

I've tried using mpld3 instead to handle the generation of the image and this still produces different errors so I know its definitely not the saving of the figure but more its generation. I've also tried using a ThreadPool and Threading to no avail, from what I can tell it seems like creating a contour plot in matplotlib cannot support multiple instances which will never work for a website...

My only clear solution I can think of right now is to replace matplotlib with something else which I really don't want to do.

Is there a way to generate contour plots with matplotlib that will work for me?

解决方案

First, let me start by saying that this is much more easy to reproduce by calling _test_chart in a couple threads

from threading import Thread
for i in xrange(2):
    Thread(target=_test_chart).start()

Doing the above, one will work as desired whilst the second one will crash.


The simple reason for this is that the pyplot module is not designed for multithreading and therefore the two charts are getting their data mixed up as they attempt to draw.

This can be better explained by mdboom

...pyplot is used for convenient plotting at the commandline and keeps around global state. For example, when you say plt.figure() it adds the figure to a global list and then sets the "current figure" pointer to the most recently created figure. Then subsequent plotting commands automatically write to that figure. Obviously, that's not threadsafe...

There are two ways to fix this issue,

  1. Force these charts to be drawn in different processes.

for i in xrange(2):
    pool = Pool(processes=1)
    pool.apply(_test_chart)   

Whilst this will work you will find that there is a noticable drop in performance since it will often take just as long to create the process as it will to generate the chart (which I didn't think was acceptable!)

  1. The real solution is to use the OO interface modules of Matplotlib which will then allow you to work with the correct objects - essentially this works down to working with subplots rather than plots. For the given example in the question, this would look like the following

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

这篇关于Matplotlib无法在Django上渲染多个轮廓图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆