Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Treat units info as nu, make nu a list rather than single function #17

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

Closed
wants to merge 2 commits into from

Conversation

ksunden
Copy link
Member

@ksunden ksunden commented Nov 4, 2022

This is mostly a proof of concept/concrete implementation that can be discussed for the idea of allowing nu functions to actually be lists of functions applied successively.

As part of this, the treatment of units has been moved to be in the list of nu functions, which allows transformations to happen either pre- or post- unit conversion, based on the order in the list.

@tacaswell has stated "nu is a property of the artist, the unit convesion is a property of the host Axes", indicating that perhaps this is not the "correct" way to to treat unit conversions.

I do wish to challenge this thought at least a little bit: the actual call to convert units has a signature that is compatible with nu (although admittedly it is a bound method rather than a pure function).
I further wonder if the key to resolving unit inconsistencies is to handle the units separate from at least some of the existing units machinery and coax the existing machinery to work with the new system rather than the other way around. (Note, that is not to say I want users to have to change their code, but rather just that we don't tie ourselves down too early.)

Now... this implementation was pretty hastily put together and none of our currently implemented examples actually use units yet, so it's not even particularly well tested.
Additionally, the implementation conflicts with changes to nu proposed in #15.

@story645
Copy link
Member

story645 commented Nov 6, 2022

"nu is a property of the artist, the unit convesion is a property of the host Axes

I'm interpreting that as each artist might need to do separate dunitization depending on what data is being passed in, but that all the conversions have to be consistent such that for multiple artists on one axes:

  1. two artists taking the same unitized data set (for example categoricals) can't encode the values that data set in two different ways (so artists should have the same nu, both artists need to map apple to 0)
  2. the inches and centimeter example works where the input is two different types (and therefore different nus on each artist) but the return unit type is the same

xref: I think @jklymak was trying to do some of this w/ matplotlib/matplotlib#9776

ETA: also this consistency probably needs to be true at the figure level - like apple shouldn't be mapped to 0 in one axes and 1 in another 'cause for example that could lead to different positions in small multiples.

I further wonder if the key to resolving unit inconsistencies is to handle the units separate from at least some of the existing units machinery and coax the existing machinery to work with the new system rather than the other way around

Um for what it's worth, I kinda thought this was the plan.

@tacaswell
Copy link
Member

The reason that unit conversion belongs the to the Axis (in the Axes) is that there needs to be the matching transform on the tick labels (in the current system we have inverse functions and store the axis limits post-conversion so that the transform stack all "just works", but I think it should be possible to make the whole thing work with only forward functions, but that is a side point).

If we are going to put a tick on the axes and label it '2 in' (or 'apple' or a datetime), then the data at that level should be what the tick says. If we mix the unit conversion up in a list of functions (I think the list of functions is a good idea in general) than it becomes much harder to reach in and make sure it is consistent with all the other places that should have the same unit -> unitless conversion. There are a bunch of issues due to people who make plots with Pandas who install their version of the unitfull -> unitless converters and then people try to use our datetime formatters which expect a different unitless -> unitful conversion (if I recall correctly, in some cases pandas treats time ranges as ~ catagoricals, maps to integers, and then we interpret that as fractional days past an epoch (do not remember when day 0 is, it changed recently) which is in general very wrong).

Another interesting idea to play with is to push on how little the Airtst need to know about their parents. Currently we go through a bunch of work to make sure that the same Artist does not get put in more than one Axes (or Figure) because we store a ref to transData on the instances and you get really weird renderings if you try it (there are closed issues, check the history of why Arist.axes is a property). I think that units and transData are the only two critical pieces of information that the Artist needs and they in principle can be passed in at draw time.

I further wonder if the key to resolving unit inconsistencies is to handle the units separate from at least some of the existing units machinery and coax the existing machinery to work with the new system rather than the other way around.

Part of your job is to sort out the best path on this ;)

self._cache[cache_key] = data
return data

def __init__(self, data, nus, **kwargs):
def __init__(self, data, nus, xunits: List[str] = [], yunits: List[str] = [], **kwargs):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def __init__(self, data, nus, xunits: List[str] = [], yunits: List[str] = [], **kwargs):
def __init__(self, data, nus, xunits: Tuple[str] = (,), yunits: Tuple[str] = (,), **kwargs):

@tacaswell
Copy link
Member

My logic for sticking taping 'query' and 'transform' together is so that the caching logic could be implemented in one place [1]. Maybe the right way to change the signature is to

def _query_and_transform(self, renderer, *, pre_nu, unit_nu, post_nu, invalidate_cache=False):
     ...

(modulo defaults to make them optional) or maybe

def _query_and_transform(self, renderer, *nus, invalidate_cache=False):
     ...

where we can take as many as we want and up to the artist sub-class to order them correctly. I can also see a case for

def _query_and_transform(self, renderer, nus: Dict[str, List], *, invalidate_cache=False):
    ...

Thus we can stuff the linearization / lookup logic still all in one place and we have an outside control that the Artist knows the cache should be invalidated (because one of the three nu's changed).

[1] There are 2 hard problems in computer science:

  1. naming things
  2. cache invalidation
  3. off-by-one bugs

@ksunden
Copy link
Member Author

ksunden commented Feb 9, 2023

OKay, rebased... I think I got all of the functionality of #15 and this PR...

All of the examples seem to work, though I would like to make one with more unit behavior.

I also think some solution for ordering the units conversion within the nus is needed (currently it is always the last step).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants