【问题标题】:How to make matplotlib handle custom class "units"如何让 matplotlib 处理自定义类“单元”
【发布时间】:2019-05-05 18:42:03
【问题描述】:

我正在尝试使我的课程与matplotlib's units 兼容并面临意外行为。

这是我的自定义类的简化版本,它不是 numpy 的 ndarray 的子类:

import numpy as np
import matplotlib
import matplotlib.units as units

class Toto:
    def __init__(self, value_like, unit_like):
        self.value_like = value_like # typically a scalar or array
        self.unit_like = unit_like # a string describing the unit

    def __array__(self, *args, **kwargs):
        return np.array(self.value_like, *args, **kwargs)

# To test if plot as expected, without units handling
arr_x = Toto(np.arange(5), "meter")
arr_y = Toto(np.arange(5), "second")
plt.plot(arr_x, arr_y)

请注意,我添加了一个 __array__ 方法,以便使用 matplotlib 使其“可绘制”(如果没有,当 numpy 尝试使用 array(toto_instance, float) 投射 Toto 时,我得到一个 TypeError: float() argument must be a string or a number, not 'Toto' 异常)。我怀疑我的问题实际上来自这种方法,但我不知道为什么/如何。无论如何,继续解决实际问题:

现在我按照example in the doc 为我的 Toto 课程制作了一个转换接口:

class TotoConverter(units.ConversionInterface):

    @staticmethod
    def convert(value, unit, axis):
        'Convert a toto object value to a scalar or array'
        old_toto_unit = axis.get_unit()
        # stupid computation to determine new_unit (simpler for a MWE)
        new_unit = old_toto_unit
        new_toto = Toto(value, new_unit)
        return new_toto.value_like

    @staticmethod
    def axisinfo(unit, axis):
        return units.AxisInfo(label=str(unit))

    @staticmethod
    def default_units(x, axis):
        'Return the default unit for x or None'
        return getattr(x, 'unit_like', None)

最后,我将我的类的转换接口添加到matplotlib的转换接口注册表中:

units.registry[Toto] = TotoConverter()

那么问题来了: 此时,我应该在绘制 Toto 实例时获得标签上的单位,但我得到的结果与定义和注册我的单位转换接口之前相同。这是为什么 ?

我怀疑转换对象从未被调用,因为我的 Toto 实例被转换为 ndarray 但我不确定

干杯

【问题讨论】:

    标签: python numpy matplotlib multidimensional-array


    【解决方案1】:

    我怀疑你的意思是这样的,你有一个 Totos 的列表/数组,而不是一个 Toto 的值。

    import matplotlib.pyplot as plt
    import matplotlib.units as units
    
    class Toto:
        def __init__(self, value_like, unit_like):
            self.value_like = value_like # typically a scalar or array
            self.unit_like = unit_like # a string describing the unit
    
    class TotoConverter(units.ConversionInterface):
    
        @staticmethod
        def convert(value, unit, axis):
            if isinstance(value, Toto):
                return value.value_like
            else:
                return [toto.value_like for toto in value]
    
        @staticmethod
        def axisinfo(unit, axis):
            return units.AxisInfo(label=str(unit))
    
        @staticmethod
        def default_units(x, axis):
            'Return the default unit for x or None'
            if isinstance(x, Toto):
                return getattr(x, 'unit_like', None)
            else:
                return getattr(x[0], 'unit_like', None)
    

    那就注册使用吧,

    units.registry[Toto] = TotoConverter()
    
    
    arr_x = [Toto(i, "meter") for i in range(5)]
    arr_y = [Toto(i, "second") for i in range(5)]
    
    plt.plot(arr_x, arr_y)            #use lists of Totos
    plt.axhline(Toto(2, "second"))    # use Toto scalars
    plt.xlim(Toto(-1, "meter"), None) # use Toto scalars
    
    plt.show()
    

    【讨论】:

    • 不,我的意思是绘制 2 个 Toto 对象,其值是数组,就像在定义接口之前的示例中一样:arr_x = Toto(np.arange(5), "meter") arr_y = Toto(np.arange(5), "second") plt.plot(arr_x, arr_y) 但我希望将它们的单位添加到标签中。跨度>
    • 那会很复杂。请参阅matplotlib.org/gallery/units/basic_units.html 了解它。
    • 我只是想了解如何绘制一个类(需要可转换为数组)以及如何被单元接口捕获(需要为 Toto 类型)
    猜你喜欢
    • 1970-01-01
    • 2011-12-15
    • 1970-01-01
    • 2015-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多