I have a dataset in the form of a dataframe structured as follows
Job | Task | Machine | Start | End | color |
---|---|---|---|---|---|
A | Do X | 1 | 0 | 5 | blue |
A | Do Y | 2 | 6 | 14 | blue |
B | Do Q | 3 | 0 | 8 | green |
B | Do Z | 3 | 9 | 12 | green |
B | Do X | 1 | 14 | 17 | green |
C | Do Y | 1 | 6 | 9 | red |
I want to plot these on an interactive timeline using Plotly (e.g. px.timeline), similar to included image below, also known as Gantt-chart. Per machine (y-axis), I want to a colored bar representing a task assigned to that machine, similar to its duration. For my example data, it means that for machine 1, there are three bars colored (0-5, 6-9, 14-17). The tasks should be colored according to a defined job color, as included in the color column.
Ideally, in hovering over the bar, it shows Job - Task, Start - end
.
I use the following;
Plotly 5.18.0
Python 3.9
Pandas 2.1.2
Is there a way to do this?
I played around with Plotly, but only managed to get the jobs on the y-axis (https://plotly.com/python/gantt/). I used https://plotly.com/python/figure-factories/ since the px.timeline does not appear to accept integer values for start and end times.
In px.timeline
, colors cannot be specified, so we can simply use a horizontal bar graph.
To draw a horizontal bar chart with a specified color, specify a stacked bar chart in a loop process with the contents conditionally extracted from the data frame by Job. The base is specified because it is always necessary to set the starting position. Text display can only be selected inside and outside, so we add it to the center of the bar chart using string annotations
import plotly.graph_objects as go
fig = go.Figure()
for j in df['Job'].unique():
dfj = df.query('Job == @j')
for row in dfj.itertuples():
print(row)
before_base = row.Start
fig.add_trace(go.Bar(
base=[row.Start],
x0=[row.Start],
x=[row.End- before_base],
y=[row.Job],
y0=[row.Job],
hovertemplate='Start: '+str(row.Start)+'<br>'+
'End: %{x}<br>'+row.Task+'<extra></extra>',
orientation='h',
#text=row.Task,
marker_color=row.color,
width=0.9,
showlegend=False,
))
fig.add_annotation(
x=(row.Start+row.End)/2,
y=row.Job,
text=row.Task,
font=dict(color='white'),
showarrow=False,
)
fig.update_layout(barmode='stack')
fig.update_yaxes(autorange="reversed")
fig.update_layout(height=400)
fig.show()