Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plotting with pint quantities #317

Closed
bendichter opened this issue Dec 22, 2015 · 5 comments
Closed

plotting with pint quantities #317

bendichter opened this issue Dec 22, 2015 · 5 comments

Comments

@bendichter
Copy link

I am interested in a combination between pint and matplotlib.

  • The axes would recognize the unit and format the ticks appropriately, including automatically dynamically changing from e.g. meters to kilometers if the range of values is large. It would be analogous to the way datetimes are currently handled.
  • The abv. unit would be formatted into the tick labels, or optionally would be added to the axis label (e.g. "height (m)")
  • Error bars would automatically be generated based on uncertainty
  • Probably more stuff I haven't thought of. Suggestions welcome.

I imagine this would take a lot of work with the matplotlib package, but I thought I'd ask here as there might be more interest here. Has this been proposed or attempted? Does anyone else think this would be really cool?

@bendichter
Copy link
Author

OK so there is some foundation over at matplotlib: https://matplotlib.org/api/units_api.html#module-matplotlib.units

Can anyone speak to whether pint is compatible or how hard it would be to make it compatible?

@hgrecco
Copy link
Owner

hgrecco commented Dec 22, 2015

We will be very happy to provide something like this. As far as I know, nobody has tried it but should be doable. We will be happy to help you out if you need it.
By the way, this #224 might help you to choose the right unit.

@dopplershift
Copy link
Contributor

I've been playing off and on with using pint with matplotlib--version 1.5 of matplotlib has some crucial fixes for using pint with the units support. Here's the code I've used to enable the registration, but I haven't played around enough to know if it's a solid, complete implementation:

import matplotlib.units as munits

# Implementation of matplotlib support for unit conversion using pint
class PintConverter(munits.ConversionInterface):
    @staticmethod
    def convert(value, unit, axis):
        return value.to(unit)

    @staticmethod
    def axisinfo(unit, axis):
        return None

    @staticmethod
    def default_units(x, axis):
        return x.to_base_units()


# Register the class
munits.registry[units.Quantity] = PintConverter()

@dopplershift
Copy link
Contributor

So it seems things work pretty well with matplotlib 2.0.2 (as well as its master branch). I've made this work pretty well so far:

import matplotlib.units as munits

class PintAxisInfo(munits.AxisInfo):
    """Support default axis and tick labeling and default limits."""

    def __init__(self, units):
        """Set the default label to the pretty-print of the unit."""
        super(PintAxisInfo, self).__init__(label='{:P}'.format(units))


class PintConverter(munits.ConversionInterface):
    """Implement support for pint within matplotlib's unit conversion framework."""

    @staticmethod
    def convert(value, unit, axis):
        """Convert pint :`Quantity` instances for matplotlib to use."""
        if isinstance(value, (tuple, list)):
            return [PintConverter._convert_value(v, unit, axis) for v in value]
        else:
            return PintConverter._convert_value(value, unit, axis)

    @staticmethod
    def _convert_value(value, unit, axis):
        """Handle converting using attached unit or falling back to axis units."""
        if hasattr(value, 'units'):
            return value.to(unit).magnitude
        else:
            return units.Quantity(value, axis.get_units()).to(unit).magnitude

    @staticmethod
    def axisinfo(unit, axis):
        """Return axis information for this particular unit."""
        return PintAxisInfo(unit)

    @staticmethod
    def default_units(x, axis):
        """Get the default unit to use for the given combination of unit and axis."""
        return getattr(x, 'units', None)


# Register the class
munits.registry[units.Quantity] = PintConverter()

@hgrecco Is there any interest in having this baked into pint itself? We could add a function for users to call that triggers the matplotlib import and registration of the unit handling for matplotlib.

@hgrecco
Copy link
Owner

hgrecco commented Aug 19, 2017

This would be awesome!

Please do a PR and we can discuss it there. I think we need
1.- As you say, have a function to import and register
2.- Maybe also check the matplotlib version
3.- Provide a way to deregister if possible
4.- Add some doc (with pictures!)

dopplershift added a commit to dopplershift/pint that referenced this issue Aug 31, 2017
These are optionally enabled on the UnitRegistry.
bors bot added a commit that referenced this issue Sep 2, 2017
549: Matplotlib support r=hgrecco a=dopplershift

This adds a method to `UnitRegistry` that hooks up handlers for `Quantity` instances within matplotlib's unit machinery:
```python
import pint
ureg = pint.UnitRegistry()
ureg.setup_matplotlib()
```
This support can also be disabled with:
```python
ureg.setup_matplotlib(False)
```

Closes #317.
@bors bors bot closed this as completed in #549 Sep 2, 2017
znicholls pushed a commit to znicholls/pint that referenced this issue Aug 29, 2018
These are optionally enabled on the UnitRegistry.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants