How to make a customized graph from a dataframe using python

Question:

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 ?

Asked By: L'Artiste

||

Answers:

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.

Answered By: Thomas Schütz

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")
Answered By: MagnusO_O
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.