Skip to content

Try to get a units converter for a type before falling back to built-in behavior #20502

@ryan-gunderson

Description

@ryan-gunderson

When given an object to plot, should matplotlib attempt to use a registered units.ConversionInterface for that object's type (and mro) before falling back to other built-in defaults?

This question/suggestion prompted by the following example:

Say one has a class

class DateTimeNano(np.ndarray):
    ...

subclassing np.ndarray with the convention that DateTimeNano stores nano-seconds since epoch. When plotting a DateTimeNano, one may want to make use of the built in utilities for date tick location/formatting. If one writes a units.ConversionInterface like

class DateTimeNanoConverter(units.ConversionInterface):
    NANOS_PER_DAY = 86_400*1_000_000_000
    @staticmethod
    def convert(value, unit, axis):
        # Values should be in floating point days since epoch to play nice with mpl
        day_floats = value / DateTimeNanoConverter.NANOS_PER_DAY 
        return day_floats

    @staticmethod
    def axisinfo(unit, axis):
        majloc = dates.AutoDateLocator()
        majfmt = dates.AutoDateFormatter(majloc)
        return AxisInfo(majloc=majloc, majfmt=majfmt, label='date time')

    @staticmethod
    def default_units(x, axis):
            return 'date_time'

registers it with

units.registry[DateTimeNano] = DateTimeNanoConverter

and then calls

plt.plot(my_date_time_nano, my_y_values)

only DateTimeNanoConverter.default_units and DateTimeNanoConverter.axisinfo are used. DateTimeNanoConverter.convert is skipped entirely as units._is_natively_supported recognizes that it is an iterable and so a converter for the type DateTimeNano is never searched for (it also appears that this would be short circuited in units.Registry.get_converter).

If the step of searching for a converter by iterating over the type's mro were moved up in the order of trying to get values out of an object, this problem would not occur. It also seems like an overall preferred behavior as searching for a way to get an array of values out (using a converter or built-in code paths) should always start with the most specific implementation and work out.
This does not currently happen if types happen to have a particular inheritance structure or set of attributes.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions