Search code examples
pythondataframeplotlyjob-schedulinggantt-chart

Plotly Express Timeline Gantt Chart, color coding based on column in dataframe


I'm trying to create a machine shop schedule that is color coded by parts that belong to the same assembly. I'm using plotly express timeline to create the Gantt. It is reading an excel file on my desktop to generate the schedule. I created a sample below. The goal is to have all the Chair parts be the same color, and all the Desk parts be the same color.

The excel sheet looks like this

Here's the code to read the excel file and create the Gantt:

df = pd.read_excel(r"C:\Users\john.doe\Documents\Machine Durations - Sample.xlsx")

df['Start Shift'] = df['Start Shift'].astype(int)
df['Finish'] = df['Finish'].astype(int)
#display(df)

# create a slice if the df for the rank = 1
dfRank1 = df[df.Rank == 1]
# reindex it 
dfRank1 = dfRank1.reset_index()
#display(dfRank1)

#Create the visual 

df["Part"] = df["Part"].astype(str)
df["delta"] = df["Finish"]-df["Start Shift"]
fig = px.timeline(df,x_start ="Start Shift", x_end = "Finish", y = "Machine", hover_name ="Part",color = "Part", text = "Part", title = "Machine Shop Cycle", opacity = .75)
fig.update_yaxes(autorange="reversed")
fig.layout.xaxis.type = 'linear'
#fig.data[0].x = df.delta.tolist()
for d in fig.data:
    filt = df['Part'] == d.name
    d.x = df[filt]['delta'].tolist()

fig.update_traces(textposition='inside')    
    
fig.show()


Here's the output Gantt.


Solution

    • good practice is paste you data as text into a question
    • have made two changes
      1. put Assembly into hover_data so that it is in customdata of each trace
      2. loop through traces to update marker_color based on Assembly in customdata
    # update colors to that of the assembly
    cmap = {"Chair":"red", "Desk":"blue"}
    fig.for_each_trace(lambda t: t.update({"marker":{"color":[cmap[a] for a in t["customdata"][:,0]]}}))
    

    full code

    import pandas as pd
    import plotly.express as px
    import io
    
    df = pd.read_csv(
        io.StringIO(
            """Part,Machine,Duration,Duration Shifts(6),Start Shift,Finish,Index,Assembly,Rank
    Legs,Lathe,100,5,0,5,1,Chair,A
    Seat,Mill,400,5,0,5,1,Chair,A
    Back,Mill,200,3,5,8,1,Chair,A
    Legs,Lathe,200,3,5,8,1,Desk,A
    Table Top,Mill,200,3,8,11,1,Desk,A
    Wheels,Mill-Turn,200,10,0,10,1,Desk,A"""
        )
    )
    
    df["Start Shift"] = df["Start Shift"].astype(int)
    df["Finish"] = df["Finish"].astype(int)
    # display(df)
    
    # create a slice if the df for the rank = 1
    dfRank1 = df[df.Rank == 1]
    # reindex it
    dfRank1 = dfRank1.reset_index()
    # display(dfRank1)
    
    # Create the visual
    
    df["Part"] = df["Part"].astype(str)
    df["delta"] = df["Finish"] - df["Start Shift"]
    fig = px.timeline(
        df,
        x_start="Start Shift",
        x_end="Finish",
        y="Machine",
        hover_name="Part",
        hover_data=["Assembly"], # want this for setting color
        color="Part",
        text="Part",
        title="Machine Shop Cycle",
        opacity=0.75,
    )
    fig.update_yaxes(autorange="reversed")
    fig.layout.xaxis.type = "linear"
    # fig.data[0].x = df.delta.tolist()
    for d in fig.data:
        filt = df["Part"] == d.name
        d.x = df[filt]["delta"].tolist()
    
    fig.update_traces(textposition="inside")
    
    # update colors to that of the assembly
    cmap = {"Chair":"red", "Desk":"blue"}
    fig.for_each_trace(lambda t: t.update({"marker":{"color":[cmap[a] for a in t["customdata"][:,0]]}}))
    

    output

    enter image description here