Matplotlib - 创建许多子图的快速方法?

Dan*_*ein 4 python matplotlib scipy

我正在使用matplotlib创建一个包含许多小子图(如4行,8列)的图形.我已经尝试了几种不同的方法,而且我能用matplotlib来制作轴的最快速度是~2秒.我看到这篇文章关于只使用一个轴对象来显示许多小图像,但我希望能够在轴上有刻度和标题.有没有办法加速这个过程,或者在matplotlib中制作轴只需要相当长的时间?

这是我到目前为止所尝试的:

import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import ImageGrid
import time

fig = plt.figure(figsize=(10,6))

plt.clf(); t = time.time()
grid = ImageGrid(fig, 111, 
                nrows_ncols = (4, 8))
print 'Time using ImageGrid: %.2f seconds'%(time.time()-t)

plt.clf(); t = time.time()
axes = plt.subplots(4,8)
print 'Time using plt.subplots: %.2f seconds'%(time.time()-t)

plt.clf(); t = time.time()
axes = []
for row in range(0,4):
    for col in range(1,9):
        ax = plt.subplot(4,8,row*8+col)
        axes.append(ax)
print 'Time using ptl.subplot loop: %.2f seconds'%(time.time()-t)
Run Code Online (Sandbox Code Playgroud)

输出:

Time using ImageGrid: 4.25 seconds
Time using plt.subplots: 2.11 seconds
Time using ptl.subplot loop: 2.34 seconds
Run Code Online (Sandbox Code Playgroud)

考虑到Joe Kington的建议,我试图挑选图形和轴,以便每次运行脚本时至少不需要创建它们.但是,加载文件实际上需要更长时间:

import matplotlib.pyplot as plt
import pickle
import time

t = time.time()
fig,axes = plt.subplots(4,8,figsize=(10,6))
print 'Time using plt.subplots: %.2f seconds'%(time.time()-t)

pickle.dump((fig,axes),open('fig.p','wb'))

t = time.time()
fig,axes = pickle.load(open('fig.p','rb'))

print 'Time to load pickled figure: %.2f seconds'%(time.time()-t)
Run Code Online (Sandbox Code Playgroud)

输出:

Time using plt.subplots: 2.01 seconds
Time to load pickled figure: 3.09 seconds
Run Code Online (Sandbox Code Playgroud)

Joe*_*ton 5

你不会subplots被任何大量的打败.创建新轴是一项相当昂贵的操作,每次创建32个.但它只做了一次.

创造一个新的数字和轴真的是你的瓶颈吗?如果是这样,你可能做错了什么.

如果您正在尝试创建动画,只需更新艺术家(例如image.set_data等)并重新绘制,而不是每次都创建一个新的图形和轴.

(另外,axes = plt.subplots(4, 8)不正确.axes将是一个元组,而不是你当前编写的一系列轴对象. subplots返回一个图形实例和一个轴数组.应该是fig, axes = plt.subplots(...).)


只是为了扩展我在下面关于酸洗的评论:

当您更改不相关的参数时,缓存"昂贵"的结果通常很方便.这在科学"脚本"中尤为常见,在这些"脚本"中,您通常会有一些相当慢的数据解析或中间计算以及一组依赖的参数,您可以半交互式"调整"这些参数.

仅仅注释几行并明确保存/加载pickle结果没有任何问题.

但是,我发现保持装饰器可以自动执行此操作.在许多情况下,有更好的方法可以做到这一点,但对于一个通用的解决方案,它非常方便:

import os
import cPickle as pickle
from functools import wraps

class Cached(object):
    def __init__(self, filename):
        self.filename = filename

    def __call__(self, func):
        @wraps(func)
        def new_func(*args, **kwargs):
            if not os.path.exists(self.filename):
                results = func(*args, **kwargs)
                with open(self.filename, 'w') as outfile:
                    pickle.dump(results, outfile, pickle.HIGHEST_PROTOCOL)
            else:
                with open(self.filename, 'r') as infile:
                    results = pickle.load(infile)
            return results
        return new_func
Run Code Online (Sandbox Code Playgroud)

然后你可以做一些事情(假设上面的类是在utilities.py):

import matplotlib.pyplot as plt
from utilities import Cached

@Cached('temp.pkl')
def setup():
    fig, axes = plt.subplots(8, 4)
    # And perhaps some other data parsing/whatever
    return fig, axes

fig, axes = setup()
for ax in axes.flat:
    ax.plot(range(10))
plt.show()
Run Code Online (Sandbox Code Playgroud)

只有在给定的filename("temp.pkl")不存在时才会运行修饰函数,temp.pkl否则将加载存储的缓存结果.这适用于任何可以腌制的东西,而不仅仅是matplotlib数字.

但要注意使用已保存的状态.如果你更改了什么setup并重新运行,将返回"旧"结果!如果更改包装函数的功能,请务必手动删除缓存的文件.

当然,如果你只是在一个地方做这件事,那就太过分了.如果您发现自己经常缓存中间结果,那么它可以很方便.