Skip to content

Commit 8583391

Browse files
committed
Respect pandas categorical order in category plots
This will include levels that appear in the `category` list, but that do not appear in the data. See #361
1 parent f5334fc commit 8583391

File tree

4 files changed

+69
-16
lines changed

4 files changed

+69
-16
lines changed

seaborn/categorical.py

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@
1313
from .external.six.moves import range
1414

1515
from . import utils
16-
from .utils import desaturate, iqr
16+
from .utils import desaturate, iqr, categorical_order
1717
from .algorithms import bootstrap
1818
from .palettes import color_palette, husl_palette, light_palette
19+
from .axisgrid import FacetGrid
1920

2021

2122
class _CategoricalPlotter(object):
@@ -189,7 +190,7 @@ def establish_variables(self, x=None, y=None, hue=None, data=None,
189190
group_label = groups.name
190191

191192
# Get the order on the categorical axis
192-
group_names = self._category_order(groups, order)
193+
group_names = categorical_order(groups, order)
193194

194195
# Group the numeric data
195196
plot_data, value_label = self._group_longform(vals, groups,
@@ -203,7 +204,7 @@ def establish_variables(self, x=None, y=None, hue=None, data=None,
203204
else:
204205

205206
# Get the order of the hue levels
206-
hue_names = self._category_order(hue, hue_order)
207+
hue_names = categorical_order(hue, hue_order)
207208

208209
# Group the hue data
209210
plot_hues, hue_title = self._group_longform(hue, groups,
@@ -228,15 +229,6 @@ def establish_variables(self, x=None, y=None, hue=None, data=None,
228229
self.hue_names = hue_names
229230
self.plot_units = plot_units
230231

231-
def _category_order(self, data, order):
232-
"""Get the order of levels for a categorical variable."""
233-
if order is None:
234-
try:
235-
order = data.unique()
236-
except AttributeError:
237-
order = pd.unique(data)
238-
return list(order)
239-
240232
def _group_longform(self, vals, grouper, order):
241233
"""Group a long-form variable by another with correct order."""
242234
# Ensure that the groupby will work
@@ -2050,10 +2042,6 @@ def barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None,
20502042
kwargs.pop("dropna")
20512043
warn = True
20522044

2053-
if "label" in kwargs:
2054-
kwargs.pop("label")
2055-
warn = True
2056-
20572045
if "x_order" in kwargs:
20582046
order = kwargs.pop("x_order")
20592047
warn = True

seaborn/tests/test_categorical.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,11 @@ def test_order(self):
290290
for group, vals in zip(["c", "b", "a"], p.plot_data):
291291
npt.assert_array_equal(vals, self.y[self.g == group])
292292

293+
df.g = (df.g.cat.add_categories("d")
294+
.cat.reorder_categories(["c", "b", "d", "a"]))
295+
p.establish_variables("g", "y", data=df)
296+
nt.assert_equal(p.group_names, ["c", "b", "d", "a"])
297+
293298
def test_hue_order(self):
294299

295300
p = cat._CategoricalPlotter()
@@ -311,6 +316,11 @@ def test_hue_order(self):
311316
p.establish_variables("g", "y", "h", data=df)
312317
nt.assert_equal(p.hue_names, ["n", "m"])
313318

319+
df.h = (df.h.cat.add_categories("o")
320+
.cat.reorder_categories(["o", "m", "n"]))
321+
p.establish_variables("g", "y", "h", data=df)
322+
nt.assert_equal(p.hue_names, ["o", "m", "n"])
323+
314324
def test_plot_units(self):
315325

316326
p = cat._CategoricalPlotter()

seaborn/tests/test_utils.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@
22
import warnings
33

44
import numpy as np
5+
import pandas as pd
56
import matplotlib.pyplot as plt
67
from numpy.testing import assert_array_equal
78
import nose
89
import nose.tools as nt
910
from nose.tools import assert_equal, raises
1011

12+
from distutils.version import LooseVersion
13+
pandas_has_categoricals = LooseVersion(pd.__version__) >= "0.15"
14+
1115
from .. import utils, rcmod
1216

1317

@@ -244,3 +248,36 @@ def test_ticklabels_overlap():
244248
x, y = utils.axes_ticklabels_overlap(ax)
245249
assert x
246250
assert not y
251+
252+
253+
def test_category_order():
254+
255+
x = ["a", "c", "c", "b", "a", "d"]
256+
order = ["a", "b", "c", "d"]
257+
258+
out = utils.categorical_order(x)
259+
nt.assert_equal(out, ["a", "c", "b", "d"])
260+
261+
out = utils.categorical_order(x, order)
262+
nt.assert_equal(out, order)
263+
264+
out = utils.categorical_order(x, ["b", "a"])
265+
nt.assert_equal(out, ["b", "a"])
266+
267+
out = utils.categorical_order(np.array(x))
268+
nt.assert_equal(out, ["a", "c", "b", "d"])
269+
270+
out = utils.categorical_order(pd.Series(x))
271+
nt.assert_equal(out, ["a", "c", "b", "d"])
272+
273+
if pandas_has_categoricals:
274+
x = pd.Categorical(x, order)
275+
out = utils.categorical_order(x)
276+
nt.assert_equal(out, list(x.categories))
277+
278+
x = pd.Series(x)
279+
out = utils.categorical_order(x)
280+
nt.assert_equal(out, list(x.cat.categories))
281+
282+
out = utils.categorical_order(x, ["b", "a"])
283+
nt.assert_equal(out, ["b", "a"])

seaborn/utils.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,3 +414,21 @@ def axes_ticklabels_overlap(ax):
414414
"""
415415
return (axis_ticklabels_overlap(ax.get_xticklabels()),
416416
axis_ticklabels_overlap(ax.get_yticklabels()))
417+
418+
419+
def categorical_order(values, order=None):
420+
"""Return a list of unique data values.
421+
422+
"""
423+
if order is None:
424+
if hasattr(values, "cat"):
425+
order = values.cat.categories
426+
elif hasattr(values, "categories"):
427+
order = values.categories
428+
else:
429+
try:
430+
order = values.unique()
431+
except AttributeError:
432+
order = pd.unique(values)
433+
434+
return list(order)

0 commit comments

Comments
 (0)