diff --git a/seaborn/categorical.py b/seaborn/categorical.py index 11da51aa31..de1bbf7be8 100644 --- a/seaborn/categorical.py +++ b/seaborn/categorical.py @@ -392,13 +392,13 @@ def annotate_axes(self, ax): prop = mpl.font_manager.FontProperties(size=title_size) leg._legend_title_box._text.set_font_properties(prop) - def add_legend_data(self, ax, x, y, color, label): + def add_legend_data(self, ax, color, label): """Add a dummy patch object so we can get legend data.""" - rect = plt.Rectangle([x, y], 0, 0, + rect = plt.Rectangle([0, 0], 0, 0, linewidth=self.linewidth / 2, edgecolor=self.gray, facecolor=color, - label=label, zorder=-1) + label=label) ax.add_patch(rect) @@ -424,10 +424,12 @@ def draw_boxplot(self, ax, kws): for i, group_data in enumerate(self.plot_data): - if not group_data.size: - continue - if self.plot_hues is None: + + # Handle case where there is no data to plot + if not group_data.size: + continue + # Draw a single box or a set of boxes # with a single level of grouping box_data = remove_na(group_data) @@ -444,8 +446,15 @@ def draw_boxplot(self, ax, kws): offsets = self.hue_offsets for j, hue_level in enumerate(self.hue_names): hue_mask = self.plot_hues[i] == hue_level - if not hue_mask.any(): + + # Add a legend for this hue level + if not i: + self.add_legend_data(ax, self.colors[j], hue_level) + + # Handle case where there is no data to plot + if not group_data.size or not hue_mask.any(): continue + box_data = remove_na(group_data[hue_mask]) center = i + offsets[j] artist_dict = ax.boxplot(box_data, @@ -454,13 +463,8 @@ def draw_boxplot(self, ax, kws): positions=[center], widths=self.nested_width, **kws) - color = self.colors[j] - self.restyle_boxplot(artist_dict, color) + self.restyle_boxplot(artist_dict, self.colors[j]) # Add legend data, but just for one set of boxes - if not i: - self.add_legend_data(ax, center, - np.median(box_data), - color, hue_level) def restyle_boxplot(self, artist_dict, color): """Take a drawn matplotlib boxplot and make it look nice.""" @@ -575,6 +579,14 @@ def estimate_densities(self, bw, cut, scale, scale_hue, gridsize): else: for j, hue_level in enumerate(self.hue_names): + # Handle special case of no data at this category level + if not group_data.size: + support[i].append(np.array([])) + density[i].append(np.array([1.])) + counts[i, j] = 0 + max_density[i, j] = 0 + continue + # Select out the observations for this hue level hue_mask = self.plot_hues[i] == hue_level @@ -788,9 +800,7 @@ def draw_violins(self, ax): # Add legend data, but just for one set of violins if not i: - self.add_legend_data(ax, support[0], 0, - self.colors[j], - hue_level) + self.add_legend_data(ax, self.colors[j], hue_level) # Handle the special case where we have no observations if support.size == 0: diff --git a/seaborn/tests/test_categorical.py b/seaborn/tests/test_categorical.py index bdada16936..746ab4b112 100644 --- a/seaborn/tests/test_categorical.py +++ b/seaborn/tests/test_categorical.py @@ -712,6 +712,34 @@ def test_draw_missing_boxes(self): nt.assert_equal(len(ax.artists), 3) plt.close("all") + def test_boxplots(self): + + # Smoke test the high level boxplot options + + cat.boxplot("y", data=self.df) + plt.close("all") + + cat.boxplot(y="y", data=self.df) + plt.close("all") + + cat.boxplot("g", "y", data=self.df) + plt.close("all") + + cat.boxplot("y", "g", data=self.df, orient="h") + plt.close("all") + + cat.boxplot("g", "y", "h", data=self.df) + plt.close("all") + + cat.boxplot("g", "y", "h", order=list("nabc"), data=self.df) + plt.close("all") + + cat.boxplot("g", "y", "h", hue_order=list("omn"), data=self.df) + plt.close("all") + + cat.boxplot("y", "g", "h", data=self.df, orient="h") + plt.close("all") + def test_axes_annotation(self): ax = cat.boxplot("g", "y", data=self.df) @@ -1329,6 +1357,12 @@ def test_violinplots(self): cat.violinplot("g", "y", "h", data=self.df) plt.close("all") + cat.violinplot("g", "y", "h", order=list("nabc"), data=self.df) + plt.close("all") + + cat.violinplot("g", "y", "h", hue_order=list("omn"), data=self.df) + plt.close("all") + cat.violinplot("y", "g", "h", data=self.df, orient="h") plt.close("all")