From 6ef358318648d708fb8367cd8ac742e82fb913e4 Mon Sep 17 00:00:00 2001 From: Hassan Kibirige Date: Tue, 15 Mar 2016 00:20:23 -0500 Subject: [PATCH] Fix: pandas series of strings *Problem* Pandas series of strings exhibits string like behaviour and it was being treated as a single string. *Solution* When requiring and identifying a single string, make sure that the object can be hashed. This screens out the series and lets them get rightly handled as sequences. --- lib/matplotlib/cbook.py | 11 +++++++++++ lib/matplotlib/collections.py | 2 +- lib/matplotlib/colors.py | 3 ++- lib/matplotlib/tests/test_cbook.py | 8 ++++++++ lib/matplotlib/tests/test_collections.py | 2 ++ lib/matplotlib/tests/test_colors.py | 18 ++++++++++++++++++ 6 files changed, 42 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 67309a6c6101..200f72b62f1e 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -715,6 +715,17 @@ def is_sequence_of_strings(obj): return True +def is_hashable(obj): + """ + Returns true if *obj* can be hashed + """ + try: + hash(obj) + except TypeError: + return False + return True + + def is_writable_file_like(obj): 'return true if *obj* looks like a file object with a *write* method' return hasattr(obj, 'write') and six.callable(obj.write) diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 9784e0b90b70..5576def9d440 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -532,7 +532,7 @@ def set_linestyle(self, ls): The line style. """ try: - if cbook.is_string_like(ls): + if cbook.is_string_like(ls) and cbook.is_hashable(ls): ls = cbook.ls_mapper.get(ls, ls) dashes = [mlines.get_dash_pattern(ls)] elif cbook.iterable(ls): diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index 73509990d41d..043a7986571c 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -821,7 +821,8 @@ def __init__(self, colors, name='from_list', N=None): if N is None: N = len(self.colors) else: - if cbook.is_string_like(self.colors): + if (cbook.is_string_like(self.colors) and + cbook.is_hashable(self.colors)): self.colors = [self.colors] * N self.monochrome = True elif cbook.iterable(self.colors): diff --git a/lib/matplotlib/tests/test_cbook.py b/lib/matplotlib/tests/test_cbook.py index f80af5906231..26d96e6dc97c 100644 --- a/lib/matplotlib/tests/test_cbook.py +++ b/lib/matplotlib/tests/test_cbook.py @@ -48,6 +48,14 @@ def test_is_sequence_of_strings(): assert cbook.is_sequence_of_strings(y) +def test_is_hashable(): + s = 'string' + assert cbook.is_hashable(s) + + lst = ['list', 'of', 'stings'] + assert not cbook.is_hashable(lst) + + def test_restrict_dict(): d = {'foo': 'bar', 1: 2} d1 = cbook.restrict_dict(d, ['foo', 1]) diff --git a/lib/matplotlib/tests/test_collections.py b/lib/matplotlib/tests/test_collections.py index 23cc507d0447..5bffd9172ff9 100644 --- a/lib/matplotlib/tests/test_collections.py +++ b/lib/matplotlib/tests/test_collections.py @@ -629,11 +629,13 @@ def test_pandas_indexing(): index = [11, 12, 13] ec = fc = pd.Series(['red', 'blue', 'green'], index=index) lw = pd.Series([1, 2, 3], index=index) + ls = pd.Series(['solid', 'dashed', 'dashdot'], index=index) aa = pd.Series([True, False, True], index=index) Collection(edgecolors=ec) Collection(facecolors=fc) Collection(linewidths=lw) + Collection(linestyles=ls) Collection(antialiaseds=aa) diff --git a/lib/matplotlib/tests/test_colors.py b/lib/matplotlib/tests/test_colors.py index 0ac1bceff80a..db14ad17bfb4 100644 --- a/lib/matplotlib/tests/test_colors.py +++ b/lib/matplotlib/tests/test_colors.py @@ -6,9 +6,11 @@ from distutils.version import LooseVersion as V from nose.tools import assert_raises, assert_equal, assert_true +from nose.tools import assert_sequence_equal import numpy as np from numpy.testing.utils import assert_array_equal, assert_array_almost_equal +from nose.plugins.skip import SkipTest import matplotlib.colors as mcolors import matplotlib.cm as cm @@ -563,6 +565,22 @@ def _azimuth2math(azimuth, elevation): return theta, phi +def test_pandas_iterable(): + try: + import pandas as pd + except ImportError: + raise SkipTest("Pandas not installed") + + # Using a list or series yields equivalent + # color maps, i.e the series isn't seen as + # a single color + lst = ['red', 'blue', 'green'] + s = pd.Series(lst) + cm1 = mcolors.ListedColormap(lst, N=5) + cm2 = mcolors.ListedColormap(s, N=5) + assert_sequence_equal(cm1.colors, cm2.colors) + + if __name__ == '__main__': import nose nose.runmodule(argv=['-s', '--with-doctest'], exit=False)