11# -*- coding: utf-8 -*-
22"""
3- catch all for categorical functions
3+ StrCategorical module for facilitating natively plotting String/Text data.
4+ This module contains the conversion mechanism (a monotonic mapping from
5+ strings to integers), tick locator and formatter, and the class:`.UnitData`
6+ object that creates and stores the string to integer mapping.
47"""
58from __future__ import (absolute_import , division , print_function ,
69 unicode_literals )
710
8- from collections import Iterable , OrderedDict
11+ from collections import OrderedDict
912import itertools
1013
1114import six
1215
16+
1317import numpy as np
1418
1519import matplotlib .units as units
2226 (bytes , six .text_type , np .str_ , np .bytes_ )))
2327
2428
25- def to_str (value ):
26- """Helper function to turn values to strings.
27- """
28- # Note: This function is only used by StrCategoryFormatter
29- if LooseVersion (np .__version__ ) < LooseVersion ('1.7.0' ):
30- if (isinstance (value , (six .text_type , np .unicode ))):
31- value = value .encode ('utf-8' , 'ignore' ).decode ('utf-8' )
32- if isinstance (value , (np .bytes_ , six .binary_type )):
33- value = value .decode (encoding = 'utf-8' )
34- elif not isinstance (value , (np .str_ , six .string_types )):
35- value = str (value )
36- return value
37-
38-
3929class StrCategoryConverter (units .ConversionInterface ):
4030 @staticmethod
4131 def convert (value , unit , axis ):
42- """Uses axis.units to encode string data as floats
32+ """Converts strings in value to floats using
33+ mapping information store in the unit object
4334
4435 Parameters
4536 ----------
46- value: string, iterable
47- value or list of values to plot
48- unit:
49- axis:
37+ value : string or iterable
38+ value or list of values to be converted
39+ unit : :class:`.UnitData`
40+ object string unit information for value
41+ axis : :class:`~matplotlib.Axis.axis`
42+ axis on which the converted value is plotted
43+
44+ Returns
45+ -------
46+ mapped_ value : float or ndarray[float]
47+
48+ .. note:: axis is not used in this function
5049 """
5150 # dtype = object preserves numerical pass throughs
5251 values = np .atleast_1d (np .array (value , dtype = object ))
@@ -57,26 +56,53 @@ def convert(value, unit, axis):
5756 return np .asarray (values , dtype = float )
5857
5958 # force an update so it also does type checking
60- axis . units .update (values )
59+ unit .update (values )
6160
62- str2idx = np .vectorize (axis . units ._mapping .__getitem__ ,
61+ str2idx = np .vectorize (unit ._mapping .__getitem__ ,
6362 otypes = [float ])
6463
6564 mapped_value = str2idx (values )
6665 return mapped_value
6766
6867 @staticmethod
6968 def axisinfo (unit , axis ):
70- """Sets the axis ticks and labels
69+ """Sets the default axis ticks and labels
70+
71+ Parameters
72+ ---------
73+ unit : :class:`.UnitData`
74+ object string unit information for value
75+ axis : :class:`~matplotlib.Axis.axis`
76+ axis for which information is being set
77+
78+ Returns
79+ -------
80+ :class:~matplotlib.units.AxisInfo~
81+ Information to support default tick labeling
82+
83+ .. note: axis is not used
7184 """
7285 # locator and formatter take mapping dict because
7386 # args need to be pass by reference for updates
74- majloc = StrCategoryLocator (axis . units )
75- majfmt = StrCategoryFormatter (axis . units )
87+ majloc = StrCategoryLocator (unit . _mapping )
88+ majfmt = StrCategoryFormatter (unit . _mapping )
7689 return units .AxisInfo (majloc = majloc , majfmt = majfmt )
7790
7891 @staticmethod
79- def default_units (data = None , axis = None ):
92+ def default_units (data , axis ):
93+ """ Sets and updates the :class:`~matplotlib.Axis.axis~ units
94+
95+ Parameters
96+ ----------
97+ data : string or iterable of strings
98+ axis : :class:`~matplotlib.Axis.axis`
99+ axis on which the data is plotted
100+
101+ Returns
102+ -------
103+ class:~.UnitData~
104+ object storing string to integer mapping
105+ """
80106 # the conversion call stack is supposed to be
81107 # default_units->axis_info->convert
82108 if axis .units is None :
@@ -88,39 +114,53 @@ def default_units(data=None, axis=None):
88114
89115class StrCategoryLocator (ticker .Locator ):
90116 """tick at every integer mapping of the string data"""
91- def __init__ (self , units ):
117+ def __init__ (self , units_mapping ):
92118 """
93119 Parameters
94120 -----------
95121 units: dict
96- ( string, integer) mapping
122+ string: integer mapping
97123 """
98- self ._units = units
124+ self ._units = units_mapping
99125
100126 def __call__ (self ):
101- return list (self ._units ._mapping . values ())
127+ return list (self ._units .values ())
102128
103129 def tick_values (self , vmin , vmax ):
104130 return self ()
105131
106132
107133class StrCategoryFormatter (ticker .Formatter ):
108134 """String representation of the data at every tick"""
109- def __init__ (self , units ):
135+ def __init__ (self , units_mapping ):
110136 """
111137 Parameters
112138 ----------
113139 units: dict
114- ( string, integer) mapping
140+ string: integer mapping
115141 """
116- self ._units = units
142+ self ._units = units_mapping
117143
118144 def __call__ (self , x , pos = None ):
119145 if pos is None :
120146 return ""
121- r_mapping = {v : to_str (k ) for k , v in self ._units ._mapping .items ()}
147+ r_mapping = {v : StrCategoryFormatter ._text (k )
148+ for k , v in self ._units .items ()}
122149 return r_mapping .get (int (np .round (x )), '' )
123150
151+ @staticmethod
152+ def _text (value ):
153+ """Converts text values into `utf-8` or `ascii` strings
154+ """
155+ if LooseVersion (np .__version__ ) < LooseVersion ('1.7.0' ):
156+ if (isinstance (value , (six .text_type , np .unicode ))):
157+ value = value .encode ('utf-8' , 'ignore' ).decode ('utf-8' )
158+ if isinstance (value , (np .bytes_ , six .binary_type )):
159+ value = value .decode (encoding = 'utf-8' )
160+ elif not isinstance (value , (np .str_ , six .string_types )):
161+ value = str (value )
162+ return value
163+
124164
125165class UnitData (object ):
126166 def __init__ (self , data = None ):
@@ -130,11 +170,10 @@ def __init__(self, data=None):
130170 data: iterable
131171 sequence of string values
132172 """
133- if data is None :
134- data = ()
135173 self ._mapping = OrderedDict ()
136174 self ._counter = itertools .count (start = 0 )
137- self .update (data )
175+ if data is not None :
176+ self .update (data )
138177
139178 def update (self , data ):
140179 """Maps new values to integer identifiers.
@@ -149,13 +188,9 @@ def update(self, data):
149188 TypeError
150189 If the value in data is not a string, unicode, bytes type
151190 """
191+ data = np .atleast_1d (np .array (data , dtype = object ))
152192
153- if (isinstance (data , VALID_TYPES ) or
154- not isinstance (data , Iterable )):
155- data = [data ]
156-
157- unsorted_unique = OrderedDict .fromkeys (data )
158- for val in unsorted_unique :
193+ for val in OrderedDict .fromkeys (data ):
159194 if not isinstance (val , VALID_TYPES ):
160195 raise TypeError ("{val!r} is not a string" .format (val = val ))
161196 if val not in self ._mapping :
0 commit comments