Skip to content

Commit

Permalink
addressing documentation comments + more use of units
Browse files Browse the repository at this point in the history
  • Loading branch information
story645 committed Feb 11, 2018
1 parent 543d235 commit 4d57690
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 68 deletions.
2 changes: 1 addition & 1 deletion .appveyor.yml
Expand Up @@ -66,7 +66,7 @@ install:
- activate test-environment
- echo %PYTHON_VERSION% %TARGET_ARCH%
# pytest-cov>=2.3.1 due to https://github.com/pytest-dev/pytest-cov/issues/124
- pip install -q "pytest!=3.3.0" "pytest-cov>=2.3.1" pytest-rerunfailures pytest-timeout pytest-xdist
- pip install -q "pytest!=3.3.0,>=3.2.0" "pytest-cov>=2.3.1" pytest-rerunfailures pytest-timeout pytest-xdist

# Apply patch to `subprocess` on Python versions > 2 and < 3.6.3
# https://github.com/matplotlib/matplotlib/issues/9176
Expand Down
2 changes: 1 addition & 1 deletion .travis.yml
Expand Up @@ -52,7 +52,7 @@ env:
- NUMPY=numpy
- PANDAS=
- PYPARSING=pyparsing
- PYTEST=pytest!=3.3.0
- PYTEST='pytest!=3.3.0,>=3.2.0'
- PYTEST_COV=pytest-cov
- PYTEST_PEP8=
- SPHINX=sphinx
Expand Down
10 changes: 10 additions & 0 deletions doc/api/next_api_changes/2018-02-10-HA.rst
@@ -0,0 +1,10 @@
Deprecated `Axis.unt_data`
``````````````````````````

Use `Axis.units` (which has long existed) instead.

Only accept string-like for Categorical input
`````````````````````````````````````````````

Do not accept mixed string / float / int input, only
strings are valid categoricals.
2 changes: 1 addition & 1 deletion lib/matplotlib/axes/_axes.py
Expand Up @@ -6442,7 +6442,7 @@ def hist(self, x, bins=None, range=None, density=None, weights=None,
if normed is not None:
warnings.warn("The 'normed' kwarg is deprecated, and has been "
"replaced by the 'density' kwarg.")

# basic input validation
input_empty = np.size(x) == 0
# Massage 'x' for processing.
Expand Down
11 changes: 4 additions & 7 deletions lib/matplotlib/axis.py
Expand Up @@ -720,9 +720,6 @@ def __init__(self, axes, pickradius=15):
self.labelpad = rcParams['axes.labelpad']
self.offsetText = self._get_offset_text()

self.majorTicks = []
self.minorTicks = []

self.pickradius = pickradius

# Initialize here for testing; later add API
Expand Down Expand Up @@ -780,14 +777,14 @@ def limit_range_for_scale(self, vmin, vmax):
return self._scale.limit_range_for_scale(vmin, vmax, self.get_minpos())

@property
@cbook.deprecated("2.1.1")
@cbook.deprecated("2.2.0")
def unit_data(self):
return self._units
return self.units

@unit_data.setter
@cbook.deprecated("2.1.1")
@cbook.deprecated("2.2.0")
def unit_data(self, unit_data):
self.set_units = unit_data
self.set_units(unit_data)

def get_children(self):
children = [self.label, self.offsetText]
Expand Down
123 changes: 79 additions & 44 deletions lib/matplotlib/category.py
@@ -1,15 +1,19 @@
# -*- coding: utf-8 -*-
"""
catch all for categorical functions
StrCategorical module for facilitating natively plotting String/Text data.
This module contains the conversion mechanism (a monotonic mapping from
strings to integers), tick locator and formatter, and the class:`.UnitData`
object that creates and stores the string to integer mapping.
"""
from __future__ import (absolute_import, division, print_function,
unicode_literals)

from collections import Iterable, OrderedDict
from collections import OrderedDict
import itertools

import six


import numpy as np

import matplotlib.units as units
Expand All @@ -22,31 +26,26 @@
(bytes, six.text_type, np.str_, np.bytes_)))


def to_str(value):
"""Helper function to turn values to strings.
"""
# Note: This function is only used by StrCategoryFormatter
if LooseVersion(np.__version__) < LooseVersion('1.7.0'):
if (isinstance(value, (six.text_type, np.unicode))):
value = value.encode('utf-8', 'ignore').decode('utf-8')
if isinstance(value, (np.bytes_, six.binary_type)):
value = value.decode(encoding='utf-8')
elif not isinstance(value, (np.str_, six.string_types)):
value = str(value)
return value


class StrCategoryConverter(units.ConversionInterface):
@staticmethod
def convert(value, unit, axis):
"""Uses axis.units to encode string data as floats
"""Converts strings in value to floats using
mapping information store in the unit object
Parameters
----------
value: string, iterable
value or list of values to plot
unit:
axis:
value : string or iterable
value or list of values to be converted
unit : :class:`.UnitData`
object string unit information for value
axis : :class:`~matplotlib.Axis.axis`
axis on which the converted value is plotted
Returns
-------
mapped_ value : float or ndarray[float]
.. note:: axis is not used in this function
"""
# dtype = object preserves numerical pass throughs
values = np.atleast_1d(np.array(value, dtype=object))
Expand All @@ -57,26 +56,53 @@ def convert(value, unit, axis):
return np.asarray(values, dtype=float)

# force an update so it also does type checking
axis.units.update(values)
unit.update(values)

str2idx = np.vectorize(axis.units._mapping.__getitem__,
str2idx = np.vectorize(unit._mapping.__getitem__,
otypes=[float])

mapped_value = str2idx(values)
return mapped_value

@staticmethod
def axisinfo(unit, axis):
"""Sets the axis ticks and labels
"""Sets the default axis ticks and labels
Parameters
---------
unit : :class:`.UnitData`
object string unit information for value
axis : :class:`~matplotlib.Axis.axis`
axis for which information is being set
Returns
-------
:class:~matplotlib.units.AxisInfo~
Information to support default tick labeling
.. note: axis is not used
"""
# locator and formatter take mapping dict because
# args need to be pass by reference for updates
majloc = StrCategoryLocator(axis.units)
majfmt = StrCategoryFormatter(axis.units)
majloc = StrCategoryLocator(unit._mapping)
majfmt = StrCategoryFormatter(unit._mapping)
return units.AxisInfo(majloc=majloc, majfmt=majfmt)

@staticmethod
def default_units(data=None, axis=None):
def default_units(data, axis):
""" Sets and updates the :class:`~matplotlib.Axis.axis~ units
Parameters
----------
data : string or iterable of strings
axis : :class:`~matplotlib.Axis.axis`
axis on which the data is plotted
Returns
-------
class:~.UnitData~
object storing string to integer mapping
"""
# the conversion call stack is supposed to be
# default_units->axis_info->convert
if axis.units is None:
Expand All @@ -88,39 +114,53 @@ def default_units(data=None, axis=None):

class StrCategoryLocator(ticker.Locator):
"""tick at every integer mapping of the string data"""
def __init__(self, units):
def __init__(self, units_mapping):
"""
Parameters
-----------
units: dict
(string, integer) mapping
string:integer mapping
"""
self._units = units
self._units = units_mapping

def __call__(self):
return list(self._units._mapping.values())
return list(self._units.values())

def tick_values(self, vmin, vmax):
return self()


class StrCategoryFormatter(ticker.Formatter):
"""String representation of the data at every tick"""
def __init__(self, units):
def __init__(self, units_mapping):
"""
Parameters
----------
units: dict
(string, integer) mapping
string:integer mapping
"""
self._units = units
self._units = units_mapping

def __call__(self, x, pos=None):
if pos is None:
return ""
r_mapping = {v: to_str(k) for k, v in self._units._mapping.items()}
r_mapping = {v: StrCategoryFormatter._text(k)
for k, v in self._units.items()}
return r_mapping.get(int(np.round(x)), '')

@staticmethod
def _text(value):
"""Converts text values into `utf-8` or `ascii` strings
"""
if LooseVersion(np.__version__) < LooseVersion('1.7.0'):
if (isinstance(value, (six.text_type, np.unicode))):
value = value.encode('utf-8', 'ignore').decode('utf-8')
if isinstance(value, (np.bytes_, six.binary_type)):
value = value.decode(encoding='utf-8')
elif not isinstance(value, (np.str_, six.string_types)):
value = str(value)
return value


class UnitData(object):
def __init__(self, data=None):
Expand All @@ -130,11 +170,10 @@ def __init__(self, data=None):
data: iterable
sequence of string values
"""
if data is None:
data = ()
self._mapping = OrderedDict()
self._counter = itertools.count(start=0)
self.update(data)
if data is not None:
self.update(data)

def update(self, data):
"""Maps new values to integer identifiers.
Expand All @@ -149,13 +188,9 @@ def update(self, data):
TypeError
If the value in data is not a string, unicode, bytes type
"""
data = np.atleast_1d(np.array(data, dtype=object))

if (isinstance(data, VALID_TYPES) or
not isinstance(data, Iterable)):
data = [data]

unsorted_unique = OrderedDict.fromkeys(data)
for val in unsorted_unique:
for val in OrderedDict.fromkeys(data):
if not isinstance(val, VALID_TYPES):
raise TypeError("{val!r} is not a string".format(val=val))
if val not in self._mapping:
Expand Down

0 comments on commit 4d57690

Please sign in to comment.