7

I've seen matplotlib examples of placing the broken axis slash marks on the axes, such as this one.

My questions, how can I place it where the bars are broken? Can this be done in a programmatic way for updating time-series plots month over month?

Below is an example of what I want done, using excel. Notice the tildes in the Paris June bar and the Madrid May bar. The tildes hide a portion of the bar it's covering.

broken axis excel example

I'm also providing easier sample data and what I've been able to do so far.

XX = pd.Series([200,400,100,1400],index=['x1','x2','x3','x4'])
fig, (ax1,ax2) = plt.subplots(2,1,sharex=True,
                         figsize=(5,6))
ax1.spines['bottom'].set_visible(False)
ax1.tick_params(axis='x',which='both',bottom=False)
ax2.spines['top'].set_visible(False)
ax2.set_ylim(0,500)
ax1.set_ylim(1200,1500)
ax1.set_yticks(np.arange(1000,1501,100))
XX.plot(ax=ax1,kind='bar')
XX.plot(ax=ax2,kind='bar')
for tick in ax2.get_xticklabels():
    tick.set_rotation(0)
d = .015  
kwargs = dict(transform=ax1.transAxes, color='k', clip_on=False)
ax1.plot((-d, +d), (-d, +d), **kwargs)      
ax1.plot((1 - d, 1 + d), (-d, +d), **kwargs)
kwargs.update(transform=ax2.transAxes)  
ax2.plot((-d, +d), (1 - d, 1 + d), **kwargs)  
ax2.plot((1 - d, 1 + d), (1 - d, 1 + d), **kwargs)
plt.show()

enter image description here

Chris
  • 149
  • 1
  • 1
  • 9
  • What exactly is the problem? The answers in the linked question show how to do it (programmatically). Is is that you want to have a wavy symbol instead of the straight lines? – ImportanceOfBeingErnest Dec 06 '18 at 01:12
  • The symbols in the linked answer are only on the axis. The Excel image I shared has it on both the axis and the bars. How can I place the slashes or tildes on the bars themselves – Chris Dec 06 '18 at 01:14
  • You would place them at every position where the bar is larger than your limit for the lower plot (in this case 8M). – ImportanceOfBeingErnest Dec 06 '18 at 01:17
  • Can you provide code for how this would be done for the sample data I just provided? When I try to add the slashes on the x4 bar, it gets hidden because of the ylimit I set. – Chris Dec 06 '18 at 04:48

2 Answers2

5

Just to show the principle, you can put the same kind of line at every position where a bar exceeds the upper limit of the lower axes and also where a bar exceeds the lower limit of the upper axes.

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

XX = pd.Series([200,400,100,1400],index=['x1','x2','x3','x4'])
fig, (ax1,ax2) = plt.subplots(2,1,sharex=True,
                         figsize=(5,6))
ax1.spines['bottom'].set_visible(False)
ax1.tick_params(axis='x',which='both',bottom=False)
ax2.spines['top'].set_visible(False)

bs = 500
ts = 1000

ax2.set_ylim(0,bs)
ax1.set_ylim(ts,1500)
ax1.set_yticks(np.arange(1000,1501,100))

bars1 = ax1.bar(XX.index, XX.values)
bars2 = ax2.bar(XX.index, XX.values)

for tick in ax2.get_xticklabels():
    tick.set_rotation(0)
d = .015  
kwargs = dict(transform=ax1.transAxes, color='k', clip_on=False)
ax1.plot((-d, +d), (-d, +d), **kwargs)      
ax1.plot((1 - d, 1 + d), (-d, +d), **kwargs)
kwargs.update(transform=ax2.transAxes)  
ax2.plot((-d, +d), (1 - d, 1 + d), **kwargs)  
ax2.plot((1 - d, 1 + d), (1 - d, 1 + d), **kwargs)

for b1, b2 in zip(bars1, bars2):
    posx = b2.get_x() + b2.get_width()/2.
    if b2.get_height() > bs:
        ax2.plot((posx-3*d, posx+3*d), (1 - d, 1 + d), color='k', clip_on=False,
                 transform=ax2.get_xaxis_transform())
    if b1.get_height() > ts:
        ax1.plot((posx-3*d, posx+3*d), (- d, + d), color='k', clip_on=False,
                 transform=ax1.get_xaxis_transform())
plt.show()

enter image description here

It doesn't look great, but can of course be adapted with a nicer shape.

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • Thank you! I can see adapting this to add the white space that should hide part of the bar – Chris Dec 06 '18 at 21:53
4

I needed broken y-axis some time ago and created some code to do it. Now, I have submitted it to PyPi so that others can use it. It's far from being a final version though. Any comments are welcome, and there is also the GitHub project, in which anyone can collaborate of course. Any issue, code and anything else is welcome.

Also, consider reading the following post. It gives some good reasons why a broken y-axis might not be desirable, before deciding on using it or not. One of the main points in my opinion is that breaking the y-axis makes the sense of proportion go away entirely. As it happens with limited y-axis ranges, it can be rather confusing to see a plot like this. That said, if used, I would recommend that the audience is made aware of the strange proportions that can arise from the use of this kind of plot.

Installing:

pip install plotinpy

Here is some example usages of the broken y-axis:

Examples

Simple broken bars

plt.figure()
pnp.plot_bars_with_breaks(
    [1, 2, 30],
    [(15, 25)]
    )
plt.savefig("img/example1.png")

Example 1

Styled broken bars

plt.figure()
pnp.plot_bars_with_breaks(
    [1, 2, 30, 1000],
    [(15, 25), (50, 975)],
    style="~~",
    break_args={"hatch": '///'}
    )
plt.savefig("img/example2.png")

Example 2

Miguel Angelo
  • 23,796
  • 16
  • 59
  • 82