1

I have this code example using plotly that builds a stacked bar chart:

import plotly.graph_objects as go

x = ['2018-01', '2018-02', '2018-03']

fig = go.Figure(go.Bar(x=x, y=[10, 15, 3], name='Client 1'))
fig.add_trace(go.Bar(x=x, y=[12, 7, 14], name='Client 2'))

fig.update_layout(
    barmode='stack',
    yaxis={'title': 'amount'},
    xaxis={
        'type': 'category',
        'title': 'month',
    },
)
fig.show()

Which outputs the following plot:

enter image description here

Is there a way to tweak the plotly layout as to order each bar's Y-axis by value?
For example in the second bar (2018-02) Client 1 has a higher value for Y, the blue bar should be on top of the red one.

Flavia Giammarino
  • 7,987
  • 11
  • 30
  • 40
Teodor Ivanov
  • 184
  • 1
  • 8
  • 1
    That doesn't make it a stacked bar chart, does it? The first layer is client 1 and the second layer is client 2. In `barmode=group`, they are ordered by client. – r-beginners Sep 16 '20 at 13:58
  • This is correct but I need this chart to represent a lot of data. In some cases a bar can have over 15 stacked bars within it. I need this as an alternative to the ribbon plot in Power BI for example. – Teodor Ivanov Sep 16 '20 at 14:06

1 Answers1

5

In Plotly the traces are always displayed in the order in which they are added to the figure and there isn't a layout option that allows to change this behavior; see, for instance, this answer. This means that for each date you would need to add the traces with the smaller values before adding the traces with the larger values. I included an example below based on your code.

import plotly.graph_objects as go
import pandas as pd
import numpy as np

# data
df = pd.DataFrame({'Date': ['2018-01', '2018-02', '2018-03'],
                   'Client 1': [10, 15, 3],
                   'Client 2': [12, 7, 14],
                   'Client 3': [18, 2, 7]})

# colors
colors = {'Client 1': 'red',
          'Client 2': 'blue',
          'Client 3': 'green'}

# traces
data = []

# loop across the different rows
for i in range(df.shape[0]):

    # for each row, order the columns based on
    # their values from smallest to largest
    ordered_columns = df.columns[1:][np.argsort(df.iloc[i, 1:].values)]

    # add a separate trace for each column,
    # ordered from smallest to largest
    for column in ordered_columns:

        data.append(go.Bar(x=[df['Date'][i]],
                           y=[df[column][i]],
                           marker=dict(color=colors[column]),
                           name=column,
                           legendgroup=column,
                           showlegend=i == 0)) # show the legend only once for each column

# layout
layout = dict(barmode='stack',
              yaxis={'title': 'amount'},
              xaxis={'type': 'category', 'title': 'month'})

# figure
fig = go.Figure(data=data, layout=layout)

fig.show()

enter image description here

Flavia Giammarino
  • 7,987
  • 11
  • 30
  • 40
  • Wonderful! I see this answer orders the column values from smallest to largest. How can I do the opposite? – EMartins May 29 '23 at 22:55
  • 1
    You can replace `np.argsort(df.iloc[i, 1:].values)` with `np.argsort(df.iloc[i, 1:].values)[::-1]`, see [this question](https://stackoverflow.com/q/16486252/11989081). – Flavia Giammarino May 30 '23 at 05:59