Double X-axis bar chart without sliding?

Hi,
This is my first week of using Plotly so I am having some difficulty in getting exactly what I want if possible, so here I am.

I would like to have a stacked bar next to an unstacked bar chart with the labels under each bar chart as seen in the first screenshot.

I also would like to have another x-axis above called rooms with the room numbers above to linked them to each unstacked and stacked bar grouped together as seen in the second screenshot.

For example, I would like to have an unstacked bar and stacked bar together under a room as room β€˜201’

If anyone has any input in adjusting the chart as I’d like, I would greatly appreciate it.

HI @njohnson welcome to the forums. That’s actually a interesting question. My first thought seeing this chart was a marimekko chart. I have not used it up to now yet.

@njohnson

  1. To get a bar plot that contains both stacked and unstacked bars, you should use the
    base, the offset and the bar width. In this case don’t set barmode in layout!!!

  2. You can associate a second xaxis only if you are referencing a trace to it,
    but since you are referencing the bars to the bottom xaxis,
    you must put the upper ticklabels as annotations.

import plotly.graph_objects as go

unstacked = go.Bar(
            y= [37, 25, 21, 17, 14], 
            base = 0, #all bars in this trace have base at y=0
            width = 0.45,# the width must be less than 1, and such that two adjacent bars have width sum <=1
            offset = 0, #The first bar starts at x=0, the second at 1, etc
            textposition='auto',
            texttemplate= '%{y}')

stackedbottom = go.Bar(
    y=[24,10, 9, 11],
    base=0, 
    width = 0.45,
    offset = 0.45, #the first bar starts at 0.45, the second at 1.45, etc
    textposition='auto',
    texttemplate= '%{y}'
)

stackedtop = go.Bar(
    y=[13, 15, 12, 0, 8], 
    width = 0.45,
    offset = 0.45,
    base=[24, 10, 9, 0, 0],#24, 10, 9 are the previous three y vals to ensure  stacking, and 0, 0 to put bars on base
    textposition='auto',
    texttemplate= '%{y}')
fig= go.Figure([unstacked, stackedbottom, stackedtop])
fig.update_layout(xaxis_tickvals=[0.45, 1.45, 2.45, 3.45, 4.45], 
                  xaxis_ticktext=['A', 'B', 'C', 'D', 'E'],
                  xaxis_range=[-0.2, 5])
fig.show()

fig2=go.Figure([go.Bar(y=[8, 9, 10], base=0, width=0.8, offset=0),
              go.Bar(y=[3, 4, 0], base=[8,9,10], width=0.8, offset=0)])
fig2.update_layout(xaxis_range=[-0.2, 3], xaxis_tickvals=[0.4, 1.4, 2.4],
                  xaxis_ticktext=[101, 102, 103],
                  annotations=[dict(x=X, y=Y, text=t, xref='x', yref='paper',
                showarrow=False) for X, Y, t in zip([0.4, 1.4, 2.4], [1.07]*3, [201, 202, 203])])        
fig2.show()
2 Likes

I was able to get this result thanks to you.

import plotly.graph_objects as go

unstacked = go.Bar(
            y= [37, 25, 21, 17, 14], 
            base = 0, #all bars in this trace have base at y=0
            width = 0.45,# the width must be less than 1, and such that two adjacent bars have width sum <=1
            offset = 0, #The first bar starts at x=0, the second at 1, etc
            textposition='auto',
            texttemplate= '%{y}')

stackedbottom = go.Bar(
    y=[24,10, 9, 11],
    base=0, 
    width = 0.45,
    offset = 0.45, #the first bar starts at 0.45, the second at 1.45, etc
    textposition='auto',
    texttemplate= '%{y}'
)

stackedtop = go.Bar(
    y=[13, 15, 12, 0, 8], 
    width = 0.45,
    offset = 0.45,
    base=[24, 10, 9, 0, 0],#24, 10, 9 are the previous three y vals to ensure  stacking, and 0, 0 to put bars on base
    textposition='auto',
    texttemplate= '%{y}')
fig= go.Figure([unstacked, stackedbottom, stackedtop, ])
fig.update_layout(xaxis_tickvals=[0.45, 1.45, 2.45, 3.45, 4.45], 
                  xaxis_ticktext=['A', 'B', 'C', 'D', 'E'],
                  xaxis_range=[-0.2, 5], 
                  annotations=[dict(x=X, y=Y, text=t, xref='x', yref='paper',
                showarrow=False) for X, Y, t in zip([0.4, 1.4, 2.4, 3.4, 4.4], [1.07]*5, [201, 202, 203, 204, 205])])
fig.show()

Is it possible to add lines between each bar from the top of the graph to the labels on the bottom x axis?

Also is it possible to have to labels centered under each bar. So instead of A, it could be A1 and A2, one under the unstacked bar and one under the stacked bar.

@njohnson Yes it is possible to draw a line but could you be more precise where is placed the vertical line? Between actual group A and B, etc or between the A1, A2 respectively B1, B2 etc? Another question: does this line have a meaning, because from the point of view of data vizualization it is uncommon to place a vertical line between bars?

@njohnson
This is a version of what you required:

fig= go.Figure([unstacked, stackedbottom, stackedtop])
import string
letters=list(string.ascii_uppercase[:5])
ticktext=[]
tickvals=[]
width=0.45
for k in range(0, 5):
    tickvals.extend([0.5*width+k, 1.5*width+k])
for letter in letters:
    ticktext.extend([f'{letter}1', f'{letter}2'])    
fig.update_layout(xaxis_tickvals=tickvals, 
                  xaxis_ticktext=ticktext,
                  xaxis_range=[-0.2, 5])

for k in range(4):
    fig.add_vline(x=k+0.95)

The line just represents a separation between each group of unstacked and stacked bar from each other. So, a line between each group. I would consider a group be A1 and A2 together or only A.

@njohnson
I see, but it’s color that helps make distinction between the two groups. If you draw a line between A1, A2, and so on, the figure is overloaded with redundant information. If you participate, let us say, in an interview that line is not appreciated. But you can add it if you like it. Just change x in fig.add_vline to plot it between the two groups.

Okay. Thank you.

Also I’ve been trying to add:

layout = go.Layout(
    title="Double X Axis Example",
    xaxis=XAxis(
        title="Categories"
    ),
    xaxis2 = XAxis(
        title="Rooms",
        overlaying= 'x', 
        side= 'top',
    ),
    yaxis=dict(
        title="Y values"
    ),
)

As my titles for the chart, but I am having a hard time putting it in the right place. How can I add both X-axis titles and Y-axis title and chart title?

I was able to add them but my second x-axis title isnt appearing.

import plotly.graph_objects as go
import string

unstacked = go.Bar(
    y=[37, 25, 21, 17, 14],
    base=0,
    width=0.45,
    offset=0,
    textposition='auto',
    texttemplate='%{y}'
)

stackedbottom = go.Bar(
    y=[24, 10, 9, 11],
    base=0,
    width=0.45,
    offset=0.45,
    textposition='auto',
    texttemplate='%{y}'
)

stackedtop = go.Bar(
    y=[13, 15, 12, 0, 8],
    width=0.45,
    offset=0.45,
    base=[24, 10, 9, 0, 0],
    textposition='auto',
    texttemplate='%{y}'
)

fig = go.Figure(data=[unstacked, stackedbottom, stackedtop])

letters = list(string.ascii_uppercase[:5])
ticktext = []
tickvals = []
width = 0.45

for k in range(0, 5):
    tickvals.extend([0.5 * width + k, 1.5 * width + k])

for letter in letters:
    ticktext.extend([f'{letter}1', f'{letter}2'])

fig.update_layout(xaxis_tickvals=tickvals,
                  xaxis_ticktext=ticktext,
                  xaxis_range=[-0.2, 5],
                  annotations=[dict(x=X, y=Y, text=t, xref='x', yref='paper',
                                     showarrow=False) for X, Y, t in zip([0.45, 1.45, 2.45, 3.45, 4.45], [1.07]*5, [201, 202, 203, 204, 205])])

for k in range(4):
    fig.add_vline(x=k + 0.95)

# Adding layout to the figure
fig.update_layout(
    title="Double X Axis Example",
    xaxis=dict(title="Categories"),
    xaxis2=dict(title="Rooms", overlaying='x', side='top'),
    yaxis=dict(title="Y values")
)

fig.show()