1

I'm trying to draw a specific graph from the dataframe shown below :

      Type   Test  Speed  Efficiency  Durability
0  Model A     OK      3           3           3
1  Model A  nonOK      2           2           2
2  Model B     OK      1           1           2
3  Model B  nonOK      3           3           2
4  Model C     OK      3           2           6
5  Model C  nonOK      3           4           0

This is the expected graph output :

enter image description here

I tried something very basic with plotly express but I got a different output :

fig = px.bar(df, x="Type", y=['Speed', 'Efficiency', 'Durability'],
            color="Test", height=450, width=500)

fig.show()

enter image description here

Do you have any suggestions please ?

2 Answers 2

3

Data:

import pandas as pd

df = pd.DataFrame(columns=("Type", "Test", "Speed", "Efficiency", "Durability"))
df.loc[0] = ["Model A", "OK",    3, 3, 3]
df.loc[1] = ["Model A", "nonOK", 2, 2, 2]
df.loc[2] = ["Model B", "OK",    1, 1, 2]
df.loc[3] = ["Model B", "nonOK", 3, 3, 2]
df.loc[4] = ["Model C", "OK",    3, 2, 6]
df.loc[5] = ["Model C", "nonOK", 3, 4, 0]
df

Split the data into ok/nonOK:

ok = "OK"
df_ok = df[df["Test"] == ok]
df_nonOK = df[df["Test"] != ok]

Plot both dataframes. I personally prefer graph_objects over express as it gives more control.

import plotly.graph_objects as go

fig = go.Figure()

showlegend = True # Only show the first set of plots in the legend

# Helper function to reduce code duplication
def bar(index_type, index_group, values, name, color, showlegend, base=0):
    return go.Bar(
        x=[index_type, [index_group]*len(values)], # Multicategorical x-axis
        y=values, 
        name=name, # Shown in legend
        offsetgroup=index_group, # Group Speed, Efficiency and Durability
        legendgroup=name, 
        showlegend=showlegend,
        marker={"color": color},
        text=values, # Depict numeric value in the bar
        textposition='auto',
        base=base # Use an offset for the nonOK data
    )

for col in ['Speed', 'Efficiency', 'Durability']:
    fig.add_trace(bar(df_ok["Type"], col, df_ok[col], "OK", "green", showlegend))
    fig.add_trace(bar(df_nonOK["Type"], col, df_nonOK[col], "nonOK", "red", showlegend, df_ok[col]))
    showlegend = False

fig.update_layout(yaxis={"title_text": "Value"})

fig.show()

ResultingFigure

In case you need more info, please check out the

Maybe not the prettiest of all plots, but it hopefully gets you going.

Sign up to request clarification or add additional context in comments.

1 Comment

That's amazing! Your answered my question with something worthy of an article. Thank you so much Thomas. Your solution worked 100%.
1

plotly express has facet plots link to docu
Kudos to this answer from Saaru Lindestøkke from where I got this info.

Figure result (code see below): enter image description here


Dataframe needs to be reshaped from wide to long, e.g. with melt:

import pandas as pd
import io

data_provided = '''                  
   Type,   Test,  Speed,  Efficiency,  Durability
Model A,     OK,      3,           3,           3
Model A,  nonOK,      2,           2,           2
Model B,     OK,      1,           1,           2
Model B,  nonOK,      3,           3,           2
Model C,     OK,      3,           2,           6
Model C,  nonOK,      3,           4,           0
'''
df_fromstring = pd.read_csv(io.StringIO(data_provided), skipinitialspace=True)

df_long = pd.melt(df_fromstring, id_vars=['Type', 'Test'], 
                  value_vars=['Speed', 'Efficiency', 'Durability'],
                  var_name='Characteristics', value_name='amount')

print(df_long)
       Type   Test Characteristics  amount
0   Model A     OK           Speed       3
1   Model A  nonOK           Speed       2
2   Model B     OK           Speed       1
3   Model B  nonOK           Speed       3
4   Model C     OK           Speed       3
5   Model C  nonOK           Speed       3
6   Model A     OK      Efficiency       3
7   Model A  nonOK      Efficiency       2
8   Model B     OK      Efficiency       1
9   Model B  nonOK      Efficiency       3
10  Model C     OK      Efficiency       2
11  Model C  nonOK      Efficiency       4
12  Model A     OK      Durability       3
13  Model A  nonOK      Durability       2
14  Model B     OK      Durability       2
15  Model B  nonOK      Durability       2
16  Model C     OK      Durability       6
17  Model C  nonOK      Durability       0

Stacked and grouped bar plot:

import plotly.express as px

fig = px.bar(df_long, x="Characteristics", y="amount", facet_col="Type", color="Test",
             text_auto=True)  # text_auto=True to print the values inside the bars

fig.show()

Note: Figure result was posted on top.


Add-on 1: Some example to change the plot title and axis (no plot picture added, give it a try):

import plotly.graph_objects as go

fig = px.bar(df_long, x="Characteristics", y="amount", facet_col="Type", color="Test", 
             title="Some Title", text_auto=True)  

# remove x axis subplots titles (bottom)
for axis in fig.layout:
    if type(fig.layout[axis]) == go.layout.XAxis:
        fig.layout[axis].title.text = ''   

# set some new figure titels 
fig.update_layout(title = "Some Other Title", title_x=0.5,
     xaxis_title = 'Type', yaxis_title = 'Value')
    
fig.show()

Add-on 2: Plotly Static Image Export in Python (just mentioned as I had to look that up as well):

Required - if not installed already:

$ pip install -U kaleido
or
$ conda install -c conda-forge python-kaleido

fig.write_image("Plotly_Bar_Stacked_Grouped.png")

1 Comment

OMG! I don't know how to thank you. You literally reproduced my expected output. With the add-on 1, I managed to rename the axis. Thank you so much Magnus.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.