diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 45a72009e5dd..eb941666832c 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -2950,11 +2950,11 @@ def xywhere(xs, ys, mask): return errorbar_container # (l0, caplines, barcols) - def boxplot(self, x, notch=False, sym=None, vert=True, whis=1.5, - positions=None, widths=None, patch_artist=False, + def boxplot(self, x, notch=None, sym=None, vert=None, whis=None, + positions=None, widths=None, patch_artist=None, bootstrap=None, usermedians=None, conf_intervals=None, - meanline=False, showmeans=False, showcaps=True, - showbox=True, showfliers=True, boxprops=None, labels=None, + meanline=None, showmeans=None, showcaps=None, + showbox=None, showfliers=None, boxprops=None, labels=None, flierprops=None, medianprops=None, meanprops=None, capprops=None, whiskerprops=None, manage_xticks=True): """ @@ -2962,7 +2962,7 @@ def boxplot(self, x, notch=False, sym=None, vert=True, whis=1.5, Call signature:: - boxplot(self, x, notch=False, sym='b+', vert=True, whis=1.5, + boxplot(self, x, notch=None, sym=None, vert=None, whis=None, positions=None, widths=None, patch_artist=False, bootstrap=None, usermedians=None, conf_intervals=None, meanline=False, showmeans=False, showcaps=True, @@ -3114,11 +3114,59 @@ def boxplot(self, x, notch=False, sym=None, vert=True, whis=1.5, .. plot:: mpl_examples/statistics/boxplot_demo.py """ + # If defined in matplotlibrc, apply the value from rc file + # Overridden if argument is passed + if whis is None: + whis = rcParams['boxplot.whiskers'] + if bootstrap is None: + bootstrap = rcParams['boxplot.bootstrap'] bxpstats = cbook.boxplot_stats(x, whis=whis, bootstrap=bootstrap, labels=labels) - # make sure we have a dictionary - if flierprops is None: - flierprops = dict() + if notch is None: + notch = rcParams['boxplot.notch'] + if vert is None: + vert = rcParams['boxplot.vertical'] + if patch_artist is None: + patch_artist = rcParams['boxplot.patchartist'] + if meanline is None: + meanline = rcParams['boxplot.meanline'] + if showmeans is None: + showmeans = rcParams['boxplot.showmeans'] + if showcaps is None: + showcaps = rcParams['boxplot.showcaps'] + if showbox is None: + showbox = rcParams['boxplot.showbox'] + if showfliers is None: + showfliers = rcParams['boxplot.showfliers'] + + def _update_dict(dictionary, rc_name, properties): + """ Loads properties in the dictionary from rc file if not already + in the dictionary""" + rc_str = 'boxplot.{0}.{1}' + if dictionary is None: + dictionary = dict() + for prop_dict in properties: + dictionary.setdefault(prop_dict, + rcParams[rc_str.format(rc_name, prop_dict)]) + return dictionary + + # Common property dictionnaries loading from rc + flier_props = ['color', 'marker', 'markerfacecolor', 'markeredgecolor', + 'markersize', 'linestyle', 'linewidth'] + default_props = ['color', 'linewidth', 'linestyle'] + + boxprops = _update_dict(boxprops, 'boxprops', default_props) + whiskerprops = _update_dict(whiskerprops, 'whiskerprops', + default_props) + capprops = _update_dict(capprops, 'capprops', default_props) + medianprops = _update_dict(medianprops, 'medianprops', default_props) + meanprops = _update_dict(meanprops, 'meanprops', default_props) + flierprops = _update_dict(flierprops, 'flierprops', flier_props) + + if patch_artist: + boxprops['linestyle'] = 'solid' + boxprops['edgecolor'] = boxprops.pop('color') + # if non-default sym value, put it into the flier dictionary # the logic for providing the default symbol ('b+') now lives # in bxp in the initial value of final_flierprops @@ -3162,8 +3210,8 @@ def boxplot(self, x, notch=False, sym=None, vert=True, whis=1.5, if conf_intervals is not None: if np.shape(conf_intervals)[0] != len(bxpstats): - raise ValueError('conf_intervals length not ' - 'compatible with x') + err_mess = 'conf_intervals length not compatible with x' + raise ValueError(err_mess) else: for stats, ci in zip(bxpstats, conf_intervals): if ci is not None: @@ -3680,7 +3728,7 @@ def scatter(self, x, y, s=20, c=None, marker='o', cmap=None, norm=None, if facecolors is not None: c = facecolors else: - c = 'b' # the original default + c = 'b' # The original default self._process_unit_info(xdata=x, ydata=y, kwargs=kwargs) x = self.convert_xunits(x) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 9040a507a910..6b03e64da507 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -921,7 +921,9 @@ def cla(self): self.collections = [] # collection.Collection instances self.containers = [] - self.grid(self._gridOn, which=rcParams['axes.grid.which']) + self.grid(False) # Disable grid on init to use rcParameter + self.grid(self._gridOn, which=rcParams['axes.grid.which'], + axis=rcParams['axes.grid.axis']) props = font_manager.FontProperties( size=rcParams['axes.titlesize'], weight=rcParams['axes.titleweight'] diff --git a/lib/matplotlib/mpl-data/stylelib/bmh.mplstyle b/lib/matplotlib/mpl-data/stylelib/bmh.mplstyle index 721511297559..56f2b0ed55a0 100644 --- a/lib/matplotlib/mpl-data/stylelib/bmh.mplstyle +++ b/lib/matplotlib/mpl-data/stylelib/bmh.mplstyle @@ -1,5 +1,5 @@ #Author: Cameron Davidson-Pilon, original styles from Bayesian Methods for Hackers -# https://github.com/CamDavidsonPilon/Probabilistic-Programming-and-Bayesian-Methods-for-Hackers/ +# https://github.com/CamDavidsonPilon/Probabilistic-Programming-and-Bayesian-Methods-for-Hackers/ lines.linewidth : 2.0 @@ -19,4 +19,5 @@ axes.titlesize: x-large axes.labelsize: large axes.color_cycle: 348ABD, A60628, 7A68A6, 467821, D55E00, CC79A7, 56B4E9, 009E73, F0E442, 0072B2 -legend.fancybox: True \ No newline at end of file +legend.fancybox: True + diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index dafd352137d2..d3b3c88607d4 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -139,6 +139,16 @@ def validate_int(s): except ValueError: raise ValueError('Could not convert "%s" to int' % s) +def validate_int_or_None(s): + """if not None, tries to validate as an int""" + if s=='None': + s = None + if s is None: + return None + try: + return int(s) + except ValueError: + raise ValueError('Could not convert "%s" to int' % s) def validate_fonttype(s): """ @@ -352,6 +362,22 @@ def validate_font_properties(s): 'verbose', ['silent', 'helpful', 'debug', 'debug-annoying']) +def validate_whiskers(s): + if s=='range': + return 'range' + else: + try: + v = validate_nseq_float(2)(s) + return v + except: + try: + v = float(s) + return v + except: + err_str = ("Not a valid whisker value ['range'," + "float, (float, float)]") + raise ValueError(err_str) + def deprecate_savefig_extension(value): warnings.warn("savefig.extension is deprecated. Use savefig.format " @@ -471,7 +497,7 @@ def validate_hinting(s): validate_movie_frame_fmt = ValidateInStrings('animation.frame_format', ['png', 'jpeg', 'tiff', 'raw', 'rgba']) -validate_axis_locator = ValidateInStrings('major', ['minor','both','major']) +validate_axis_locator = ValidateInStrings('major', ['minor', 'both', 'major']) def validate_bbox(s): if isinstance(s, six.string_types): @@ -530,6 +556,7 @@ def __call__(self, s): (self.vmax, s)) return s +validate_grid_axis = ValidateInStrings('axes.grid.axis', ['x', 'y', 'both']) # a map from key -> value, converter defaultParams = { @@ -574,6 +601,45 @@ def __call__(self, s): 'patch.facecolor': ['b', validate_color], # blue 'patch.antialiased': [True, validate_bool], # antialised (no jaggies) + ## Boxplot properties + 'boxplot.notch': [False, validate_bool], + 'boxplot.vertical': [True, validate_bool], + 'boxplot.whiskers': [1.5, validate_whiskers], + 'boxplot.bootstrap': [None, validate_int_or_None], + 'boxplot.patchartist': [False, validate_bool], + 'boxplot.showmeans': [False, validate_bool], + 'boxplot.showcaps': [True, validate_bool], + 'boxplot.showbox': [True, validate_bool], + 'boxplot.showfliers': [True, validate_bool], + 'boxplot.meanline': [False, validate_bool], + + 'boxplot.flierprops.color': ['b', validate_color], + 'boxplot.flierprops.marker': ['+', six.text_type], + 'boxplot.flierprops.markerfacecolor': ['b', validate_color], + 'boxplot.flierprops.markeredgecolor': ['k', validate_color], + 'boxplot.flierprops.markersize': [6, validate_float], + 'boxplot.flierprops.linestyle': ['-', six.text_type], + 'boxplot.flierprops.linewidth': [1.0, validate_float], + + 'boxplot.boxprops.color': ['b', validate_color], + 'boxplot.boxprops.linewidth': [1.0, validate_float], + 'boxplot.boxprops.linestyle': ['-', six.text_type], + + 'boxplot.whiskerprops.color': ['b', validate_color], + 'boxplot.whiskerprops.linewidth': [1.0, validate_float], + 'boxplot.whiskerprops.linestyle': ['--', six.text_type], + + 'boxplot.capprops.color': ['k', validate_color], + 'boxplot.capprops.linewidth': [1.0, validate_float], + 'boxplot.capprops.linestyle': ['-', six.text_type], + + 'boxplot.medianprops.color': ['r', validate_color], + 'boxplot.medianprops.linewidth': [1.0, validate_float], + 'boxplot.medianprops.linestyle': ['-', six.text_type], + + 'boxplot.meanprops.color': ['r', validate_color], + 'boxplot.meanprops.linewidth': [1.0, validate_float], + 'boxplot.meanprops.linestyle': ['-', six.text_type], ## font props 'font.family': [['sans-serif'], validate_stringlist], # used by text object @@ -648,6 +714,12 @@ def __call__(self, s): 'axes.facecolor': ['w', validate_color], # background color; white 'axes.edgecolor': ['k', validate_color], # edge color; black 'axes.linewidth': [1.0, validate_float], # edge linewidth + + 'axes.spines.left': [True, validate_bool], # Set visibility of axes + 'axes.spines.right': [True, validate_bool], # 'spines', the lines + 'axes.spines.bottom': [True, validate_bool], # around the chart + 'axes.spines.top': [True, validate_bool], # denoting data boundary + 'axes.titlesize': ['large', validate_fontsize], # fontsize of the # axes title 'axes.titleweight': ['normal', six.text_type], # font weight of axes title @@ -656,6 +728,8 @@ def __call__(self, s): # default draw on 'major' # 'minor' or 'both' kind of # axis locator + 'axes.grid.axis': ['both', validate_grid_axis], # grid type. + # Can be 'x', 'y', 'both' 'axes.labelsize': ['medium', validate_fontsize], # fontsize of the # x any y labels 'axes.labelpad': [5.0, validate_float], # space between label and axis diff --git a/lib/matplotlib/spines.py b/lib/matplotlib/spines.py index 0d1f707844d5..a68245086c31 100644 --- a/lib/matplotlib/spines.py +++ b/lib/matplotlib/spines.py @@ -467,6 +467,8 @@ def linear_spine(cls, axes, spine_type, **kwargs): else: raise ValueError('unable to make path for spine "%s"' % spine_type) result = cls(axes, spine_type, path, **kwargs) + result.set_visible(rcParams['axes.spines.{0}'.format(spine_type)]) + return result @classmethod diff --git a/lib/matplotlib/tests/baseline_images/test_axes/boxplot_rc_parameters.pdf b/lib/matplotlib/tests/baseline_images/test_axes/boxplot_rc_parameters.pdf new file mode 100644 index 000000000000..8664d3a51d08 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/boxplot_rc_parameters.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/boxplot_rc_parameters.png b/lib/matplotlib/tests/baseline_images/test_axes/boxplot_rc_parameters.png new file mode 100644 index 000000000000..db1f522be5f7 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/boxplot_rc_parameters.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/boxplot_rc_parameters.svg b/lib/matplotlib/tests/baseline_images/test_axes/boxplot_rc_parameters.svg new file mode 100644 index 000000000000..e0fa85524626 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_axes/boxplot_rc_parameters.svg @@ -0,0 +1,1169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_axes/rc_grid.png b/lib/matplotlib/tests/baseline_images/test_axes/rc_grid.png new file mode 100644 index 000000000000..27eb8b259db9 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/rc_grid.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/rc_spines.png b/lib/matplotlib/tests/baseline_images/test_axes/rc_spines.png new file mode 100644 index 000000000000..ee0a23a207a6 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/rc_spines.png differ diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 6d9d2adff210..8971c84c7ba6 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -1,8 +1,8 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) -from matplotlib.externals import six -from matplotlib.externals.six.moves import xrange +import six +from six.moves import xrange import io @@ -1691,6 +1691,73 @@ def test_boxplot_autorange_whiskers(): ax.boxplot([x, x], bootstrap=10000, notch=1) ax.set_ylim((-5, 5)) +def _rc_test_bxp_helper(ax, rc_dict): + x = np.linspace(-7, 7, 140) + x = np.hstack([-25, x, 25]) + with matplotlib.rc_context(rc_dict): + ax.boxplot([x, x]) + return ax + +@image_comparison(baseline_images=['boxplot_rc_parameters'], + savefig_kwarg={'dpi': 100}, remove_text=True) +def test_boxplot_rc_parameters(): + fig, ax = plt.subplots(3) + + rc_axis0 = { + 'boxplot.notch':True, + 'boxplot.whiskers': [5, 95], + 'boxplot.bootstrap': 10000, + + 'boxplot.flierprops.color': 'b', + 'boxplot.flierprops.marker': 'o', + 'boxplot.flierprops.markerfacecolor': 'g', + 'boxplot.flierprops.markeredgecolor': 'b', + 'boxplot.flierprops.markersize': 5, + 'boxplot.flierprops.linestyle': '--', + 'boxplot.flierprops.linewidth': 2.0, + + 'boxplot.boxprops.color': 'r', + 'boxplot.boxprops.linewidth': 2.0, + 'boxplot.boxprops.linestyle': '--', + + 'boxplot.capprops.color': 'c', + 'boxplot.capprops.linewidth': 2.0, + 'boxplot.capprops.linestyle': '--', + + 'boxplot.medianprops.color': 'k', + 'boxplot.medianprops.linewidth': 2.0, + 'boxplot.medianprops.linestyle': '--', + } + + rc_axis1 = { + 'boxplot.vertical': False, + 'boxplot.whiskers': 'range', + 'boxplot.patchartist': True, + } + + rc_axis2 = { + 'boxplot.whiskers': 2.0, + 'boxplot.showcaps': False, + 'boxplot.showbox': False, + 'boxplot.showfliers': False, + 'boxplot.showmeans': True, + 'boxplot.meanline': True, + + 'boxplot.meanprops.color': 'c', + 'boxplot.meanprops.linewidth': 2.0, + 'boxplot.meanprops.linestyle': '--', + + 'boxplot.whiskerprops.color': 'r', + 'boxplot.whiskerprops.linewidth': 2.0, + 'boxplot.whiskerprops.linestyle': '-.', + } + dict_list = [rc_axis0, rc_axis1, rc_axis2] + for axis, rc_axis in zip(ax, dict_list): + _rc_test_bxp_helper(axis, rc_axis) + + assert (matplotlib.patches.PathPatch in + [type(t) for t in ax[1].get_children()]) + @image_comparison(baseline_images=['boxplot_with_CIarray'], remove_text=True, extensions=['png'], @@ -3718,7 +3785,37 @@ def test_move_offsetlabel(): ax.yaxis.tick_right() assert_equal((1, 0.5), ax.yaxis.offsetText.get_position()) - +@image_comparison(baseline_images=['rc_spines'], extensions=['png'], + savefig_kwarg={'dpi':40}) +def test_rc_spines(): + rc_dict = { + 'axes.spines.left':False, + 'axes.spines.right':False, + 'axes.spines.top':False, + 'axes.spines.bottom':False} + with matplotlib.rc_context(rc_dict): + fig, ax = plt.subplots() + +@image_comparison(baseline_images=['rc_grid'], extensions=['png'], + savefig_kwarg={'dpi':40}) +def test_rc_grid(): + fig = plt.figure() + rc_dict0 = { + 'axes.grid.axis': 'both' + } + rc_dict1 = { + 'axes.grid.axis': 'x' + } + rc_dict2 = { + 'axes.grid.axis': 'y' + } + dict_list = [rc_dict0, rc_dict1, rc_dict2] + + i=1 + for rc_dict in dict_list: + with matplotlib.rc_context(rc_dict): + fig.add_subplot(3, 1, i) + i += 1 @cleanup def test_bar_negative_width(): fig, ax = plt.subplots()