Stacked Bar chart is ordering data based on label category

I am working on creating a visualization where each data point in the barchart is a duration in seconds (x-axis )and the color represents an event type such as as create, update, delete etc. I need all the events to be in a single bar and the the events should occur in the chart in the order they occur in the data with no grouping or sorting. Currently this seems to be grouping the events by type. I’m not sure if there is a better way to do this or not. I am fairly new to plotly’s library.
I’ve included an image of the current output which is almost correct except I need the events to occur in the order they originally occurred and not grouped by type. I could not find a good example image of what I am looking for, its more like a Gantt chart but all the events occur on a single timeline.

def create_stacked_chart(x_data, y_data, project_title, projectId):
    
    data = {'x': [i for i in range(len(x_data))], 'duration': y_data, 'type': x_data}
    df = pd.DataFrame(data)
    
    # Add dummy column to ensure each row is plotted separately
    df['dummy'] = df.index
    
    color_map = {
             'create': '#3288bd', 
             'update': '#fee08b', 
             'delete': '#4d4d4d', 
             'paste':'#fc8d59',
             'copy': '#fc8d59',
             'copy_paste': '#fc8d59',
             'run_success': '#99d594', 
             'run_fail': '#d53e4f',
             'enter_focus': '#e0e0e0',
             'exit_focus': '#e0e0e0',
    }

    df['color'] = df['type'].map(color_map)

    fig = px.bar(df, x='duration', y=[0.5]*len(df), color_discrete_map=color_map,
                 color='type', orientation='h', hover_name='type')

    fig.update_layout(
        xaxis={'categoryorder':'array', 'categoryarray':[]},
        title={
            'text': f'<b>{project_title}</b>',
            'y':0.97,
            'x':0.5,
            'xanchor': 'center',
            'yanchor': 'top'
        },
        xaxis_title='Timeline of All Event by Duration (Seconds)',
        yaxis_title='',
        legend=dict(title='Event Type', 
                    orientation='h', 
                    yanchor='top', y=1.25, xanchor='left',
                    x=0),
        showlegend=True, 
        width=1200,
        height=250
        
        
    )

    fig.update_traces(marker=dict(line=dict(width=0.25, color='black')))
    fig.show()```

HI @Beck2714 welcome to the forums.

I am not sure how to prevent the grouping in plotly.express, but here is a solution using plotly.graph_objects. Basically you add each data point as new bar and after adding all bars, you switch to barmode="stack"

import pandas as pd
#import plotly.express as px
import plotly.graph_objects as go

def create_stacked_chart(x_data, y_data, project_title, projectId):
    
    data = {'x': [i for i in range(len(x_data))], 'duration': y_data, 'type': x_data}
    df = pd.DataFrame(data)
    
    # Add dummy column to ensure each row is plotted separately
    df['dummy'] = df.index
    
    color_map = {
             'create': '#3288bd', 
             'update': '#fee08b', 
             'delete': '#4d4d4d', 
             'paste':'#fc8d59',
             'copy': '#fc8d59',
             'copy_paste': '#fc8d59',
             'run_success': '#99d594', 
             'run_fail': '#d53e4f',
             'enter_focus': '#e0e0e0',
             'exit_focus': '#e0e0e0',
    }

    df['color'] = df['type'].map(color_map)

    #fig = px.bar(df, x='duration', y=[0.5]*len(df), color_discrete_map=color_map,
    #             color='type', orientation='h', hover_name='type')
    
    # set for remembering types
    remember = set()
    fig = go.Figure()
    for duration, color, text in zip(df.duration, df.color, df.type):
        fig.add_bar(
            x=[duration], 
            y=[0.5]*len(df), 
            marker_color=color, 
            orientation='h', 
            hovertext=text,
            showlegend=text not in remember, # decide if trace is shown in legend
            name=text
        )
        remember.add(text) # add current type to set
    

    fig.update_layout(
        xaxis={'categoryorder':'array', 'categoryarray':[]},
        title={
            'text': f'<b>{project_title}</b>',
            'y':0.97,
            'x':0.5,
            'xanchor': 'center',
            'yanchor': 'top'
        },
        xaxis_title='Timeline of All Event by Duration (Seconds)',
        yaxis_title='',
        legend=dict(title='Event Type', 
                    orientation='h', 
                    yanchor='top', y=1.5, xanchor='left',
                    x=0),
        showlegend=True, 
        width=1200,
        height=250
        
        
    )

    fig.update_traces(marker=dict(line=dict(width=0.25, color='black')))
    
    # switch to stacked bar
    fig.update_layout(barmode='stack') 
    fig.show()

usage:

import random

# using the event type from this dict
color_map = {
         'create': '#3288bd', 
         'update': '#fee08b', 
         'delete': '#4d4d4d', 
         'paste':'#fc8d59',
         'copy': '#fc8d59',
         'copy_paste': '#fc8d59',
         'run_success': '#99d594', 
         'run_fail': '#d53e4f',
         'enter_focus': '#e0e0e0',
         'exit_focus': '#e0e0e0',
}

x_data = [random.choice([*color_map.keys()]) for _ in range(40)]
y_data = [random.random() for _ in range(40)]

fig = create_stacked_chart(x_data, y_data, 'text_A', 'text_B')

creates:


mrep stacked

2 Likes

Wow! you are an absolute hero my friend. Thank you!

2 Likes