diff --git a/.travis.yml b/.travis.yml index 6a69c3052713..b6277de0be76 100644 --- a/.travis.yml +++ b/.travis.yml @@ -110,7 +110,7 @@ install: # Install nose from a build which has partial # support for python36 and suport for coverage output suppressing pip install git+https://github.com/jenshnielsen/nose.git@matplotlibnose - + pip install pytest # We manually install humor sans using the package from Ubuntu 14.10. Unfortunatly humor sans is not # availible in the Ubuntu version used by Travis but we can manually install the deb from a later # version since is it basically just a .ttf file diff --git a/appveyor.yml b/appveyor.yml index 58d343641fb5..78752beedf87 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -82,7 +82,7 @@ install: # same things as the requirements in ci/conda_recipe/meta.yaml # if conda-forge gets a new pyqt, it might be nice to install it as well to have more backends # https://github.com/conda-forge/conda-forge.github.io/issues/157#issuecomment-223536381 - - cmd: conda create -q -n test-environment python=%PYTHON_VERSION% pip setuptools numpy python-dateutil freetype=2.6 msinttypes "tk=8.5" pyparsing pytz tornado "libpng>=1.6.21,<1.7" "zlib=1.2" "cycler>=0.10" nose mock + - cmd: conda create -q -n test-environment python=%PYTHON_VERSION% pip setuptools numpy python-dateutil freetype=2.6 msinttypes "tk=8.5" pyparsing pytz tornado "libpng>=1.6.21,<1.7" "zlib=1.2" "cycler>=0.10" nose mock pytest - activate test-environment - cmd: echo %PYTHON_VERSION% %TARGET_ARCH% - cmd: IF %PYTHON_VERSION% == 2.7 conda install -q functools32 diff --git a/build_alllocal.cmd b/build_alllocal.cmd deleted file mode 100644 index 9eb9ceadbc68..000000000000 --- a/build_alllocal.cmd +++ /dev/null @@ -1,36 +0,0 @@ -:: This assumes you have installed all the dependencies via conda packages: -:: # create a new environment with the required packages -:: conda create -n "matplotlib_build" python=3.4 numpy python-dateutil pyparsing pytz tornado "cycler>=0.10" tk libpng zlib freetype -:: activate matplotlib_build -:: if you want qt backend, you also have to install pyqt -:: conda install pyqt -:: # this package is only available in the conda-forge channel -:: conda install -c conda-forge msinttypes -:: if you build on py2.7: -:: conda install -c conda-forge functools32 - -set TARGET=bdist_wheel -IF [%1]==[] ( - echo Using default target: %TARGET% -) else ( - set TARGET=%1 - echo Using user supplied target: %TARGET% -) - -IF NOT DEFINED CONDA_PREFIX ( - echo No Conda env activated: you need to create a conda env with the right packages and activate it! - GOTO:eof -) - -:: copy the libs which have "wrong" names -set LIBRARY_LIB=%CONDA_PREFIX%\Library\lib -mkdir lib || cmd /c "exit /b 0" -copy %LIBRARY_LIB%\zlibstatic.lib lib\z.lib -copy %LIBRARY_LIB%\libpng_static.lib lib\png.lib - -:: Make the header files and the rest of the static libs available during the build -:: CONDA_PREFIX is a env variable which is set to the currently active environment path -set MPLBASEDIRLIST=%CONDA_PREFIX%\Library\;. - -:: build the target -python setup.py %TARGET% diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 276c09605a70..cbccdb695489 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -642,7 +642,7 @@ def __init__(self, axes, pickradius=15): self.offsetText = self._get_offset_text() self.majorTicks = [] self.minorTicks = [] - self.unit_data = [] + self.unit_data = None self.pickradius = pickradius # Initialize here for testing; later add API @@ -695,14 +695,14 @@ def limit_range_for_scale(self, vmin, vmax): @property def unit_data(self): - """Holds data that a ConversionInterface subclass relys on + """Holds data that a ConversionInterface subclass uses to convert between labels and indexes """ return self._unit_data @unit_data.setter - def unit_data(self, data): - self._unit_data = data + def unit_data(self, unit_data): + self._unit_data = unit_data def get_children(self): children = [self.label, self.offsetText] diff --git a/lib/matplotlib/category.py b/lib/matplotlib/category.py index bfac242149c3..4abdf4b86a4b 100644 --- a/lib/matplotlib/category.py +++ b/lib/matplotlib/category.py @@ -9,9 +9,10 @@ import numpy as np -import matplotlib.units as units -import matplotlib.ticker as ticker - +import matplotlib.colors as mcolors +import matplotlib.cbook as cbook +import matplotlib.units as munits +import matplotlib.ticker as mticker # pure hack for numpy 1.6 support from distutils.version import LooseVersion @@ -22,54 +23,148 @@ def to_array(data, maxlen=100): if NP_NEW: return np.array(data, dtype=np.unicode) + if cbook.is_scalar_or_string(data): + data = [data] try: vals = np.array(data, dtype=('|S', maxlen)) except UnicodeEncodeError: - # pure hack + # this yields gibberish vals = np.array([convert_to_string(d) for d in data]) return vals -class StrCategoryConverter(units.ConversionInterface): +class StrCategoryConverter(munits.ConversionInterface): + """Converts categorical (or string) data to numerical values + + Conversion typically happens in the following order: + 1. default_units: + create unit_data category-integer mapping and binds to axis + 2. axis_info: + set ticks/locator and labels/formatter + 3. convert: + map input category data to integers using unit_data + """ @staticmethod def convert(value, unit, axis): - """Uses axis.unit_data map to encode - data as floats """ - vmap = dict(axis.unit_data) + Encode value as floats using axis.unit_data + """ + vmap = dict(zip(axis.unit_data.seq, axis.unit_data.locs)) if isinstance(value, six.string_types): - return vmap[value] + return vmap.get(value, None) vals = to_array(value) - for lab, loc in axis.unit_data: + for lab, loc in vmap.items(): vals[vals == lab] = loc - return vals.astype('float') + return vals.astype('float64') @staticmethod def axisinfo(unit, axis): - seq, locs = zip(*axis.unit_data) - majloc = StrCategoryLocator(locs) - majfmt = StrCategoryFormatter(seq) - return units.AxisInfo(majloc=majloc, majfmt=majfmt) + """ + Return the :class:`~matplotlib.units.AxisInfo` for *unit*. + + *unit* is None + *axis.unit_data* is used to set ticks and labels + """ + majloc = StrCategoryLocator(axis.unit_data.locs) + majfmt = StrCategoryFormatter(axis.unit_data.seq) + return munits.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 default_units(data, axis, sort=True, normed=False): + """ + Create mapping between string categories in *data* + and integers, and store in *axis.unit_data* + """ + if axis and axis.unit_data: + axis.unit_data.update(data, sort) + return axis.unit_data + + unit_data = UnitData(data, sort) + if axis: + axis.unit_data = unit_data + return unit_data -class StrCategoryLocator(ticker.FixedLocator): +class StrCategoryLocator(mticker.FixedLocator): + """ + Ensures that every category has a tick by subclassing + :class:`~matplotlib.ticker.FixedLocator` + """ def __init__(self, locs): - super(StrCategoryLocator, self).__init__(locs, None) + self.locs = locs + self.nbins = None -class StrCategoryFormatter(ticker.FixedFormatter): +class StrCategoryFormatter(mticker.FixedFormatter): + """ + Labels every category by subclassing + :class:`~matplotlib.ticker.FixedFormatter` + """ def __init__(self, seq): - super(StrCategoryFormatter, self).__init__(seq) + self.seq = seq + self.offset_string = '' + + +class CategoryNorm(mcolors.Normalize): + """ + Preserves ordering of discrete values + """ + def __init__(self, data): + """ + *categories* + distinct values for mapping + + Out-of-range values are mapped to np.nan + """ + + self.units = StrCategoryConverter() + self.unit_data = None + self.units.default_units(data, + self, sort=False) + self.loc2seq = dict(zip(self.unit_data.locs, self.unit_data.seq)) + self.vmin = min(self.unit_data.locs) + self.vmax = max(self.unit_data.locs) + + def __call__(self, value, clip=None): + # gonna have to go into imshow and undo casting + value = np.asarray(value, dtype=np.int) + ret = self.units.convert(value, None, self) + # knock out values not in the norm + mask = np.in1d(ret, self.unit_data.locs).reshape(ret.shape) + # normalize ret & locs + ret /= self.vmax + return np.ma.array(ret, mask=~mask) + + def inverse(self, value): + if not cbook.iterable(value): + value = np.asarray(value) + vscaled = np.asarray(value) * self.vmax + return [self.loc2seq[int(vs)] for vs in vscaled] + + +def colors_from_categories(codings): + """ + Helper routine to generate a cmap and a norm from a list + of (color, value) pairs + + Parameters + ---------- + codings : sequence of (key, value) pairs + + Returns + ------- + (cmap, norm) : tuple containing a :class:`Colormap` and a \ + :class:`Normalize` instance + """ + if isinstance(codings, dict): + codings = cbook.sanitize_sequence(codings.items()) + values, colors = zip(*codings) + cmap = mcolors.ListedColormap(list(colors)) + norm = CategoryNorm(list(values)) + return cmap, norm def convert_to_string(value): @@ -77,8 +172,8 @@ def convert_to_string(value): np.array(...,dtype=unicode) for all later versions of numpy""" if isinstance(value, six.string_types): - return value - if np.isfinite(value): + pass + elif np.isfinite(value): value = np.asarray(value, dtype=str)[np.newaxis][0] elif np.isnan(value): value = 'nan' @@ -91,61 +186,53 @@ def convert_to_string(value): return value -def map_categories(data, old_map=None): - """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 +class UnitData(object): + # debatable if it makes sense to special code missing values spdict = {'nan': -1.0, 'inf': -2.0, '-inf': -3.0} - if isinstance(data, six.string_types): - data = [data] - - # will update this post cbook/dict support - strdata = to_array(data) - uniq = np.unique(strdata) - - if old_map: - olabs, okeys = zip(*old_map) - svalue = max(okeys) + 1 - else: - old_map, olabs, okeys = [], [], [] - svalue = 0 - - 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()] - - category_map.extend([(m, spdict[m]) for m in missing]) - - new_labs = [nl for nl in new_labs if nl not in missing] - - new_locs = np.arange(svalue, svalue + len(new_labs), dtype='float') - category_map.extend(list(zip(new_labs, new_locs))) - return category_map - + def __init__(self, data, sort=True): + """Create mapping between unique categorical values + and numerical identifier + Paramters + --------- + data: iterable + sequence of values + sort: bool + sort input data, default is True + False preserves input order + """ + self.seq, self.locs = [], [] + self._set_seq_locs(data, 0, sort) + self.sort = sort + + def update(self, new_data, sort=True): + if sort: + self.sort = sort + # so as not to conflict with spdict + value = max(max(self.locs) + 1, 0) + self._set_seq_locs(new_data, value, self.sort) + + def _set_seq_locs(self, data, value, sort): + # magic to make it work under np1.6 + strdata = to_array(data) + + # np.unique makes dateframes work + if sort: + unq = np.unique(strdata) + else: + _, idx = np.unique(strdata, return_index=~sort) + unq = strdata[np.sort(idx)] + + new_s = [d for d in unq if d not in self.seq] + for ns in new_s: + self.seq.append(convert_to_string(ns)) + if ns in UnitData.spdict.keys(): + self.locs.append(UnitData.spdict[ns]) + else: + self.locs.append(value) + value += 1 # Connects the convertor to matplotlib -units.registry[str] = StrCategoryConverter() -units.registry[bytes] = StrCategoryConverter() -units.registry[six.text_type] = StrCategoryConverter() +munits.registry[str] = StrCategoryConverter() +munits.registry[bytes] = StrCategoryConverter() +munits.registry[six.text_type] = StrCategoryConverter() diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index 67cdae563d52..dff73fbc519a 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -30,6 +30,7 @@ import matplotlib as mpl import matplotlib.artist as martist +import matplotlib.category as category import matplotlib.cbook as cbook import matplotlib.collections as collections import matplotlib.colors as colors @@ -312,6 +313,8 @@ def __init__(self, ax, cmap=None, if format is None: if isinstance(self.norm, colors.LogNorm): self.formatter = ticker.LogFormatterMathtext() + elif isinstance(self.norm, category.CategoryNorm): + self.formatter = ticker.FixedFormatter(self.norm.unit_data.seq) else: self.formatter = ticker.ScalarFormatter() elif cbook.is_string_like(format): @@ -580,6 +583,8 @@ def _ticker(self): locator = ticker.FixedLocator(b, nbins=10) elif isinstance(self.norm, colors.LogNorm): locator = ticker.LogLocator() + elif isinstance(self.norm, category.CategoryNorm): + locator = ticker.FixedLocator(self.norm.unit_data.locs) else: if mpl.rcParams['_internal.classic_mode']: locator = ticker.MaxNLocator() diff --git a/lib/matplotlib/tests/test_category.py b/lib/matplotlib/tests/test_category.py index 02db774e4ff6..cdf8af1f51a1 100644 --- a/lib/matplotlib/tests/test_category.py +++ b/lib/matplotlib/tests/test_category.py @@ -2,254 +2,254 @@ """Catch all for categorical functions""" from __future__ import (absolute_import, division, print_function, unicode_literals) -import unittest +import pytest import numpy as np import matplotlib.pyplot as plt from matplotlib.testing.decorators import cleanup import matplotlib.category as cat +import unittest -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("Здравствуйте мир")) +class TestConvertToString(object): + testdata = [("abc", "abc"), ("Здравствуйте мир", "Здравствуйте мир"), + ("3.14", 3.14), ("nan", np.nan), + ("inf", np.inf), ("-inf", -np.inf)] + ids = ["string", "unicode", "decimal", "nan", "posinf", "neginf", ] - def test_decimal(self): - self.assertEqual("3.14", cat.convert_to_string(3.14)) + @pytest.mark.parametrize("expected, test", testdata, ids=ids) + def test_convert_to_string(self, expected, test): + assert expected == cat.convert_to_string(test) - 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)) +class TestUnitData(object): + testdata = [("hello world", ["hello world"], [0]), + ("Здравствуйте мир", ["Здравствуйте мир"], [0]), + (['A', 'A', np.nan, 'B', -np.inf, 3.14, np.inf], + ['-inf', '3.14', 'A', 'B', 'inf', 'nan'], + [-3.0, 0, 1, 2, -2.0, -1.0])] - def test_neginf(self): - self.assertEqual("-inf", cat.convert_to_string(-np.inf)) + ids = ["single", "unicode", "mixed"] + @pytest.mark.parametrize("data, seq, locs", testdata, ids=ids) + def test_unit(self, data, seq, locs): + act = cat.UnitData(data) + assert act.seq == seq + assert act.locs == locs -class TestMapCategories(unittest.TestCase): - def test_map_unicode(self): - act = cat.map_categories("Здравствуйте мир") - exp = [("Здравствуйте мир", 0)] - self.assertListEqual(act, exp) + def test_update_map(self): + data = ['a', 'd'] + oseq = ['a', 'd'] + olocs = [0, 1] - def test_map_data(self): - act = cat.map_categories("hello world") - exp = [("hello world", 0)] - self.assertListEqual(act, exp) + data_update = ['b', 'd', 'e', np.inf] + useq = ['a', 'd', 'b', 'e', 'inf'] + ulocs = [0, 1, 2, 3, -2] - 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)) + unitdata = cat.UnitData(data) + assert unitdata.seq == oseq + assert unitdata.locs == olocs - 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)] + unitdata.update(data_update) + assert unitdata.seq == useq + assert unitdata.locs == ulocs - 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, unit_data): + self.unit_data = unit_data -class FakeAxis(object): - def __init__(self): - self.unit_data = [] +class MockUnitData(object): + def __init__(self, data): + seq, locs = zip(*data) + self.seq = list(seq) + self.locs = list(locs) -class TestStrCategoryConverter(unittest.TestCase): +class TestStrCategoryConverter(object): """Based on the pandas conversion and factorization tests: ref: /pandas/tseries/tests/test_converter.py /pandas/tests/test_algos.py:TestFactorize """ - - def setUp(self): + testdata = [("Здравствуйте мир", [("Здравствуйте мир", 42)], 42), + ("hello world", [("hello world", 42)], 42), + (['a', 'b', 'b', 'a', 'a', 'c', 'c', 'c'], + [('a', 0), ('b', 1), ('c', 2)], + [0, 1, 1, 0, 0, 2, 2, 2]), + (['A', 'A', np.nan, 'B', -np.inf, 3.14, np.inf], + [('nan', -1), ('3.14', 0), ('A', 1), ('B', 2), + ('-inf', 100), ('inf', 200)], + [1, 1, -1, 2, 100, 0, 200])] + ids = ["unicode", "single", "basic", "mixed"] + + @pytest.fixture(autouse=True) + def mock_axis(self, request): self.cc = cat.StrCategoryConverter() - self.axis = FakeAxis() - - 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 = [("hello world", 42)] - act = self.cc.convert("hello world", None, self.axis) - exp = 42 - 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, 100, 0, 200] - self.axis.unit_data = [('nan', -1), ('3.14', 0), - ('A', 1), ('B', 2), - ('-inf', 100), ('inf', 200)] - act = self.cc.convert(data, None, self.axis) + @pytest.mark.parametrize("data, unitmap, exp", testdata, ids=ids) + def test_convert(self, data, unitmap, exp): + MUD = MockUnitData(unitmap) + axis = FakeAxis(MUD) + act = self.cc.convert(data, None, 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)) + MUD = MockUnitData([(None, None)]) + axis = FakeAxis(MUD) + ax = self.cc.axisinfo(None, axis) + assert isinstance(ax.majloc, cat.StrCategoryLocator) + assert isinstance(ax.majfmt, cat.StrCategoryFormatter) def test_default_units(self): - self.assertEqual(self.cc.default_units(["a"], self.axis), None) + axis = FakeAxis(None) + assert isinstance(self.cc.default_units(["a"], axis), cat.UnitData) -class TestStrCategoryLocator(unittest.TestCase): - def setUp(self): - self.locs = list(range(10)) - +class TestStrCategoryLocator(object): def test_StrCategoryLocator(self): - ticks = cat.StrCategoryLocator(self.locs) - np.testing.assert_equal(ticks.tick_values(None, None), - self.locs) + locs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + ticks = cat.StrCategoryLocator(locs) + np.testing.assert_array_equal(ticks.tick_values(None, None), locs) class TestStrCategoryFormatter(unittest.TestCase): - def setUp(self): - self.seq = ["hello", "world", "hi"] - def test_StrCategoryFormatter(self): - labels = cat.StrCategoryFormatter(self.seq) - self.assertEqual(labels('a', 1), "world") + seq = ["hello", "world", "hi"] + labels = cat.StrCategoryFormatter(seq) + assert labels('a', 1) == "world" + + def test_StrCategoryFormatterUnicode(self): + seq = ["Здравствуйте", "привет"] + labels = cat.StrCategoryFormatter(seq) + assert labels('a', 1) == "привет" + + +class TestCategoryNorm(object): + testdata = [[[205, 302, 205, 101], [0, 1, 0, .5]], + [[205, np.nan, 101, 305], [0, np.nan, .5, 1]], + [[205, 101, 504, 101], [0, .5, np.nan, .5]]] + + ids = ["regular", "nan", "exclude"] + + @pytest.mark.parametrize("data, nmap", testdata, ids=ids) + def test_norm(self, data, nmap): + norm = cat.CategoryNorm([205, 101, 302]) + masked_nmap = np.ma.masked_equal(nmap, np.nan) + assert np.ma.allequal(norm(data), masked_nmap) + + def test_invert(self): + data = [205, 302, 101] + strdata = ['205', '302', '101'] + value = [0, .5, 1] + norm = cat.CategoryNorm(data) + assert norm.inverse(value) == strdata + + +class TestColorsFromCategories(object): + testdata = [[{'101': "blue", '205': "red", '302': "green"}, dict], + [[('205', "red"), ('101', "blue"), ('302', "green")], list]] + ids = ["dict", "tuple"] + + @pytest.mark.parametrize("codings, mtype", testdata, ids=ids) + def test_colors_from_categories(self, codings, mtype): + cmap, norm = cat.colors_from_categories(codings) + assert mtype(zip(norm.unit_data.seq, cmap.colors)) == codings def lt(tl): return [l.get_text() for l in tl] -class TestPlot(unittest.TestCase): - - def setUp(self): +class TestPlot(object): + @pytest.fixture + def data(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)] + unitmap = [('a', 0), ('b', 1), ('c', 2)] + self.dunit_data = MockUnitData(unitmap) + @pytest.fixture + def missing_data(self): 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)] + self.dmticks = [0, -1, 1] + self.dmlabels = ['here', 'nan', 'there'] + unitmap = [('here', 0), ('nan', -1), ('there', 1)] + self.dmunit_data = MockUnitData(unitmap) + + def axis_test(self, axis, ticks, labels, unit_data): + np.testing.assert_array_equal(axis.get_majorticklocs(), ticks) + assert lt(axis.get_majorticklabels()) == labels + np.testing.assert_array_equal(axis.unit_data.locs, unit_data.locs) + assert axis.unit_data.seq == unit_data.seq @cleanup def test_plot_unicode(self): - # needs image test - works but - fig = plt.figure() - ax = fig.add_subplot(1, 1, 1) + # Image test would fail on numpy 1.6 words = ['Здравствуйте', 'привет'] locs = [0.0, 1.0] + unit_data = MockUnitData(zip(words, locs)) + + fig, ax = plt.subplots() 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) + self.axis_test(ax.yaxis, locs, words, unit_data) @cleanup + @pytest.mark.usefixtures("data") def test_plot_1d(self): - fig = plt.figure() - ax = fig.add_subplot(1, 1, 1) + fig, ax = plt.subplots() ax.plot(self.d) fig.canvas.draw() - np.testing.assert_array_equal(ax.get_yticks(), self.dticks) - self.assertListEqual(lt(ax.get_yticklabels()), - self.dlabels) - self.assertListEqual(ax.yaxis.unit_data, self.dunit_data) + self.axis_test(ax.yaxis, self.dticks, self.dlabels, self.dunit_data) @cleanup + @pytest.mark.usefixtures("missing_data") def test_plot_1d_missing(self): - - fig = plt.figure() - ax = fig.add_subplot(1, 1, 1) + fig, ax = plt.subplots() ax.plot(self.dm) fig.canvas.draw() - np.testing.assert_array_equal(ax.get_yticks(), self.dmticks) - self.assertListEqual(lt(ax.get_yticklabels()), - self.dmlabels) - self.assertListEqual(ax.yaxis.unit_data, self.dmunit_data) + self.axis_test(ax.yaxis, self.dmticks, self.dmlabels, self.dmunit_data) @cleanup + @pytest.mark.usefixtures("data", "missing_data") def test_plot_2d(self): - - fig = plt.figure() - ax = fig.add_subplot(1, 1, 1) + fig, ax = plt.subplots() ax.plot(self.dm, self.d) fig.canvas.draw() - np.testing.assert_array_equal(ax.get_xticks(), self.dmticks) - 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(lt(ax.get_yticklabels()), - self.dlabels) - self.assertListEqual(ax.yaxis.unit_data, self.dunit_data) + self.axis_test(ax.xaxis, self.dmticks, self.dmlabels, self.dmunit_data) + self.axis_test(ax.yaxis, self.dticks, self.dlabels, self.dunit_data) @cleanup + @pytest.mark.usefixtures("data", "missing_data") def test_scatter_2d(self): - fig = plt.figure() - ax = fig.add_subplot(1, 1, 1) + fig, ax = plt.subplots() ax.scatter(self.dm, self.d) fig.canvas.draw() - np.testing.assert_array_equal(ax.get_xticks(), self.dmticks) - 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(lt(ax.get_yticklabels()), - self.dlabels) - self.assertListEqual(ax.yaxis.unit_data, self.dunit_data) + self.axis_test(ax.xaxis, self.dmticks, self.dmlabels, self.dmunit_data) + self.axis_test(ax.yaxis, self.dticks, self.dlabels, self.dunit_data) - @unittest.SkipTest @cleanup def test_plot_update(self): - fig = plt.figure() - ax = fig.add_subplot(1, 1, 1) + fig, ax = plt.subplots() 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(lt(ax.get_yticklabels()), labels_new) + labels = ['a', 'b', 'd', 'c'] + ticks = [0, 1, 2, 3] + unit_data = MockUnitData(list(zip(labels, ticks))) + + self.axis_test(ax.yaxis, ticks, labels, unit_data) diff --git a/tox.ini b/tox.ini index 296cefb56281..f1d3bc8669ca 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, py31, py32 +envlist = py26, py27, py31, py32, py35 [testenv] changedir = /tmp @@ -15,3 +15,4 @@ deps = nose mock numpy + pytest \ No newline at end of file pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy