1

I have a multi-level indexed dataframe that I am trying to display in Seaborn. The plot is showing up fine, but the values of the x-axis are being treated as text labels instead of actual x-values. The snippet below shows how sample data is made and plotted:

>>> import numpy, pandas, seaborn
>>> from matplotlib import pyplot
>>> index = pandas.MultiIndex.from_product((list('abc'), [10**x for x in range(4)]), names=['letters', 'powers'])
>>> index
MultiIndex(levels=[['a', 'b', 'c'], [1, 10, 100, 1000]],
           labels=[[0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2], [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]],
           names=['letters', 'powers'])

>>> df = pandas.DataFrame(numpy.random.randn(12, 2), index=index, columns=['x', 't'])
>>> df
                       x         t
letters powers                    
a       1       1.764052  0.400157
        10      0.978738  2.240893
        100     1.867558 -0.977278
        1000    0.950088 -0.151357
b       1      -0.103219  0.410599
        10      0.144044  1.454274
        100     0.761038  0.121675
        1000    0.443863  0.333674
c       1       1.494079 -0.205158
        10      0.313068 -0.854096
        100    -2.552990  0.653619
        1000    0.864436 -0.742165

>>> seaborn.factorplot(x='powers', y='t', hue='letters', data=df.reset_index())
>>> pyplot.show()

The plot shows up:

plot of sample data

However, the x-axis is using the numerical values as text labels. I would like the x-axis to show an exponential progression as expected from the values (i.e., 1000 should be 10 times farther from 100 than 100 is from 10). How can I fix that?

I suspect that the multi-index is not relevant to the problem, but perhaps the datatype it is being interpreted as is significant. A similar issue seems to be happening here: seaborn boxplots at desired distances along the x axis. I do not think it is a duplicate, but if the community disagrees, I would appreciate a brief explanation of how to apply it to my case.

Community
  • 1
  • 1
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264

1 Answers1

5

factorplot is treating your [1, 10, 100, 1000] as categories (or factors). Those are not numbers for seaborn - just labels. That's why they are spaced evenly (and internally it places those labels on a linear spaced scale from 0 to 3). The side effect from this is that it mimics the log-scaled representation, which you might want to keep.

If I understand correctly what are trying to do, this can be achieved without seaborn, but if it is styling you are after you can still import it and do something like this afterwards:

fig, ax = plt.subplots(figsize=(5,3))

for l in df.index.get_level_values(0).unique():
    ax.plot(df.loc[l, 'x'], 'o-', label=l)
ax.legend(loc=0)
ax.set_xlim([-10, 1001])
ax.set_xticks(df.index.get_level_values(1).unique())

Which will produce chart like this:

enter image description here

And I am not sure this is really what you need since representing linear scale on x-axis is making left side unreadable. You current chart has appearance of a 'log' scaled x-axis, which seems to be a more readable representation.

Primer
  • 10,092
  • 5
  • 43
  • 55
  • Thanks. This is exactly what I was looking for. This way `ax.set_*scale` can be applied at will. In case you were wondering, I have some data that was acquired at quasi-logarithmic intervals but should show a linear relationship on a regular plot. Once I verify the linear relationship, I will probably take your advice and do a semilogx or log-log plot if the linear one is inadequate. – Mad Physicist Apr 03 '16 at 02:06
  • By the way, am I reading your the part about importing correctly? Does importing seaborn modify the defaults of subsequent regular matplotlib plots? – Mad Physicist Apr 03 '16 at 02:08
  • 1
    That's right - when you `import seaborn` it will overwrite matplotlib defaults for that session or until you change those settings manually. – Primer Apr 03 '16 at 07:48
  • That is also exactly what I was looking for. Do you know if `import seaborn` would work within a context manager like `matplotlib.rc_context` as expected? I.e., would the matplotlib defaults return when the `with` block terminated if the import was inside it? – Mad Physicist Apr 04 '16 at 03:27
  • 1
    My understanding is that whenever you call `with` block it will revert to whatever state it has picked up `matplotlib` settings at. Which means it you imported seaborn before - you can overwrite it temporarily and then revert back to whatever state importing seaborn brought it. – Primer Apr 04 '16 at 12:47
  • I meant what if you do `with matplotlib.rc_context(): import seaborn; ...`? – Mad Physicist Apr 04 '16 at 14:54
  • 1
    The same rule should apply. All changes from `import seaborn` should be reverted after `with` block ends. – Primer Apr 05 '16 at 07:10