3

你可能已经知道,在 matplotlib 1.2.0 中有一个新的实验特性,即数字是可挑选的(它们可以用pickle模块保存)。

但是,当使用对数刻度时它不起作用,例如。

import matplotlib.pyplot as plt
import numpy as np
import pickle
ax = plt.subplot(111)
x = np.linspace(0, 10)
y = np.exp(x)
plt.plot(x, y)
ax.set_yscale('log')
pickle.dump(ax, file('myplot.pickle', 'w'))

结果是:

PicklingError: Can't pickle <class 'matplotlib.scale.Log10Transform'>: attribute lookup matplotlib.scale.Log10Transform failed

有人知道对此有任何解决方案/解决方法吗?

4

1 回答 1

4

我已将其作为关于 matplotlib 的github 问题跟踪器的错误报告打开。在 matplotlib 存储库端实现它是一个相当容易的修复(只是不要将Log10Transform类嵌套在LogScale类中),但这并不能真正帮助您在 mpl 1.2.0 中使用它......

有一个解决方案可以在 1.2.0 中为您工作,但我警告您 - 它并不漂亮!

根据我对腌制问题的回答,可以腌制嵌套类(按原样Log10Transform)。我们需要做的就是告诉Log10Transform如何“减少”本身:

import matplotlib.scale

class _NestedClassGetter(object):
    """
    When called with the containing class as the first argument, 
    the name of the nested class as the second argument,
    and the state of the object as the third argument,
    returns an instance of the nested class.

    """
    def __call__(self, containing_class, class_name, state):
        nested_class = getattr(containing_class, class_name)
        # return an instance of a nested_class. Some more intelligence could be
        # applied for class construction if necessary.
        c = nested_class.__new__(nested_class)
        c.__setstate__(state)
        return c

def _reduce(self):
    # return a class which can return this class when called with the 
    # appropriate tuple of arguments
    cls_name = matplotlib.scale.LogScale.Log10Transform.__name__
    call_args = (matplotlib.scale.LogScale, cls_name, self.__getstate__())
    return (_NestedClassGetter(), call_args)

matplotlib.scale.LogScale.Log10Transform.__reduce__ = _reduce 

您可能还决定对其他基于日志的转换/类执行此操作,但对于您的示例,您现在可以腌制(并成功取消腌制)您的示例图:

import matplotlib.pyplot as plt
import numpy as np
import pickle


ax = plt.subplot(111)
x = np.linspace(0, 10)
y = np.exp(x)
plt.plot(x, y)
ax.set_yscale('log')

pickle.dump(ax, file('myplot.pickle', 'w'))
plt.savefig('pickle_log.pre.png')
plt.close()

pickle.load(file('myplot.pickle', 'r'))
plt.savefig('pickle_log.post.png')

我将继续为 mpl 1.3.x 修复此问题,以便将来不需要这种讨厌的解决方法:-)。

高温下,

于 2012-11-28T11:14:04.510 回答