From 47b7fc8d4a5443d1590131f5161ba64aba5a755d Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 1 Jun 2015 23:31:51 -0400 Subject: [PATCH 1/7] ENH/RFC: decorators to deal with data frames Draft of decorators to automatically map DataFrame columns to base objects for plotting. --- lib/matplotlib/cbook.py | 109 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 78e9aa5c41a3..e08253246977 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -12,6 +12,7 @@ import six from six.moves import xrange, zip from itertools import repeat +from functools import wraps import datetime import errno @@ -2406,6 +2407,114 @@ 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. + + 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 + expect an `Axes` as the first input and a `DataFrame`-like objects + 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 + + # 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 From 270144b01a9da4e691390ef2009df84a6e9ee23d Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 1 Jun 2015 23:37:13 -0400 Subject: [PATCH 2/7] ENH: add apply version of the decorators --- lib/matplotlib/cbook.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index e08253246977..01458bcaa9b8 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -2515,6 +2515,21 @@ def inner(ax, data, *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 + 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 From 6657e652353dc29a12ccf926f4a5cc744f4ed62d Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 7 Jun 2015 16:13:23 -0400 Subject: [PATCH 3/7] DOC: fix up indentation in docstrings --- lib/matplotlib/cbook.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 01458bcaa9b8..b7689a0608ae 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -2422,31 +2422,34 @@ def mappable_kwargs_decorator(func, map_targets): The function call :: - func(ax, x=data['foo'], y=data['bar']) + func(ax, x=data['foo'], y=data['bar']) is the same as :: - wrapped = mappable_kwargs_decorator(func, {'x': 'foo', 'y':'bar'}) - wrapped(ax, data) + 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']`` + ``kwargs['x'] == data['foo']`` and ``kwargs['y'] == data['bar']`` Returns ------- callable - Decorated function with the signature :: - func(ax, df, *args, **kwargs) + Decorated function with the signature :: + + func(ax, df, *args, **kwargs) """ From 08edd2246c083a3535ae9dccd7c30b4e8f922df9 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 7 Jun 2015 17:18:02 -0400 Subject: [PATCH 4/7] MNT: settle on using np.asarray on input Use `np.asarray` to ensure that the contents of the mapping are suitable to pass into mpl functions. This allows these functions to work with both DataFrames, with dict-likes of lists/arrays and (presumably) with xray objects. --- lib/matplotlib/cbook.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index b7689a0608ae..5575d4106d09 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -2460,7 +2460,7 @@ def inner(ax, data, *args, **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 + kwargs[k] = np.asarray(data[v]) return func(ax, *args, **kwargs) return inner @@ -2512,7 +2512,7 @@ def mappable_args_decorator(func, map_targets): """ @wraps(func) def inner(ax, data, *args, **kwargs): - args = tuple(data[k] for k in map_targets) + args + args = tuple(np.asarray(data[k]) for k in map_targets) + args return func(ax, *args, **kwargs) return inner @@ -2524,12 +2524,12 @@ def apply_kwargs_mapping(ax, func, data, map_targets, *args, **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 + kwargs[k] = np.asarary(data[v]) 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 + args = tuple(np.asarray(data[k]) for k in map_targets) + args return func(ax, *args, **kwargs) From 48d1cfa6f6c194bcc73b8b2a12e3b38a42ae97bc Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 7 Jun 2015 17:19:56 -0400 Subject: [PATCH 5/7] DOC: updates docstrings --- lib/matplotlib/cbook.py | 102 +++++++++++++++++++++++++++++++++++----- 1 file changed, 90 insertions(+), 12 deletions(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 5575d4106d09..656b36a11e87 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -2410,24 +2410,24 @@ def get_instancemethod(self): 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. + This is a decorator to ease the burden of unpacking + pandas data frames into 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 - expect an `Axes` as the first input and a `DataFrame`-like objects + The args and kwargs are then unpacked into the function, which will + expect an `Axes` as the first input and a `DataFrame`-like object as the second. The function call :: - func(ax, x=data['foo'], y=data['bar']) + func(ax, x=data['foo'], y=data['bar'], *args, **kwargs) is the same as :: wrapped = mappable_kwargs_decorator(func, {'x': 'foo', 'y':'bar'}) - wrapped(ax, data) + wrapped(ax, data, *args, **kwargs) Parameters ---------- @@ -2469,25 +2469,25 @@ def inner(ax, data, *args, **kwargs): 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. + This is a decorator to ease the burden of unpacking + pandas data frames into numpy arrays. 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 + The args and kwargs are then unpacked into the function, which will + expect an `Axes` as the first input and a `DataFrame`-like object as the second. The function call :: - func(ax, data['foo'], data['bar']) + func(ax, data['foo'], data['bar'], *args, **kwargs)) is the same as :: wrapped = mappable_args_decorator(func, ('foo', 'bar')) - wrapped(ax, data) + wrapped(ax, data, *args, **kwargs) Parameters @@ -2519,6 +2519,48 @@ def inner(ax, data, *args, **kwargs): def apply_kwargs_mapping(ax, func, data, map_targets, *args, **kwargs): + """Decompose labeled data and pass to function + + This is a convince to ease the burden of unpacking + pandas data frames into numpy arrays. + + It calls the function with the specified columns are re-mapped + into the kwargs. + + + The function call :: + func(ax, x=data['foo'], y=data['bar'], *args, **kwargs) + + + is the same as :: + + apply_kwargs_mapping(ax, func, data, {'x': 'foo', 'y':'bar'}, + *args, **kwargs) + + + Parameters + ---------- + ax : Axes + The axes to plot to + + function : callable + A function which expects an `Axes` objects as the first argument + and can take data as kwargs. + + data : pandas.DataFrame or dict-like + Labeled data. It is assumed that data[key] will return something + that `np.asarray` can convert to a numpy array + + + 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']`` + + + + """ for k, v in map_targets.items(): if k in kwargs: raise ValueError(("Trying to map a column ({1} -> {0}) " @@ -2529,6 +2571,42 @@ def apply_kwargs_mapping(ax, func, data, map_targets, *args, **kwargs): def apply_args_mapping(ax, func, data, map_targets, *args, **kwargs): + """Decompose labeled data and pass to function + + This is a convince to ease the burden of unpacking + pandas data frames into numpy arrays. + + It calls the function with the specified columns are re-mapped + to precede the args. + + + The function call :: + + func(ax, data['foo'], data['bar'], *args, **kwargs)) + + is the same as :: + + apply_args_mapping(ax, func, data, ('foo', 'bar'), *args, **kwargs) + + + Parameters + ---------- + ax : Axes + The axes to plot to + + function : callable + A function which expects an `Axes` objects as the first argument + and can take data as kwargs. + + data : pandas.DataFrame or dict-like + Labeled data. It is assumed that data[key] will return something + that `np.asarray` can convert to a numpy array + + map_targets : tuple + Order of columns to extract to pass to the function. These column + are the 2nd -> n+1 args + + """ args = tuple(np.asarray(data[k]) for k in map_targets) + args return func(ax, *args, **kwargs) From bbd735693f380e772b7dc7ee9f7e99b6467c5f00 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 7 Jun 2015 17:23:11 -0400 Subject: [PATCH 6/7] MNT: use `asannayarray` instead of `asarray` --- lib/matplotlib/cbook.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 656b36a11e87..f99221ffe899 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -2460,7 +2460,7 @@ def inner(ax, data, *args, **kwargs): raise ValueError(("Trying to map a column ({1} -> {0}) " "on to a passed in " "keyword arg ({0})").format(k, v)) - kwargs[k] = np.asarray(data[v]) + kwargs[k] = np.asanyarray(data[v]) return func(ax, *args, **kwargs) return inner @@ -2512,7 +2512,7 @@ def mappable_args_decorator(func, map_targets): """ @wraps(func) def inner(ax, data, *args, **kwargs): - args = tuple(np.asarray(data[k]) for k in map_targets) + args + args = tuple(np.asanyarray(data[k]) for k in map_targets) + args return func(ax, *args, **kwargs) return inner @@ -2549,7 +2549,7 @@ def apply_kwargs_mapping(ax, func, data, map_targets, *args, **kwargs): data : pandas.DataFrame or dict-like Labeled data. It is assumed that data[key] will return something - that `np.asarray` can convert to a numpy array + that `np.asanyarray` can convert to a numpy array map_targets : dict @@ -2566,7 +2566,7 @@ def apply_kwargs_mapping(ax, func, data, map_targets, *args, **kwargs): raise ValueError(("Trying to map a column ({1} -> {0}) " "on to a passed in " "keyword arg ({0})").format(k, v)) - kwargs[k] = np.asarary(data[v]) + kwargs[k] = np.asanyarary(data[v]) return func(ax, *args, **kwargs) @@ -2600,14 +2600,14 @@ def apply_args_mapping(ax, func, data, map_targets, *args, **kwargs): data : pandas.DataFrame or dict-like Labeled data. It is assumed that data[key] will return something - that `np.asarray` can convert to a numpy array + that `np.asanyarray` can convert to a numpy array map_targets : tuple Order of columns to extract to pass to the function. These column are the 2nd -> n+1 args """ - args = tuple(np.asarray(data[k]) for k in map_targets) + args + args = tuple(np.asanyarray(data[k]) for k in map_targets) + args return func(ax, *args, **kwargs) From 1e5ae2e3c798509520ef76b72fe1bf67797fccf0 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 7 Jun 2015 17:52:46 -0400 Subject: [PATCH 7/7] DOC: make sphinx happy --- lib/matplotlib/cbook.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index f99221ffe899..45f9ef8dc5f4 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -2529,13 +2529,14 @@ def apply_kwargs_mapping(ax, func, data, map_targets, *args, **kwargs): The function call :: + func(ax, x=data['foo'], y=data['bar'], *args, **kwargs) is the same as :: - apply_kwargs_mapping(ax, func, data, {'x': 'foo', 'y':'bar'}, - *args, **kwargs) + mapping = {'x': 'foo', 'y':'bar'} + apply_kwargs_mapping(ax, func, data, mapping, *args, **kwargs) Parameters