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

Enh mappable remapper #4490

Closed
wants to merge 7 commits into from
Closed
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
127 changes: 127 additions & 0 deletions lib/matplotlib/cbook.py
Expand Up @@ -12,6 +12,7 @@
import six
from six.moves import xrange, zip
from itertools import repeat
from functools import wraps

import datetime
import errno
Expand Down Expand Up @@ -2406,6 +2407,132 @@ def get_instancemethod(self):
return getattr(self.parent_obj, self.instancemethod_name)


def mappable_kwargs_decorator(func, map_targets):
"""Decompose labeled data for passing to base functions.

This is a decorator to easy the burden of unpacking
pandas data frames into base objects.
Copy link
Member

Choose a reason for hiding this comment

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

"base objects" -> "numpy arrays"?


In the returned function args and kwargs are passed through and
the specified columns are re-mapped to keys in the kwargs.

The args and kwargs are then unpacked into the function (which will
Copy link
Member

Choose a reason for hiding this comment

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

" (which" -> ", which "

expect an `Axes` as the first input and a `DataFrame`-like objects
Copy link
Member

Choose a reason for hiding this comment

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

-> "object" (singular)

as the second.

The function call ::

func(ax, x=data['foo'], y=data['bar'])

is the same as ::

wrapped = mappable_kwargs_decorator(func, {'x': 'foo', 'y':'bar'})
wrapped(ax, data)

Parameters
----------
function : callable

A function which expects an `Axes` objects as the first argument
and can take data as kwargs.

map_targets : dict

Mapping between key expected by the function and the column name.
ex ``{'x': 'foo', 'y': 'bar'}`` ->
``kwargs['x'] == data['foo']`` and ``kwargs['y'] == data['bar']``


Returns
-------
callable

Decorated function with the signature ::

func(ax, df, *args, **kwargs)


"""
@wraps(func)
def inner(ax, data, *args, **kwargs):
for k, v in map_targets.items():
if k in kwargs:
raise ValueError(("Trying to map a column ({1} -> {0}) "
"on to a passed in "
"keyword arg ({0})").format(k, v))
kwargs[k] = data[v].values
return func(ax, *args, **kwargs)

return inner


def mappable_args_decorator(func, map_targets):
"""Decompose labeled data for passing to base functions.

This is a decorator to easy the burden of unpacking
pandas data frames into base objects.

In the returned function args and kwargs are passed through and
the specified columns are re-mapped to precede the args.

The args and kwargs are then unpacked into the function (which will
expect an `Axes` as the first input and a `DataFrame`-like objects
as the second.


The function call ::

func(ax, data['foo'], data['bar'])

is the same as ::

wrapped = mappable_args_decorator(func, ('foo', 'bar'))
wrapped(ax, data)


Parameters
----------
function : callable
A function which expects an `Axes` objects as the first argument
and can take data as kwargs.

map_targets : tuple
Order of columns to extract to pass to the function. These column
are the 2nd -> n+1 args


Returns
-------
callable
Decorated function with the signature ::

func(ax, df, *args, **kwargs)


"""
@wraps(func)
def inner(ax, data, *args, **kwargs):
args = tuple(data[k] for k in map_targets) + args
return func(ax, *args, **kwargs)

return inner


def apply_kwargs_mapping(ax, func, data, map_targets, *args, **kwargs):
for k, v in map_targets.items():
if k in kwargs:
raise ValueError(("Trying to map a column ({1} -> {0}) "
"on to a passed in "
"keyword arg ({0})").format(k, v))
kwargs[k] = data[v].values
return func(ax, *args, **kwargs)


def apply_args_mapping(ax, func, data, map_targets, *args, **kwargs):
args = tuple(data[k] for k in map_targets) + args
Copy link
Member

Choose a reason for hiding this comment

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

Why are you using the values attribute for the kwargs cases but not for the args cases?

Copy link
Member Author

Choose a reason for hiding this comment

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

Oversight, but I am not fully sure which way is better. .values gets us a numpy array full-stop, but just [] gets use a Series which more-or-less behave like a numpy array.

Copy link
Member Author

Choose a reason for hiding this comment

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

Not using .values also means that these decorators will work with dicts of numpy arrays. It might be better to use np.asarray.

Copy link
Member

Choose a reason for hiding this comment

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

Omitting .values also means it will work with a numpy structured array; but I'm not sure if there would be any point in this.
Maybe use np.asanyarray in case something might yield a masked array?
I presume with a DataFrame, missing data will end up as NaN after conversion, correct?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it is always NaN internally in pandas and they don't do masking/sparse at all

return func(ax, *args, **kwargs)


# Numpy > 1.6.x deprecates putmask in favor of the new copyto.
# So long as we support versions 1.6.x and less, we need the
# following local version of putmask. We choose to make a
Expand Down