Skip to content

COMPAT: Support for MPL 1.5 #11145

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 22, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ci/install_conda.sh
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ bash miniconda.sh -b -p $HOME/miniconda || exit 1

conda config --set always_yes yes --set changeps1 no || exit 1
conda update -q conda || exit 1
conda config --add channels conda-forge || exit 1
conda config --add channels http://conda.binstar.org/pandas || exit 1
conda config --set ssl_verify false || exit 1

Expand Down
1 change: 0 additions & 1 deletion ci/requirements-3.4.run
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ beautiful-soup
scipy
numexpr
pytables
matplotlib=1.3.1
lxml
sqlalchemy
bottleneck
Expand Down
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.17.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Highlights include:
- Documentation comparing SAS to *pandas*, see :ref:`here <compare_with_sas>`
- Removal of the automatic TimeSeries broadcasting, deprecated since 0.8.0, see :ref:`here <whatsnew_0170.prior_deprecations>`
- Compatibility with Python 3.5 (:issue:`11097`)
- Compatibility with matplotlib 1.5.0 (:issue:`11111`)

Check the :ref:`API Changes <whatsnew_0170.api>` and :ref:`deprecations <whatsnew_0170.deprecations>` before updating.

Expand Down
210 changes: 142 additions & 68 deletions pandas/tests/test_graphics.py

Large diffs are not rendered by default.

57 changes: 32 additions & 25 deletions pandas/tests/test_graphics_others.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,22 +161,22 @@ def test_plot_fails_when_ax_differs_from_figure(self):
@slow
def test_autocorrelation_plot(self):
from pandas.tools.plotting import autocorrelation_plot
_check_plot_works(autocorrelation_plot, self.ts)
_check_plot_works(autocorrelation_plot, self.ts.values)
_check_plot_works(autocorrelation_plot, series=self.ts)
_check_plot_works(autocorrelation_plot, series=self.ts.values)

ax = autocorrelation_plot(self.ts, label='Test')
self._check_legend_labels(ax, labels=['Test'])

@slow
def test_lag_plot(self):
from pandas.tools.plotting import lag_plot
_check_plot_works(lag_plot, self.ts)
_check_plot_works(lag_plot, self.ts, lag=5)
_check_plot_works(lag_plot, series=self.ts)
_check_plot_works(lag_plot, series=self.ts, lag=5)

@slow
def test_bootstrap_plot(self):
from pandas.tools.plotting import bootstrap_plot
_check_plot_works(bootstrap_plot, self.ts, size=10)
_check_plot_works(bootstrap_plot, series=self.ts, size=10)


@tm.mplskip
Expand Down Expand Up @@ -210,7 +210,7 @@ def test_boxplot_legacy(self):
_check_plot_works(df.boxplot, column='one', by=['indic', 'indic2'])
_check_plot_works(df.boxplot, by='indic')
_check_plot_works(df.boxplot, by=['indic', 'indic2'])
_check_plot_works(plotting.boxplot, df['one'], return_type='dict')
_check_plot_works(plotting.boxplot, data=df['one'], return_type='dict')
_check_plot_works(df.boxplot, notch=1, return_type='dict')
_check_plot_works(df.boxplot, by='indic', notch=1)

Expand Down Expand Up @@ -304,6 +304,7 @@ def test_boxplot_empty_column(self):

@slow
def test_hist_df_legacy(self):
from matplotlib.patches import Rectangle
_check_plot_works(self.hist_df.hist)

# make sure layout is handled
Expand Down Expand Up @@ -347,7 +348,8 @@ def test_hist_df_legacy(self):
# make sure kwargs to hist are handled
ax = ser.hist(normed=True, cumulative=True, bins=4)
# height of last bin (index 5) must be 1.0
self.assertAlmostEqual(ax.get_children()[5].get_height(), 1.0)
rects = [x for x in ax.get_children() if isinstance(x, Rectangle)]
self.assertAlmostEqual(rects[-1].get_height(), 1.0)

tm.close()
ax = ser.hist(log=True)
Expand Down Expand Up @@ -413,9 +415,9 @@ def scat(**kwds):
def scat2(x, y, by=None, ax=None, figsize=None):
return plotting.scatter_plot(df, x, y, by, ax, figsize=None)

_check_plot_works(scat2, 0, 1)
_check_plot_works(scat2, x=0, y=1)
grouper = Series(np.repeat([1, 2, 3, 4, 5], 20), df.index)
_check_plot_works(scat2, 0, 1, by=grouper)
_check_plot_works(scat2, x=0, y=1, by=grouper)

def test_scatter_matrix_axis(self):
tm._skip_if_no_scipy()
Expand All @@ -424,15 +426,17 @@ def test_scatter_matrix_axis(self):
with tm.RNGContext(42):
df = DataFrame(randn(100, 3))

axes = _check_plot_works(scatter_matrix, df, range_padding=.1)
axes = _check_plot_works(scatter_matrix, filterwarnings='always', frame=df,
range_padding=.1)
axes0_labels = axes[0][0].yaxis.get_majorticklabels()
# GH 5662
expected = ['-2', '-1', '0', '1', '2']
self._check_text_labels(axes0_labels, expected)
self._check_ticks_props(axes, xlabelsize=8, xrot=90, ylabelsize=8, yrot=0)

df[0] = ((df[0] - 2) / 3)
axes = _check_plot_works(scatter_matrix, df, range_padding=.1)
axes = _check_plot_works(scatter_matrix, filterwarnings='always', frame=df,
range_padding=.1)
axes0_labels = axes[0][0].yaxis.get_majorticklabels()
expected = ['-1.2', '-1.0', '-0.8', '-0.6', '-0.4', '-0.2', '0.0']
self._check_text_labels(axes0_labels, expected)
Expand All @@ -445,17 +449,17 @@ def test_andrews_curves(self):

df = self.iris

_check_plot_works(andrews_curves, df, 'Name')
_check_plot_works(andrews_curves, frame=df, class_column='Name')

rgba = ('#556270', '#4ECDC4', '#C7F464')
ax = _check_plot_works(andrews_curves, df, 'Name', color=rgba)
ax = _check_plot_works(andrews_curves, frame=df, class_column='Name', color=rgba)
self._check_colors(ax.get_lines()[:10], linecolors=rgba, mapping=df['Name'][:10])

cnames = ['dodgerblue', 'aquamarine', 'seagreen']
ax = _check_plot_works(andrews_curves, df, 'Name', color=cnames)
ax = _check_plot_works(andrews_curves, frame=df, class_column='Name', color=cnames)
self._check_colors(ax.get_lines()[:10], linecolors=cnames, mapping=df['Name'][:10])

ax = _check_plot_works(andrews_curves, df, 'Name', colormap=cm.jet)
ax = _check_plot_works(andrews_curves, frame=df, class_column='Name', colormap=cm.jet)
cmaps = lmap(cm.jet, np.linspace(0, 1, df['Name'].nunique()))
self._check_colors(ax.get_lines()[:10], linecolors=cmaps, mapping=df['Name'][:10])

Expand All @@ -478,23 +482,23 @@ def test_parallel_coordinates(self):

df = self.iris

ax = _check_plot_works(parallel_coordinates, df, 'Name')
ax = _check_plot_works(parallel_coordinates, frame=df, class_column='Name')
nlines = len(ax.get_lines())
nxticks = len(ax.xaxis.get_ticklabels())

rgba = ('#556270', '#4ECDC4', '#C7F464')
ax = _check_plot_works(parallel_coordinates, df, 'Name', color=rgba)
ax = _check_plot_works(parallel_coordinates, frame=df, class_column='Name', color=rgba)
self._check_colors(ax.get_lines()[:10], linecolors=rgba, mapping=df['Name'][:10])

cnames = ['dodgerblue', 'aquamarine', 'seagreen']
ax = _check_plot_works(parallel_coordinates, df, 'Name', color=cnames)
ax = _check_plot_works(parallel_coordinates, frame=df, class_column='Name', color=cnames)
self._check_colors(ax.get_lines()[:10], linecolors=cnames, mapping=df['Name'][:10])

ax = _check_plot_works(parallel_coordinates, df, 'Name', colormap=cm.jet)
ax = _check_plot_works(parallel_coordinates, frame=df, class_column='Name', colormap=cm.jet)
cmaps = lmap(cm.jet, np.linspace(0, 1, df['Name'].nunique()))
self._check_colors(ax.get_lines()[:10], linecolors=cmaps, mapping=df['Name'][:10])

ax = _check_plot_works(parallel_coordinates, df, 'Name', axvlines=False)
ax = _check_plot_works(parallel_coordinates, frame=df, class_column='Name', axvlines=False)
assert len(ax.get_lines()) == (nlines - nxticks)

colors = ['b', 'g', 'r']
Expand All @@ -517,20 +521,20 @@ def test_radviz(self):
from matplotlib import cm

df = self.iris
_check_plot_works(radviz, df, 'Name')
_check_plot_works(radviz, frame=df, class_column='Name')

rgba = ('#556270', '#4ECDC4', '#C7F464')
ax = _check_plot_works(radviz, df, 'Name', color=rgba)
ax = _check_plot_works(radviz, frame=df, class_column='Name', color=rgba)
# skip Circle drawn as ticks
patches = [p for p in ax.patches[:20] if p.get_label() != '']
self._check_colors(patches[:10], facecolors=rgba, mapping=df['Name'][:10])

cnames = ['dodgerblue', 'aquamarine', 'seagreen']
_check_plot_works(radviz, df, 'Name', color=cnames)
_check_plot_works(radviz, frame=df, class_column='Name', color=cnames)
patches = [p for p in ax.patches[:20] if p.get_label() != '']
self._check_colors(patches, facecolors=cnames, mapping=df['Name'][:10])

_check_plot_works(radviz, df, 'Name', colormap=cm.jet)
_check_plot_works(radviz, frame=df, class_column='Name', colormap=cm.jet)
cmaps = lmap(cm.jet, np.linspace(0, 1, df['Name'].nunique()))
patches = [p for p in ax.patches[:20] if p.get_label() != '']
self._check_colors(patches, facecolors=cmaps, mapping=df['Name'][:10])
Expand Down Expand Up @@ -607,6 +611,8 @@ def test_grouped_plot_fignums(self):

@slow
def test_grouped_hist_legacy(self):
from matplotlib.patches import Rectangle

df = DataFrame(randn(500, 2), columns=['A', 'B'])
df['C'] = np.random.randint(0, 4, 500)
df['D'] = ['X'] * 500
Expand All @@ -633,7 +639,8 @@ def test_grouped_hist_legacy(self):
xlabelsize=xf, xrot=xrot, ylabelsize=yf, yrot=yrot)
# height of last bin (index 5) must be 1.0
for ax in axes.ravel():
height = ax.get_children()[5].get_height()
rects = [x for x in ax.get_children() if isinstance(x, Rectangle)]
height = rects[-1].get_height()
self.assertAlmostEqual(height, 1.0)
self._check_ticks_props(axes, xlabelsize=xf, xrot=xrot,
ylabelsize=yf, yrot=yrot)
Expand Down
78 changes: 68 additions & 10 deletions pandas/tools/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@
from pandas.compat import range, lrange, lmap, map, zip, string_types
import pandas.compat as compat
from pandas.util.decorators import Appender

try: # mpl optional
import pandas.tseries.converter as conv
conv.register() # needs to override so set_xlim works with str/number
except ImportError:
pass


# Extracted from https://gist.github.com/huyng/816622
# this is the rcParams set when setting display.with_mpl_style
# to True.
Expand Down Expand Up @@ -97,6 +97,48 @@
'ytick.minor.size': 0.0
}


def _mpl_le_1_2_1():
try:
import matplotlib as mpl
return (str(mpl.__version__) <= LooseVersion('1.2.1') and
str(mpl.__version__)[0] != '0')
except ImportError:
return False

def _mpl_ge_1_3_1():
try:
import matplotlib
# The or v[0] == '0' is because their versioneer is
# messed up on dev
return (matplotlib.__version__ >= LooseVersion('1.3.1')
or matplotlib.__version__[0] == '0')
except ImportError:
return False

def _mpl_ge_1_4_0():
try:
import matplotlib
return (matplotlib.__version__ >= LooseVersion('1.4')
or matplotlib.__version__[0] == '0')
except ImportError:
return False

def _mpl_ge_1_5_0():
try:
import matplotlib
return (matplotlib.__version__ >= LooseVersion('1.5')
or matplotlib.__version__[0] == '0')
except ImportError:
return False

if _mpl_ge_1_5_0():
# Compat with mp 1.5, which uses cycler.
import cycler
colors = mpl_stylesheet.pop('axes.color_cycle')
mpl_stylesheet['axes.prop_cycle'] = cycler.cycler('color_cycle', colors)


def _get_standard_kind(kind):
return {'density': 'kde'}.get(kind, kind)

Expand Down Expand Up @@ -784,7 +826,6 @@ def _kind(self):
_layout_type = 'vertical'
_default_rot = 0
orientation = None

_pop_attributes = ['label', 'style', 'logy', 'logx', 'loglog',
'mark_right', 'stacked']
_attr_defaults = {'logy': False, 'logx': False, 'loglog': False,
Expand Down Expand Up @@ -973,8 +1014,9 @@ def _maybe_right_yaxis(self, ax, axes_num):
else:
# otherwise, create twin axes
orig_ax, new_ax = ax, ax.twinx()
new_ax._get_lines.color_cycle = orig_ax._get_lines.color_cycle

# TODO: use Matplotlib public API when available
new_ax._get_lines = orig_ax._get_lines
new_ax._get_patches_for_fill = orig_ax._get_patches_for_fill
orig_ax.right_ax, new_ax.left_ax = new_ax, orig_ax

if not self._has_plotted_object(orig_ax): # no data on left y
Expand Down Expand Up @@ -1197,6 +1239,14 @@ def plt(self):
import matplotlib.pyplot as plt
return plt

@staticmethod
def mpl_ge_1_3_1():
return _mpl_ge_1_3_1()

@staticmethod
def mpl_ge_1_5_0():
return _mpl_ge_1_5_0()

_need_to_set_index = False

def _get_xticks(self, convert_period=False):
Expand Down Expand Up @@ -1474,9 +1524,6 @@ def __init__(self, data, x, y, s=None, c=None, **kwargs):
self.c = c

def _make_plot(self):
import matplotlib as mpl
mpl_ge_1_3_1 = str(mpl.__version__) >= LooseVersion('1.3.1')

x, y, c, data = self.x, self.y, self.c, self.data
ax = self.axes[0]

Expand All @@ -1488,9 +1535,13 @@ def _make_plot(self):
# pandas uses colormap, matplotlib uses cmap.
cmap = self.colormap or 'Greys'
cmap = self.plt.cm.get_cmap(cmap)

if c is None:
color = self.kwds.pop("color", None)
if c is not None and color is not None:
raise TypeError('Specify exactly one of `c` and `color`')
elif c is None and color is None:
c_values = self.plt.rcParams['patch.facecolor']
elif color is not None:
c_values = color
elif c_is_column:
c_values = self.data[c].values
else:
Expand All @@ -1505,7 +1556,7 @@ def _make_plot(self):
if cb:
img = ax.collections[0]
kws = dict(ax=ax)
if mpl_ge_1_3_1:
if self.mpl_ge_1_3_1():
kws['label'] = c if c_is_column else ''
self.fig.colorbar(img, **kws)

Expand Down Expand Up @@ -1636,6 +1687,11 @@ def _ts_plot(cls, ax, x, data, style=None, **kwds):

# Set ax with freq info
_decorate_axes(ax, freq, kwds)
# digging deeper
if hasattr(ax, 'left_ax'):
_decorate_axes(ax.left_ax, freq, kwds)
if hasattr(ax, 'right_ax'):
_decorate_axes(ax.right_ax, freq, kwds)
ax._plot_data.append((data, cls._kind, kwds))

lines = cls._plot(ax, data.index, data.values, style=style, **kwds)
Expand Down Expand Up @@ -1743,6 +1799,8 @@ def _plot(cls, ax, x, y, style=None, column_num=None,
if not 'color' in kwds:
kwds['color'] = lines[0].get_color()

if cls.mpl_ge_1_5_0(): # mpl 1.5 added real support for poly legends
kwds.pop('label')
ax.fill_between(xdata, start, y_values, **kwds)
cls._update_stacker(ax, stacking_id, y)
return lines
Expand Down
4 changes: 4 additions & 0 deletions pandas/tseries/tests/test_plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,7 @@ def test_gaps(self):
ts[5:25] = np.nan
ax = ts.plot()
lines = ax.get_lines()
tm._skip_if_mpl_1_5()
self.assertEqual(len(lines), 1)
l = lines[0]
data = l.get_xydata()
Expand Down Expand Up @@ -532,6 +533,9 @@ def test_gap_upsample(self):
self.assertEqual(len(ax.right_ax.get_lines()), 1)
l = lines[0]
data = l.get_xydata()

tm._skip_if_mpl_1_5()

tm.assertIsInstance(data, np.ma.core.MaskedArray)
mask = data.mask
self.assertTrue(mask[5:25, 1].all())
Expand Down
8 changes: 8 additions & 0 deletions pandas/util/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,14 @@ def setUpClass(cls):
return cls


def _skip_if_mpl_1_5():
import matplotlib
v = matplotlib.__version__
if v > LooseVersion('1.4.3') or v[0] == '0':
import nose
raise nose.SkipTest("matplotlib 1.5")


def _skip_if_no_scipy():
try:
import scipy.stats
Expand Down