From b1848420bf80cb40211f37fb32f3a2d07e9b5f7f Mon Sep 17 00:00:00 2001 From: hannah Date: Thu, 16 Jun 2016 18:19:18 -0400 Subject: [PATCH 1/2] Basic support for plotting lists of strings/categorical data. Support for updating ticks/animation in progress/buggy, 'especially for scatter. --- lib/matplotlib/__init__.py | 1 + lib/matplotlib/axes/_axes.py | 1 + lib/matplotlib/axis.py | 12 + lib/matplotlib/category.py | 117 +++++ .../baseline_images/test_axes/units_strings.pdf | Bin 5843 -> 0 bytes .../baseline_images/test_axes/units_strings.png | Bin 19628 -> 0 bytes .../baseline_images/test_axes/units_strings.svg | 544 --------------------- lib/matplotlib/tests/test_axes.py | 11 - lib/matplotlib/tests/test_category.py | 215 ++++++++ lib/matplotlib/units.py | 9 +- 10 files changed, 350 insertions(+), 560 deletions(-) create mode 100644 lib/matplotlib/category.py delete mode 100644 lib/matplotlib/tests/baseline_images/test_axes/units_strings.pdf delete mode 100644 lib/matplotlib/tests/baseline_images/test_axes/units_strings.png delete mode 100644 lib/matplotlib/tests/baseline_images/test_axes/units_strings.svg create mode 100644 lib/matplotlib/tests/test_category.py diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index b2b0eb1b571..097ba8f3232 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1489,6 +1489,7 @@ def _jupyter_nbextension_paths(): 'matplotlib.tests.test_backend_svg', 'matplotlib.tests.test_basic', 'matplotlib.tests.test_bbox_tight', + 'matplotlib.tests.test_category', 'matplotlib.tests.test_cbook', 'matplotlib.tests.test_coding_standards', 'matplotlib.tests.test_collections', diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 960ba9e96f5..017f59cc54a 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -21,6 +21,7 @@ import matplotlib.collections as mcoll import matplotlib.colors as mcolors import matplotlib.contour as mcontour +import matplotlib.category as _ # <-registers a date unit converter import matplotlib.dates as _ # <-registers a date unit converter from matplotlib import docstring import matplotlib.image as mimage diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index dc3f0bb4332..298ab177c16 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -662,6 +662,7 @@ def __init__(self, axes, pickradius=15): self.offsetText = self._get_offset_text() self.majorTicks = [] self.minorTicks = [] + self.unit_data = [] self.pickradius = pickradius # Initialize here for testing; later add API @@ -712,6 +713,17 @@ def _set_scale(self, value, **kwargs): def limit_range_for_scale(self, vmin, vmax): return self._scale.limit_range_for_scale(vmin, vmax, self.get_minpos()) + @property + def unit_data(self): + """Holds data that a ConversionInterface subclass relys on + to convert between labels and indexes + """ + return self._unit_data + + @unit_data.setter + def unit_data(self, data): + self._unit_data = data + def get_children(self): children = [self.label, self.offsetText] majorticks = self.get_major_ticks() diff --git a/lib/matplotlib/category.py b/lib/matplotlib/category.py new file mode 100644 index 00000000000..32093cb3ea1 --- /dev/null +++ b/lib/matplotlib/category.py @@ -0,0 +1,117 @@ +""" +catch all for categorical functions +""" +from __future__ import (absolute_import, division, print_function, + unicode_literals) + +import six +import numpy as np + +import matplotlib.units as units +import matplotlib.ticker as ticker + + +class StrCategoryConverter(units.ConversionInterface): + @staticmethod + def convert(value, unit, axis): + """Uses axis.unit_data map to encode + data as integers + """ + + if isinstance(value, six.string_types): + return dict(axis.unit_data)[value] + + vals = np.asarray(value, dtype='str') + for label, loc in axis.unit_data: + vals[vals == label] = loc + return vals.astype('float') + + @staticmethod + def axisinfo(unit, axis): + seq, locs = zip(*axis.unit_data) + majloc = StrCategoryLocator(locs) + majfmt = StrCategoryFormatter(seq) + return units.AxisInfo(majloc=majloc, majfmt=majfmt) + + @staticmethod + def default_units(data, axis): + # the conversion call stack is: + # default_units->axis_info->convert + axis.unit_data = map_categories(data, axis.unit_data) + return None + + +def map_categories(data, old_map=[], sort=True): + """Create mapping between unique categorical + values and numerical identifier. + + Paramters + --------- + data: iterable + sequence of values + old_map: list of tuple, optional + if not `None`, than old_mapping will be updated with new values and + previous mappings will remain unchanged) + sort: bool, optional + sort keys by ASCII value + + Returns + ------- + list of tuple + [(label, ticklocation),...] + + """ + + # code typical missing data in the negative range because + # everything else will always have positive encoding + # question able if it even makes sense + spdict = {'nan': -1, 'inf': -2, '-inf': -3} + + # cast all data to str + strdata = [str(d) for d in data] + + uniq = set(strdata) + + category_map = old_map.copy() + + if old_map: + olabs, okeys = zip(*old_map) + olabs, okeys = set(olabs), set(okeys) + svalue = max(okeys) + 1 + else: + olabs, okeys = set(), set() + svalue = 0 + + new_labs = (uniq - olabs) + + missing = (new_labs & set(spdict.keys())) + category_map.extend([(m, spdict[m]) for m in missing]) + + new_labs = (new_labs - missing) + if sort: + new_labs = list(new_labs) + new_labs.sort() + + new_locs = range(svalue, svalue + len(new_labs)) + category_map.extend(list(zip(new_labs, new_locs))) + return category_map + + +class StrCategoryLocator(ticker.FixedLocator): + def __init__(self, locs): + super(StrCategoryLocator, self).__init__(locs, None) + + +class StrCategoryFormatter(ticker.FixedFormatter): + def __init__(self, seq): + super(StrCategoryFormatter, self).__init__(seq) + + +# Connects the convertor to matplotlib +units.registry[bytearray] = StrCategoryConverter() +units.registry[str] = StrCategoryConverter() + +if six.PY3: + units.registry[bytes] = StrCategoryConverter() +elif six.PY2: + units.registry[unicode] = StrCategoryConverter() diff --git a/lib/matplotlib/tests/baseline_images/test_axes/units_strings.pdf b/lib/matplotlib/tests/baseline_images/test_axes/units_strings.pdf deleted file mode 100644 index a5ad772d25a96732e05af9f9e6b5f95d064e6647..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5843 zcmb^#3piA1JC*3rB2glHwnB25bLPyMi&9}!Vj@~;sSE}q%}g_g#-&=Jt=(#6>4LPD zTBVChcgkf`q?B}9rBtih?pL*u|NG7uhWhvE`JaD0ukW1qyx)8K-tYb1_p|chPj$c@ zS%}rH8)(rn1V=Gc8WM#}oQN`fgb^YI$^;l+lrcL_CPEo*LP{7djX)<&LPU}pqR>FVia!;w5Gg5?;jsv?b?qA6x<(Il9>Q=}Qq%k>Atgdt zDh7U$!Z5KU0%faSr%ELhN_6oSQj|z8K}q_xM;#mk2(oMG9$*JQSD?ZSzICOo!fpK9K`1DyJZfX>w_d428b( z1Lnzv5`_$Y4~qGmh3RU&%aoG_9JqcR)L@e95~Z z+2%8TGENk^*_@dF&1TmlO%<(|TAuCwP<2zH*TxuSp8uCN7B3j=N(`TpRN}FlNV;xX zwr)?{E0@sQ{!9H1KTljIID_wQ`pWz1+m6@aw|0GX-o`HOM(W&<>SD7h1KTah7nkje znaH`by|nqz0;%o1`O$%xxTP>SJF9uVp7Tb^3f=`d;^gaboiv zgVlCAiLrmK&H2!uadZ6FHVu&x#t-VWvd)^mbTiRwn5}n|VLIb*RMNFaR7K_OVe+&; zR!Y6+>gblrD5{T-BVofZ^h+*HTqd986Du55l(`Ejd$ zR#rt}vdPOd^Y!@$WgBr9|&V`FEOb{A% zx(1xd(2d(sbKdr$=OiaDgF}ud_l?%u9b@&lHVe(QdzQC(|GtaY?gYz5TF?L9loR|w zymll>wXE3gnCY5y%If*%#F&H!C%ny+QD*YkfktjM!GoiYnF<0*CT>lesdcAwS=CdA zdwJ=vYIDuso98@vKH-3*#U?6t^hLP|v+)4y^ax#Jcgu-!?%QQ|4qRDsnKiUATyN$E ztvxd*ulcH9@aXw-zkj@k&+}|pw{5Gm^mLoSlzx6CpK%vVbzkHf7N?hM1hbAW;^X>{ zZVd~%-S5%DxLiHod2gj|HM(h)Xdc7%cPrDk)t>q-I)Mf!?ydjUZMo%S{4rtgTyw8+ za_iBW*~3nl?9ATeGd!fFBXVG=Da9CVSF3aDXOX_*eB14O!;Qh#tl(Wz59{HVONp?` z)p_fxT(W16SRxo^zsKYpaps9XXTQ?{G+Jq8e5bk}f8h1uY3G9T*X+woJ0-U)MAEgt z3%>f$eAZ7Z>@uzlrhdEf$FaKGMfcyva@-g%ZxaV_Sv6!sKid;U zLsoApy#DJCDVgazM>V`yQ}8^nV%}<@PheovKxOy^!|ToK*BTK|E}AW#YqW5(^TYgE z%!9Mc3app*Y)ngTlZzhydhy;3-qCHFZ2i|i z*Pc+THK44cIel$?!fpFCK~0anY@S~f|88Nt`-EjkNAl!o&+MSnc0afeD=ZzSyX>lq z@#4gQQ*B}S{3Rt?SH>7$j)jrorKR?N; z^F7sOdSF<+!`^B2Mu#1cfwPRA-!W}WMmN?sK5$YBH%=O`*Knq-sZ!^ceuMV5EKp|5 zOfH{b&TKaNW&KFQu@zaKKMAI1ID53){q?3GlSM7Fwo|-Mknb24m0zQ4f(9JDrc+>N zYdO}~dG+-D@-mM9s^+vg>8B&Ctj!E>TJIB-l;6FPS(37OldE=X!1tG_1IPZc@CvwF zobqD4(DZV$AeY>c5?-<{G^|S5R_V1mb!F91pX{ZXgLiKH`O=NSW42Ed#6GFqAIN_8 z4L|=qVV_ZPdfZs8A7+M*MH-5~H%j7_75`Y)s>d@eUnSa-Wv{;|(&~8py43OuIR~~0 ztFPu>ef3etJuo5v(^X(0U#0m=JcEKY9l`0#dd ze7~ZO_(3-f8Z1T!d`3gfLi67=WU{!uHon6wUittR-Lv;i;$0P&p6xrkzOC~iQmd7` z(hYTTSnN~nZMea%`Q@2!OIn_79#OHRBJWmhjJYY#&}4eUh}nX|d8L+RIUe(v1#F&u-6Rs}w4)dlFPX-`-mMn-#YK`|Rdef#e z-a9sSvGcjJX0Jch3x~D4S}!l5f^v%#%26gsqKuisMWCMLi=u@7F@B&{232!R2u1f5 zu)81-2CVR1RYkc2jxll6fyE|JoWoDnK(4H8f&!!`ld;i@)8 zjY1fFad@~0mgJzE4@5yMG2%sXDaw#afGX4yDM~U?MmVVV!GCcqz$wH^lw_j}kvJlf z0s=ujL#xFXDMIC986}mYcvlZHRV?o^4+AwjP+cIT${i1~~ZZegX}St56}L zo0wisP^qlK&k=`Fk-#)n(*O;*79K|u{04@svV8r9191eH~%SZy@90(vZnLH8!fU-eIk|baP zwgU%5F^3Bjz`!#c;dcT6sNFeWKIF;c^1!IV!I3tG;2>~>NI)KyrBW$m0Ng0iKOBfq^QyEFhNzdli;w@EZw6E^rncX<$M+HthsBLizNf zK?edIfMXFTlLT{Ur>K>KBOS?55(~ay&_z&fXc`;=(CY%lsQ@9n)~uVA>H#RIV!;F! zgJ~qx3TUnl1NCyLN3djwS`nH9i-SY6Y4u?obn}N)&?Y*VKtRFwZZ1$M3`6K|T?}|^ z9;yZy2&x!>anKuRUxT=SF`)r;JiwSx105?}F#+R26QEx7JKzChLk-Y!Hb#Oq1iHc) z_zdv4U`=3LfJCba>X=ZE&?#Wu5qgBr8bGhL>N}+A1{fD`RpB_VJITR>u1<1vnx;EX z->%dQ3P$=SN|l6wW9c`Tuko%s9XgNGwGX(pfyo^#I!y$#J+p64)l8{V!FsALoqBSW z=0WCyYo0FM_f!lw7a)KUDnxPmpm1USS2Dbt3{rNbbq#}ikQ&`~4^pGg>_MuNDLnW* z#bNM-qwl|b;X$cN(W*ND1LP{1brJ}_lDDFr) z;*K(@g2LHc!ZAz~i`hl+SUikmhuWi&6eV+FFnZ`6rSb?{gaMA`u$WMAiKWR(78Z_% z2tyYF(YD|w4hj=XC46|(H;(Vb#BeqSk_gTvcsS{Rv1~BR#un)@Qz;jPBOocW5Rfvv zejp=~Ab!HpZXApi_(LUX9Ep*@$lf>>mjuDm7smr}(g()_X`~O1#K5lk1P69i9~_3U z!NS5{k1iMiQdVCa69Ylqn-0fVpU5R(hVMg1us(ysKVvUEE&9yGF>vJd#SyGee1)^Y zY14;}i8DXp$Aqc3FC7~kqkV7$T@*oNk6%a}R#R#mB^QdLMRNM~*+UWzHZb@IZ=Zdo hQW(DUv!6H~>}Hm#D-g;lIy^`w7u;c6xqDAV{sRHpqEG+; diff --git a/lib/matplotlib/tests/baseline_images/test_axes/units_strings.png b/lib/matplotlib/tests/baseline_images/test_axes/units_strings.png deleted file mode 100644 index 7fc60cb30235448a7040130ecaec2969972b7cf5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19628 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 diff --git a/lib/matplotlib/tests/baseline_images/test_axes/units_strings.svg b/lib/matplotlib/tests/baseline_images/test_axes/units_strings.svg deleted file mode 100644 index 79446b2e853..00000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/units_strings.svg +++ /dev/null @@ -1,544 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 478f5844100..73c42579d1b 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -905,17 +905,6 @@ def test_arc_ellipse(): ax.add_patch(e2) -@image_comparison(baseline_images=['units_strings']) -def test_units_strings(): - # Make sure passing in sequences of strings doesn't cause the unit - # conversion registry to recurse infinitely - Id = ['50', '100', '150', '200', '250'] - pout = ['0', '7.4', '11.4', '14.2', '16.3'] - fig = plt.figure() - ax = fig.add_subplot(111) - ax.plot(Id, pout) - - @image_comparison(baseline_images=['markevery'], remove_text=True) def test_markevery(): diff --git a/lib/matplotlib/tests/test_category.py b/lib/matplotlib/tests/test_category.py new file mode 100644 index 00000000000..d2bb79357fc --- /dev/null +++ b/lib/matplotlib/tests/test_category.py @@ -0,0 +1,215 @@ +"""Catch all for categorical functions +""" +from __future__ import (absolute_import, division, print_function, + unicode_literals) +import unittest + +import numpy as np + +import matplotlib.pyplot as plt +from matplotlib.testing.decorators import cleanup, knownfailureif +import matplotlib.category as cat + + +class FakeAxis(object): + def __init__(self): + self.unit_data = [] + + +class TestStrCategoryConverter(unittest.TestCase): + """ + Based on the pandas conversion and factorization tests: + + ref: /pandas/tseries/tests/test_converter.py + /pandas/tests/test_algos.py:TestFactorize + """ + + def setUp(self): + self.cc = cat.StrCategoryConverter() + self.axis = FakeAxis() + + def test_convert_accepts_unicode(self): + # possibly not needed in PY3 + self.axis.unit_data = [('a', 0), ('b', 1)] + c1 = self.cc.convert("a", None, self.axis) + c2 = self.cc.convert(u"a", None, self.axis) + self.assertEqual(c1, c2) + # single values always set at 0 + c1 = self.cc.convert(["a", "b"], None, self.axis) + c2 = self.cc.convert([u"a", u"b"], None, self.axis) + np.testing.assert_array_equal(c1, c2) + + def test_convert_single(self): + self.axis.unit_data = [('a', 0)] + act = self.cc.convert("a", None, self.axis) + exp = 0 + self.assertEqual(act, exp) + + def test_convert_basic(self): + data = ['a', 'b', 'b', 'a', 'a', 'c', 'c', 'c'] + exp = [0, 1, 1, 0, 0, 2, 2, 2] + self.axis.unit_data = [('a', 0), ('b', 1), ('c', 2)] + act = self.cc.convert(data, None, self.axis) + np.testing.assert_array_equal(act, exp) + + def test_convert_mixed(self): + data = ['A', 'A', np.nan, 'B', -np.inf, 3.14, np.inf] + exp = [1, 1, -1, 2, 3, 0, 4] + self.axis.unit_data = [('nan', -1), ('3.14', 0), + ('A', 1), ('B', 2), + ('-inf', 3), ('inf', 4)] + act = self.cc.convert(data, None, self.axis) + np.testing.assert_array_equal(act, exp) + + def test_axisinfo(self): + self.axis.unit_data = [('a', 0)] + ax = self.cc.axisinfo(None, self.axis) + self.assertTrue(isinstance(ax.majloc, cat.StrCategoryLocator)) + self.assertTrue(isinstance(ax.majfmt, cat.StrCategoryFormatter)) + + def test_default_units(self): + self.assertEqual(self.cc.default_units(["a"], self.axis), None) + + +class TestMapCategories(unittest.TestCase): + def test_map_data(self): + act = cat.map_categories("a") + exp = [('a', 0)] + self.assertListEqual(act, exp) + + def test_map_data_basic(self): + data = ['a', 'b', 'b', 'a', 'a', 'c', 'c', 'c'] + exp = [('a', 0), ('b', 1), ('c', 2)] + act = cat.map_categories(data) + self.assertListEqual(act, exp) + + def test_map_data_mixed(self): + data = ['A', 'A', np.nan, 'B', -np.inf, 3.14, np.inf] + exp = [('nan', -1), ('3.14', 0), + ('A', 1), ('B', 2), ('-inf', -3), ('inf', -2)] + + act = cat.map_categories(data) + self.assertListEqual(sorted(act), sorted(exp)) + + def test_update_map(self): + data = ['b', 'd', 'e', np.inf] + old_map = [('a', 0), ('d', 1)] + exp = [('inf', -2), ('a', 0), ('d', 1), + ('b', 2), ('e', 3)] + act = cat.map_categories(data, old_map) + self.assertListEqual(sorted(act), sorted(exp)) + + +class TestStrCategoryLocator(unittest.TestCase): + def setUp(self): + self.locs = list(range(10)) + + def test_CategoricalLocator(self): + ticks = cat.StrCategoryLocator(self.locs) + np.testing.assert_equal(ticks.tick_values(None, None), + self.locs) + + +class TestStrCategoryFormatter(unittest.TestCase): + def setUp(self): + self.seq = ["hello", "world", "hi"] + + def test_CategoricalFormatter(self): + labels = cat.StrCategoryFormatter(self.seq) + self.assertEqual(labels('a', 1), "world") + + +class TestPlot(unittest.TestCase): + @classmethod + def setupClass(cls): + cls.lt = lambda tl: [l.get_text() for l in tl] + + def setUp(self): + self.d = ['a', 'b', 'c', 'a'] + self.dticks = [0, 1, 2] + self.dlabels = ['a', 'b', 'c'] + self.dunit_data = [('a', 0), ('b', 1), ('c', 2)] + + self.dm = ['here', np.nan, 'here', 'there'] + self.dmticks = [-1, 0, 1] + self.dmlabels = ['nan', 'here', 'there'] + self.dmunit_data = [('nan', -1), ('here', 0), ('there', 1)] + + @cleanup + def test_plot_1d(self): + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1) + ax.plot(self.d) + fig.canvas.draw() + + np.testing.assert_array_equal(ax.get_yticks(), self.dticks) + self.assertListEqual(TestPlot.lt(ax.get_yticklabels()), + self.dlabels) + self.assertListEqual(ax.yaxis.unit_data, self.dunit_data) + + @cleanup + def test_plot_1d_missing(self): + + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1) + ax.plot(self.dm) + fig.canvas.draw() + + np.testing.assert_array_equal(ax.get_yticks(), self.dmticks) + self.assertListEqual(TestPlot.lt(ax.get_yticklabels()), + self.dmlabels) + self.assertListEqual(ax.yaxis.unit_data, self.dmunit_data) + + @cleanup + def test_plot_2d(self): + + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1) + ax.plot(self.dm, self.d) + fig.canvas.draw() + + np.testing.assert_array_equal(ax.get_xticks(), self.dmticks) + self.assertListEqual(TestPlot.lt(ax.get_xticklabels()), + self.dmlabels) + self.assertListEqual(ax.xaxis.unit_data, self.dmunit_data) + + np.testing.assert_array_equal(ax.get_yticks(), self.dticks) + self.assertListEqual(TestPlot.lt(ax.get_yticklabels()), + self.dlabels) + self.assertListEqual(ax.yaxis.unit_data, self.dunit_data) + + @cleanup + def test_scatter_2d(self): + + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1) + ax.scatter(self.dm, self.d) + fig.canvas.draw() + + np.testing.assert_array_equal(ax.get_xticks(), self.dmticks) + self.assertListEqual(TestPlot.lt(ax.get_xticklabels()), + self.dmlabels) + self.assertListEqual(ax.xaxis.unit_data, self.dmunit_data) + + np.testing.assert_array_equal(ax.get_yticks(), self.dticks) + self.assertListEqual(TestPlot.lt(ax.get_yticklabels()), + self.dlabels) + self.assertListEqual(ax.yaxis.unit_data, self.dunit_data) + + @unittest.expectedFailure + @cleanup + def test_plot_update(self): + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1) + + ax.plot(['a', 'b']) + ax.plot(['a', 'b', 'd']) + ax.plot(['b', 'c', 'd']) + fig.canvas.draw() + + labels_new = ['a', 'b', 'd', 'c'] + ticks_new = [0, 1, 2, 3] + self.assertListEqual(ax.yaxis.unit_data, + list(zip(labels_new, ticks_new))) + np.testing.assert_array_equal(ax.get_yticks(), ticks_new) + self.assertListEqual(TestPlot.lt(ax.get_yticklabels()), labels_new) diff --git a/lib/matplotlib/units.py b/lib/matplotlib/units.py index 081827910b5..afaba10f964 100644 --- a/lib/matplotlib/units.py +++ b/lib/matplotlib/units.py @@ -1,16 +1,15 @@ """ The classes here provide support for using custom classes with matplotlib, e.g., those that do not expose the array interface but know -how to converter themselves to arrays. It also supoprts classes with +how to convert themselves to arrays. It also supports classes with units and units conversion. Use cases include converters for custom objects, e.g., a list of datetime objects, as well as for objects that -are unit aware. We don't assume any particular units implementation, -rather a units implementation must provide a ConversionInterface, and -the register with the Registry converter dictionary. For example, +are unit aware. We don't assume any particular units implementation; +rather a units implementation must provide the register with the Registry +converter dictionary and a ConversionInterface. For example, here is a complete implementation which supports plotting with native datetime objects:: - import matplotlib.units as units import matplotlib.dates as dates import matplotlib.ticker as ticker From 8a96281305ac7e8c67cf02a099515d584cd6bd3d Mon Sep 17 00:00:00 2001 From: hannah Date: Fri, 17 Jun 2016 16:37:07 -0400 Subject: [PATCH 2/2] categorical axis support with np1.6 hack --- lib/matplotlib/axes/_axes.py | 2 +- lib/matplotlib/category.py | 108 ++++++++++++++-------- lib/matplotlib/tests/test_category.py | 164 +++++++++++++++++++++------------- lib/matplotlib/units.py | 10 +-- 4 files changed, 178 insertions(+), 106 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 017f59cc54a..e24bf1a6097 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -21,7 +21,7 @@ import matplotlib.collections as mcoll import matplotlib.colors as mcolors import matplotlib.contour as mcontour -import matplotlib.category as _ # <-registers a date unit converter +import matplotlib.category as _ # <-registers a category unit converter import matplotlib.dates as _ # <-registers a date unit converter from matplotlib import docstring import matplotlib.image as mimage diff --git a/lib/matplotlib/category.py b/lib/matplotlib/category.py index 32093cb3ea1..bfac242149c 100644 --- a/lib/matplotlib/category.py +++ b/lib/matplotlib/category.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 OA-*-za """ catch all for categorical functions """ @@ -5,25 +6,45 @@ unicode_literals) import six + import numpy as np import matplotlib.units as units import matplotlib.ticker as ticker +# pure hack for numpy 1.6 support +from distutils.version import LooseVersion + +NP_NEW = (LooseVersion(np.version.version) >= LooseVersion('1.7')) + + +def to_array(data, maxlen=100): + if NP_NEW: + return np.array(data, dtype=np.unicode) + try: + vals = np.array(data, dtype=('|S', maxlen)) + except UnicodeEncodeError: + # pure hack + vals = np.array([convert_to_string(d) for d in data]) + return vals + + class StrCategoryConverter(units.ConversionInterface): @staticmethod def convert(value, unit, axis): """Uses axis.unit_data map to encode - data as integers + data as floats """ + vmap = dict(axis.unit_data) if isinstance(value, six.string_types): - return dict(axis.unit_data)[value] + return vmap[value] + + vals = to_array(value) + for lab, loc in axis.unit_data: + vals[vals == lab] = loc - vals = np.asarray(value, dtype='str') - for label, loc in axis.unit_data: - vals[vals == label] = loc return vals.astype('float') @staticmethod @@ -41,7 +62,36 @@ def default_units(data, axis): return None -def map_categories(data, old_map=[], sort=True): +class StrCategoryLocator(ticker.FixedLocator): + def __init__(self, locs): + super(StrCategoryLocator, self).__init__(locs, None) + + +class StrCategoryFormatter(ticker.FixedFormatter): + def __init__(self, seq): + super(StrCategoryFormatter, self).__init__(seq) + + +def convert_to_string(value): + """Helper function for numpy 1.6, can be replaced with + np.array(...,dtype=unicode) for all later versions of numpy""" + + if isinstance(value, six.string_types): + return value + if np.isfinite(value): + value = np.asarray(value, dtype=str)[np.newaxis][0] + elif np.isnan(value): + value = 'nan' + elif np.isposinf(value): + value = 'inf' + elif np.isneginf(value): + value = '-inf' + else: + raise ValueError("Unconvertable {}".format(value)) + return value + + +def map_categories(data, old_map=None): """Create mapping between unique categorical values and numerical identifier. @@ -65,53 +115,37 @@ def map_categories(data, old_map=[], sort=True): # code typical missing data in the negative range because # everything else will always have positive encoding # question able if it even makes sense - spdict = {'nan': -1, 'inf': -2, '-inf': -3} - - # cast all data to str - strdata = [str(d) for d in data] + spdict = {'nan': -1.0, 'inf': -2.0, '-inf': -3.0} - uniq = set(strdata) + if isinstance(data, six.string_types): + data = [data] - category_map = old_map.copy() + # will update this post cbook/dict support + strdata = to_array(data) + uniq = np.unique(strdata) if old_map: olabs, okeys = zip(*old_map) - olabs, okeys = set(olabs), set(okeys) svalue = max(okeys) + 1 else: - olabs, okeys = set(), set() + old_map, olabs, okeys = [], [], [] svalue = 0 - new_labs = (uniq - olabs) + category_map = old_map[:] + + new_labs = [u for u in uniq if u not in olabs] + missing = [nl for nl in new_labs if nl in spdict.keys()] - missing = (new_labs & set(spdict.keys())) category_map.extend([(m, spdict[m]) for m in missing]) - new_labs = (new_labs - missing) - if sort: - new_labs = list(new_labs) - new_labs.sort() + new_labs = [nl for nl in new_labs if nl not in missing] - new_locs = range(svalue, svalue + len(new_labs)) + new_locs = np.arange(svalue, svalue + len(new_labs), dtype='float') category_map.extend(list(zip(new_labs, new_locs))) return category_map -class StrCategoryLocator(ticker.FixedLocator): - def __init__(self, locs): - super(StrCategoryLocator, self).__init__(locs, None) - - -class StrCategoryFormatter(ticker.FixedFormatter): - def __init__(self, seq): - super(StrCategoryFormatter, self).__init__(seq) - - # Connects the convertor to matplotlib -units.registry[bytearray] = StrCategoryConverter() units.registry[str] = StrCategoryConverter() - -if six.PY3: - units.registry[bytes] = StrCategoryConverter() -elif six.PY2: - units.registry[unicode] = StrCategoryConverter() +units.registry[bytes] = StrCategoryConverter() +units.registry[six.text_type] = StrCategoryConverter() diff --git a/lib/matplotlib/tests/test_category.py b/lib/matplotlib/tests/test_category.py index d2bb79357fc..02db774e4ff 100644 --- a/lib/matplotlib/tests/test_category.py +++ b/lib/matplotlib/tests/test_category.py @@ -1,5 +1,5 @@ -"""Catch all for categorical functions -""" +# -*- coding: utf-8 -*- +"""Catch all for categorical functions""" from __future__ import (absolute_import, division, print_function, unicode_literals) import unittest @@ -7,18 +7,76 @@ import numpy as np import matplotlib.pyplot as plt -from matplotlib.testing.decorators import cleanup, knownfailureif +from matplotlib.testing.decorators import cleanup import matplotlib.category as cat +class TestConvertToString(unittest.TestCase): + def setUp(self): + pass + + def test_string(self): + self.assertEqual("abc", cat.convert_to_string("abc")) + + def test_unicode(self): + self.assertEqual("Здравствуйте мир", + cat.convert_to_string("Здравствуйте мир")) + + def test_decimal(self): + self.assertEqual("3.14", cat.convert_to_string(3.14)) + + def test_nan(self): + self.assertEqual("nan", cat.convert_to_string(np.nan)) + + def test_posinf(self): + self.assertEqual("inf", cat.convert_to_string(np.inf)) + + def test_neginf(self): + self.assertEqual("-inf", cat.convert_to_string(-np.inf)) + + +class TestMapCategories(unittest.TestCase): + def test_map_unicode(self): + act = cat.map_categories("Здравствуйте мир") + exp = [("Здравствуйте мир", 0)] + self.assertListEqual(act, exp) + + def test_map_data(self): + act = cat.map_categories("hello world") + exp = [("hello world", 0)] + self.assertListEqual(act, exp) + + def test_map_data_basic(self): + data = ['a', 'b', 'b', 'a', 'a', 'c', 'c', 'c'] + exp = [('a', 0), ('b', 1), ('c', 2)] + act = cat.map_categories(data) + self.assertListEqual(sorted(act), sorted(exp)) + + def test_map_data_mixed(self): + data = ['A', 'A', np.nan, 'B', -np.inf, 3.14, np.inf] + exp = [('nan', -1), ('3.14', 0), + ('A', 1), ('B', 2), ('-inf', -3), ('inf', -2)] + + act = cat.map_categories(data) + self.assertListEqual(sorted(act), sorted(exp)) + + @unittest.SkipTest + def test_update_map(self): + data = ['b', 'd', 'e', np.inf] + old_map = [('a', 0), ('d', 1)] + exp = [('inf', -2), ('a', 0), ('d', 1), + ('b', 2), ('e', 3)] + act = cat.map_categories(data, old_map) + self.assertListEqual(sorted(act), sorted(exp)) + + class FakeAxis(object): def __init__(self): self.unit_data = [] class TestStrCategoryConverter(unittest.TestCase): - """ - Based on the pandas conversion and factorization tests: + """Based on the pandas conversion and factorization tests: ref: /pandas/tseries/tests/test_converter.py /pandas/tests/test_algos.py:TestFactorize @@ -28,21 +86,16 @@ def setUp(self): self.cc = cat.StrCategoryConverter() self.axis = FakeAxis() - def test_convert_accepts_unicode(self): - # possibly not needed in PY3 - self.axis.unit_data = [('a', 0), ('b', 1)] - c1 = self.cc.convert("a", None, self.axis) - c2 = self.cc.convert(u"a", None, self.axis) - self.assertEqual(c1, c2) - # single values always set at 0 - c1 = self.cc.convert(["a", "b"], None, self.axis) - c2 = self.cc.convert([u"a", u"b"], None, self.axis) - np.testing.assert_array_equal(c1, c2) + def test_convert_unicode(self): + self.axis.unit_data = [("Здравствуйте мир", 42)] + act = self.cc.convert("Здравствуйте мир", None, self.axis) + exp = 42 + self.assertEqual(act, exp) def test_convert_single(self): - self.axis.unit_data = [('a', 0)] - act = self.cc.convert("a", None, self.axis) - exp = 0 + self.axis.unit_data = [("hello world", 42)] + act = self.cc.convert("hello world", None, self.axis) + exp = 42 self.assertEqual(act, exp) def test_convert_basic(self): @@ -54,10 +107,10 @@ def test_convert_basic(self): def test_convert_mixed(self): data = ['A', 'A', np.nan, 'B', -np.inf, 3.14, np.inf] - exp = [1, 1, -1, 2, 3, 0, 4] + exp = [1, 1, -1, 2, 100, 0, 200] self.axis.unit_data = [('nan', -1), ('3.14', 0), ('A', 1), ('B', 2), - ('-inf', 3), ('inf', 4)] + ('-inf', 100), ('inf', 200)] act = self.cc.convert(data, None, self.axis) np.testing.assert_array_equal(act, exp) @@ -71,40 +124,11 @@ def test_default_units(self): self.assertEqual(self.cc.default_units(["a"], self.axis), None) -class TestMapCategories(unittest.TestCase): - def test_map_data(self): - act = cat.map_categories("a") - exp = [('a', 0)] - self.assertListEqual(act, exp) - - def test_map_data_basic(self): - data = ['a', 'b', 'b', 'a', 'a', 'c', 'c', 'c'] - exp = [('a', 0), ('b', 1), ('c', 2)] - act = cat.map_categories(data) - self.assertListEqual(act, exp) - - def test_map_data_mixed(self): - data = ['A', 'A', np.nan, 'B', -np.inf, 3.14, np.inf] - exp = [('nan', -1), ('3.14', 0), - ('A', 1), ('B', 2), ('-inf', -3), ('inf', -2)] - - act = cat.map_categories(data) - self.assertListEqual(sorted(act), sorted(exp)) - - def test_update_map(self): - data = ['b', 'd', 'e', np.inf] - old_map = [('a', 0), ('d', 1)] - exp = [('inf', -2), ('a', 0), ('d', 1), - ('b', 2), ('e', 3)] - act = cat.map_categories(data, old_map) - self.assertListEqual(sorted(act), sorted(exp)) - - class TestStrCategoryLocator(unittest.TestCase): def setUp(self): self.locs = list(range(10)) - def test_CategoricalLocator(self): + def test_StrCategoryLocator(self): ticks = cat.StrCategoryLocator(self.locs) np.testing.assert_equal(ticks.tick_values(None, None), self.locs) @@ -114,15 +138,16 @@ class TestStrCategoryFormatter(unittest.TestCase): def setUp(self): self.seq = ["hello", "world", "hi"] - def test_CategoricalFormatter(self): + def test_StrCategoryFormatter(self): labels = cat.StrCategoryFormatter(self.seq) self.assertEqual(labels('a', 1), "world") +def lt(tl): + return [l.get_text() for l in tl] + + class TestPlot(unittest.TestCase): - @classmethod - def setupClass(cls): - cls.lt = lambda tl: [l.get_text() for l in tl] def setUp(self): self.d = ['a', 'b', 'c', 'a'] @@ -136,6 +161,21 @@ def setUp(self): self.dmunit_data = [('nan', -1), ('here', 0), ('there', 1)] @cleanup + def test_plot_unicode(self): + # needs image test - works but + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1) + words = ['Здравствуйте', 'привет'] + locs = [0.0, 1.0] + ax.plot(words) + fig.canvas.draw() + + self.assertListEqual(ax.yaxis.unit_data, + list(zip(words, locs))) + np.testing.assert_array_equal(ax.get_yticks(), locs) + self.assertListEqual(lt(ax.get_yticklabels()), words) + + @cleanup def test_plot_1d(self): fig = plt.figure() ax = fig.add_subplot(1, 1, 1) @@ -143,7 +183,7 @@ def test_plot_1d(self): fig.canvas.draw() np.testing.assert_array_equal(ax.get_yticks(), self.dticks) - self.assertListEqual(TestPlot.lt(ax.get_yticklabels()), + self.assertListEqual(lt(ax.get_yticklabels()), self.dlabels) self.assertListEqual(ax.yaxis.unit_data, self.dunit_data) @@ -156,7 +196,7 @@ def test_plot_1d_missing(self): fig.canvas.draw() np.testing.assert_array_equal(ax.get_yticks(), self.dmticks) - self.assertListEqual(TestPlot.lt(ax.get_yticklabels()), + self.assertListEqual(lt(ax.get_yticklabels()), self.dmlabels) self.assertListEqual(ax.yaxis.unit_data, self.dmunit_data) @@ -169,12 +209,12 @@ def test_plot_2d(self): fig.canvas.draw() np.testing.assert_array_equal(ax.get_xticks(), self.dmticks) - self.assertListEqual(TestPlot.lt(ax.get_xticklabels()), + self.assertListEqual(lt(ax.get_xticklabels()), self.dmlabels) self.assertListEqual(ax.xaxis.unit_data, self.dmunit_data) np.testing.assert_array_equal(ax.get_yticks(), self.dticks) - self.assertListEqual(TestPlot.lt(ax.get_yticklabels()), + self.assertListEqual(lt(ax.get_yticklabels()), self.dlabels) self.assertListEqual(ax.yaxis.unit_data, self.dunit_data) @@ -187,16 +227,16 @@ def test_scatter_2d(self): fig.canvas.draw() np.testing.assert_array_equal(ax.get_xticks(), self.dmticks) - self.assertListEqual(TestPlot.lt(ax.get_xticklabels()), + self.assertListEqual(lt(ax.get_xticklabels()), self.dmlabels) self.assertListEqual(ax.xaxis.unit_data, self.dmunit_data) np.testing.assert_array_equal(ax.get_yticks(), self.dticks) - self.assertListEqual(TestPlot.lt(ax.get_yticklabels()), + self.assertListEqual(lt(ax.get_yticklabels()), self.dlabels) self.assertListEqual(ax.yaxis.unit_data, self.dunit_data) - @unittest.expectedFailure + @unittest.SkipTest @cleanup def test_plot_update(self): fig = plt.figure() @@ -212,4 +252,4 @@ def test_plot_update(self): self.assertListEqual(ax.yaxis.unit_data, list(zip(labels_new, ticks_new))) np.testing.assert_array_equal(ax.get_yticks(), ticks_new) - self.assertListEqual(TestPlot.lt(ax.get_yticklabels()), labels_new) + self.assertListEqual(lt(ax.get_yticklabels()), labels_new) diff --git a/lib/matplotlib/units.py b/lib/matplotlib/units.py index afaba10f964..c700e257627 100644 --- a/lib/matplotlib/units.py +++ b/lib/matplotlib/units.py @@ -44,8 +44,6 @@ def default_units(x, axis): from __future__ import (absolute_import, division, print_function, unicode_literals) -import six - from matplotlib.cbook import iterable, is_numlike import numpy as np @@ -126,9 +124,9 @@ def get_converter(self, x): if not len(self): return None # nothing registered - #DISABLED idx = id(x) - #DISABLED cached = self._cached.get(idx) - #DISABLED if cached is not None: return cached + # DISABLED idx = id(x) + # DISABLED cached = self._cached.get(idx) + # DISABLED if cached is not None: return cached converter = None classx = getattr(x, '__class__', None) @@ -166,7 +164,7 @@ def get_converter(self, x): converter = self.get_converter(thisx) return converter - #DISABLED self._cached[idx] = converter + # DISABLED self._cached[idx] = converter return converter