Как заставить matplotlib обрабатывать пользовательские классы "юнитов" - PullRequest
0 голосов
/ 05 мая 2019

Я пытаюсь сделать мой класс совместимым с единицами matplotlib и столкнулся с неожиданным поведением.

Вот упрощенная версия моего пользовательского класса, которая не является подклассом nndy's 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 (если нет, я получаю исключение TypeError: float() argument must be a string or a number, not 'Toto', когда numpy пытается привести Toto к array(toto_instance, float)).Я подозреваю, что моя проблема на самом деле происходит от этого метода, но я не могу понять, почему / как.В любом случае, перейдем к актуальной проблеме:

Теперь я последовал примеру в документе , чтобы создать интерфейс преобразования для моего класса 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 преобразуются в ndarray, но я не уверен,

Приветствия

1 Ответ

0 голосов
/ 05 мая 2019

Я подозреваю, что вы имеете в виду нечто подобное, когда у вас есть список / массив Toto с, а не 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()
...