Why is a column of categorical data not correctly plotted with its associated values?

Viewed 107
  • If a column is set as categorical with pd.Categorical, the categorical column is not correctly plotted with the associated value, when using plt.plot, as shown below.
  • Anybody know how to avoid this sort of thing? Is this a bug, or am I the problem?
import pandas as pd
import matplotlib.pyplot as plt

labels = ["b", "b", "a", "b", "a"]
values = [10, 11, 1, 12, 2]
df = pd.DataFrame([{"label": label, "value": v} for label, v in zip(labels, values)])
df.label = pd.Categorical(df.label)

The dataframe looks like this:

>>> print(df)
  label  value
0     b     10
1     b     11
2     a      1
3     b     12
4     a      2

But when I plot the dataframe with matplotlib.pyplot.plot, the labels "a" and "b" are mixed up:

>>> plt.plot(df.label, df.value, "o")

link to plot cause I don't have enough karma to embed images

Compare the (correct) output of plt.scatter(df.label, df.value):

link to correct plot

The problem goes away when swapping the labels "a" and "b", so it looks like plt.plot sorts the labels before plotting. But why would it do that? It works fine when the labels are strings; the problem only arises because df.labels is categorical.

1 Answers

Update 2020-08-04

  • python 3.8.5
  • pandas 1.1.1
  • matplotlib 3.3.1
  • JupyterLab 2.2.6

I can't reproduce the original issue with the new versions.

enter image description here

Initial Answer

  • Reported as a bug:
  • Setting label as a category dtype with the either of the following, results in the issue.
    • df.label = pd.Categorical(df.label)
    • df.label = pd.Categorical(df.label, categories=["a", "b"], ordered=True)
  • Sorting the label column seems to be a workaround for the issue.
    • As noted in a comment, plt.plot(df.label.values, df.value.values, "o") will also produce the correct plot.
import pandas as pd
import matplotlib.pyplot as plt

labels = ["b", "b", "a", "b", "a"]
values = [10, 11, 1, 12, 2]
df = pd.DataFrame([{"label": label, "value": v} for label, v in zip(labels, values)])

# categorical
df.label = pd.Categorical(df.label, categories=["a", "b"], ordered=True)

# sort
df.sort_values('label', inplace=True)

# plot
plt.plot('label', 'value', 'o', data=df)

enter image description here

Related