1
1
# -*- coding: utf-8 -*-
2
2
"""
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.
4
7
"""
5
8
from __future__ import (absolute_import , division , print_function ,
6
9
unicode_literals )
7
10
8
- from collections import Iterable , OrderedDict
11
+ from collections import OrderedDict
9
12
import itertools
10
13
11
14
import six
12
15
16
+
13
17
import numpy as np
14
18
15
19
import matplotlib .units as units
22
26
(bytes , six .text_type , np .str_ , np .bytes_ )))
23
27
24
28
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
-
39
29
class StrCategoryConverter (units .ConversionInterface ):
40
30
@staticmethod
41
31
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
43
34
44
35
Parameters
45
36
----------
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 of floats
47
+
48
+ .. note:: axis is not used in this function
50
49
"""
51
50
# dtype = object preserves numerical pass throughs
52
51
values = np .atleast_1d (np .array (value , dtype = object ))
@@ -57,26 +56,53 @@ def convert(value, unit, axis):
57
56
return np .asarray (values , dtype = float )
58
57
59
58
# force an update so it also does type checking
60
- axis . units .update (values )
59
+ unit .update (values )
61
60
62
- str2idx = np .vectorize (axis . units ._mapping .__getitem__ ,
61
+ str2idx = np .vectorize (unit ._mapping .__getitem__ ,
63
62
otypes = [float ])
64
63
65
64
mapped_value = str2idx (values )
66
65
return mapped_value
67
66
68
67
@staticmethod
69
68
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
71
84
"""
72
85
# locator and formatter take mapping dict because
73
86
# 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 )
76
89
return units .AxisInfo (majloc = majloc , majfmt = majfmt )
77
90
78
91
@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
+ """
80
106
# the conversion call stack is supposed to be
81
107
# default_units->axis_info->convert
82
108
if axis .units is None :
@@ -88,39 +114,53 @@ def default_units(data=None, axis=None):
88
114
89
115
class StrCategoryLocator (ticker .Locator ):
90
116
"""tick at every integer mapping of the string data"""
91
- def __init__ (self , units ):
117
+ def __init__ (self , units_mapping ):
92
118
"""
93
119
Parameters
94
120
-----------
95
121
units: dict
96
- ( string, integer) mapping
122
+ string: integer mapping
97
123
"""
98
- self ._units = units
124
+ self ._units = units_mapping
99
125
100
126
def __call__ (self ):
101
- return list (self ._units ._mapping . values ())
127
+ return list (self ._units .values ())
102
128
103
129
def tick_values (self , vmin , vmax ):
104
130
return self ()
105
131
106
132
107
133
class StrCategoryFormatter (ticker .Formatter ):
108
134
"""String representation of the data at every tick"""
109
- def __init__ (self , units ):
135
+ def __init__ (self , units_mapping ):
110
136
"""
111
137
Parameters
112
138
----------
113
139
units: dict
114
- ( string, integer) mapping
140
+ string: integer mapping
115
141
"""
116
- self ._units = units
142
+ self ._units = units_mapping
117
143
118
144
def __call__ (self , x , pos = None ):
119
145
if pos is None :
120
146
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 ()}
122
149
return r_mapping .get (int (np .round (x )), '' )
123
150
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
+
124
164
125
165
class UnitData (object ):
126
166
def __init__ (self , data = None ):
@@ -130,11 +170,10 @@ def __init__(self, data=None):
130
170
data: iterable
131
171
sequence of string values
132
172
"""
133
- if data is None :
134
- data = ()
135
173
self ._mapping = OrderedDict ()
136
174
self ._counter = itertools .count (start = 0 )
137
- self .update (data )
175
+ if data is not None :
176
+ self .update (data )
138
177
139
178
def update (self , data ):
140
179
"""Maps new values to integer identifiers.
@@ -149,13 +188,9 @@ def update(self, data):
149
188
TypeError
150
189
If the value in data is not a string, unicode, bytes type
151
190
"""
191
+ data = np .atleast_1d (np .array (data , dtype = object ))
152
192
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 ):
159
194
if not isinstance (val , VALID_TYPES ):
160
195
raise TypeError ("{val!r} is not a string" .format (val = val ))
161
196
if val not in self ._mapping :
0 commit comments