Hi @RobboTheGreg ,
As far as I understand,
You want to spread 3 bar (making as a group) instead of making them overlay.
I have tried use offset
, bar width
and bargroupgap
and below is the result.
import plotly.express as px
import pandas as pd
data = {
'System': ['System 1', 'System 2', 'System 3', 'System 4', 'System 5', 'System 6', 'System 7', 'System 8', 'System 9'],
'Start Date': ['2025-11-03 00:00:00+00:00', '2025-05-05 00:00:00+00:00', '2026-12-13 00:00:00+00:00', '2027-07-12 00:00:00+00:00', '2029-07-21 00:00:00+00:00', '2028-07-23 00:00:00+00:00', '2024-11-20 00:00:00+00:00', '2024-11-12 00:00:00+00:00', '2026-06-01 00:00:00+00:00'],
'End Date': ['2026-06-01 00:00:00+00:00', '2025-12-01 00:00:00+00:00', '2027-07-11 00:00:00+00:00', '2028-02-07 00:00:00+00:00', '2030-02-17 00:00:00+00:00', '2029-03-05 00:00:00+00:00', '2025-01-29 00:00:00+00:00', '2025-01-21 00:00:00+00:00', '2026-12-28 00:00:00+00:00'],
'is_next_serv': ['yes', 'yes', 'yes', 'yes', 'yes', 'yes', 'yes', 'yes', 'yes'],
'ActivityCode': ['A', 'A', 'A', 'A', 'A', 'A', 'B', 'C', 'A'],
'DateDue': ['2024-12-03 00:00:00+00:00', '2024-08-02 00:00:00+00:00', '2026-03-28 00:00:00+00:00', '2026-11-29 00:00:00+00:00', '2028-12-13 00:00:00+00:00', '2027-12-22 00:00:00+00:00', '2024-06-17 00:00:00+00:00', '2024-06-16 00:00:00+00:00', '2026-02-12 00:00:00+00:00'],
'Interval': [304, 304, 304, 304, 304, 304, 152, 152, 304],
'Interval_days': [2128, 2128, 2128, 2128, 2128, 2128, 1064, 1064, 2128],
'Scheduled_Due_Diff': [335, 276, 260, 225, 220, 214, 156, 149, 109],
'Percent': [15.7, 13, 12.2, 10.6, 10.3, 10.1, 14.7, 14, 5.1],
'End_Date': ['2026-06-01 00:00:00+00:00', '2025-12-01 00:00:00+00:00', '2027-07-11 00:00:00+00:00', '2028-02-07 00:00:00+00:00', '2030-02-17 00:00:00+00:00', '2029-03-05 00:00:00+00:00', '2025-01-29 00:00:00+00:00', '2025-01-21 00:00:00+00:00', '2026-12-28 00:00:00+00:00'],
'Start_Date': ['2025-11-03 00:00:00+00:00', '2025-05-05 00:00:00+00:00', '2026-12-13 00:00:00+00:00', '2027-07-12 00:00:00+00:00', '2029-07-21 00:00:00+00:00', '2028-07-23 00:00:00+00:00', '2024-11-20 00:00:00+00:00', '2024-11-12 00:00:00+00:00', '2026-06-01 00:00:00+00:00']
}
data2 = {
'System': ['System 1', 'System 2', 'System 3', 'System 4', 'System 5', 'System 6', 'System 7', 'System 8', 'System 9'],
'Start Date': ['2025-11-03 00:00:00+00:00', '2025-05-05 00:00:00+00:00', '2026-12-13 00:00:00+00:00', '2027-07-12 00:00:00+00:00', '2029-07-21 00:00:00+00:00', '2028-07-23 00:00:00+00:00', '2024-11-20 00:00:00+00:00', '2024-11-12 00:00:00+00:00', '2026-06-01 00:00:00+00:00'],
'End Date': ['2026-06-01 00:00:00+00:00', '2025-12-01 00:00:00+00:00', '2027-07-11 00:00:00+00:00', '2028-02-07 00:00:00+00:00', '2030-02-17 00:00:00+00:00', '2029-03-05 00:00:00+00:00', '2025-01-29 00:00:00+00:00', '2025-01-21 00:00:00+00:00', '2026-12-28 00:00:00+00:00'],
'is_next_serv': ['yes', 'yes', 'yes', 'yes', 'yes', 'yes', 'yes', 'yes', 'yes'],
'ActivityCode': ['A', 'A', 'A', 'A', 'A', 'A', 'B', 'C', 'A'],
'DateDue': ['2024-12-25 00:00:00+00:00', '2024-08-26 00:00:00+00:00', '2026-04-22 00:00:00+00:00', '2026-12-22 00:00:00+00:00', '2029-01-12 00:00:00+00:00', '2028-01-20 00:00:00+00:00', '2024-07-10 00:00:00+00:00', '2024-07-04 00:00:00+00:00', '2026-03-15 00:00:00+00:00'],
'Interval': [304, 304, 304, 304, 304, 304, 152, 152, 304],
'Interval_days': [2128, 2128, 2128, 2128, 2128, 2128, 1064, 1064, 2128],
'Scheduled_Due_Diff': [335, 276, 260, 225, 220, 214, 156, 149, 109],
'Percent': [12.7, 12, 11.2, 7.6, 8.3, 10.1, 10.7, 10, 3.1],
'End_Date': ['2026-06-01 00:00:00+00:00', '2025-12-01 00:00:00+00:00', '2027-07-11 00:00:00+00:00', '2028-02-07 00:00:00+00:00', '2030-02-17 00:00:00+00:00', '2029-03-05 00:00:00+00:00', '2025-01-29 00:00:00+00:00', '2025-01-21 00:00:00+00:00', '2026-12-28 00:00:00+00:00'],
'Start_Date': ['2025-11-03 00:00:00+00:00', '2025-05-05 00:00:00+00:00', '2026-12-13 00:00:00+00:00', '2027-07-12 00:00:00+00:00', '2029-07-21 00:00:00+00:00', '2028-07-23 00:00:00+00:00', '2024-11-20 00:00:00+00:00', '2024-11-12 00:00:00+00:00', '2026-06-01 00:00:00+00:00']
}
data3 = {
'System': ['System 1', 'System 2', 'System 3', 'System 4', 'System 5', 'System 6', 'System 7', 'System 8', 'System 9'],
'Start Date': ['2025-11-03 00:00:00+00:00', '2025-05-05 00:00:00+00:00', '2026-12-13 00:00:00+00:00', '2027-07-12 00:00:00+00:00', '2029-07-21 00:00:00+00:00', '2028-07-23 00:00:00+00:00', '2024-11-20 00:00:00+00:00', '2024-11-12 00:00:00+00:00', '2026-06-01 00:00:00+00:00'],
'End Date': ['2026-06-01 00:00:00+00:00', '2025-12-01 00:00:00+00:00', '2027-07-11 00:00:00+00:00', '2028-02-07 00:00:00+00:00', '2030-02-17 00:00:00+00:00', '2029-03-05 00:00:00+00:00', '2025-01-29 00:00:00+00:00', '2025-01-21 00:00:00+00:00', '2026-12-28 00:00:00+00:00'],
'is_next_serv': ['yes', 'yes', 'yes', 'yes', 'yes', 'yes', 'yes', 'yes', 'yes'],
'ActivityCode': ['A', 'A', 'A', 'A', 'A', 'A', 'B', 'C', 'A'],
'DateDue': ['2025-5-25 00:00:00+00:00', '2025-03-26 00:00:00+00:00', '2026-10-22 00:00:00+00:00', '2027-05-22 00:00:00+00:00', '2029-05-12 00:00:00+00:00', '2028-04-20 00:00:00+00:00', '2024-10-10 00:00:00+00:00', '2024-10-04 00:00:00+00:00', '2026-05-15 00:00:00+00:00'],
'Interval': [304, 304, 304, 304, 304, 304, 152, 152, 304],
'Interval_days': [2128, 2128, 2128, 2128, 2128, 2128, 1064, 1064, 2128],
'Scheduled_Due_Diff': [335, 276, 260, 225, 220, 214, 156, 149, 109],
'Percent': [2.7, 6, 4.2, 5.6, 6.3, 5.1, 3.7, 5, 1.1],
'End_Date': ['2026-06-01 00:00:00+00:00', '2025-12-01 00:00:00+00:00', '2027-07-11 00:00:00+00:00', '2028-02-07 00:00:00+00:00', '2030-02-17 00:00:00+00:00', '2029-03-05 00:00:00+00:00', '2025-01-29 00:00:00+00:00', '2025-01-21 00:00:00+00:00', '2026-12-28 00:00:00+00:00'],
'Start_Date': ['2025-11-03 00:00:00+00:00', '2025-05-05 00:00:00+00:00', '2026-12-13 00:00:00+00:00', '2027-07-12 00:00:00+00:00', '2029-07-21 00:00:00+00:00', '2028-07-23 00:00:00+00:00', '2024-11-20 00:00:00+00:00', '2024-11-12 00:00:00+00:00', '2026-06-01 00:00:00+00:00']
}
df1 = pd.DataFrame(data)
df2 = pd.DataFrame(data2)
df3 = pd.DataFrame(data3)
# bar color
color_dict_list = [{'A': 'rgba(255, 99, 71,0.5)','B':'rgba(131, 255, 71,0.5)','C':'rgba(131, 152, 255, 0.5)'},{'A': 'rgba(255, 99, 71, 0.8)','B':'rgba(131, 255, 71, 0.8)','C':'rgba(131, 152, 255, 0.8)'},{'A': 'rgba(255, 99, 71,1.0)','B':'rgba(131, 255, 71,1.0)','C':'rgba(131, 152, 255, 1.0)'}]
for idx,df in enumerate([df3,df2,df1]):
if idx == 0:
fig = px.timeline(df,
x_start = 'DateDue',
x_end = 'Start Date',
y = 'System',
color = 'ActivityCode',
color_discrete_map = color_dict_list[0],
text = df['ActivityCode'] + " " + df['Percent'].astype(str) + '%',
custom_data=['ActivityCode', 'DateDue', 'Start_Date', 'Percent' ],
category_orders = {'System': sorted(df['System'].unique())})
fig.update_traces(
hovertemplate=
"<b>***%{y} %{customdata[0]} REQUIRES Extension***</b><br>"
"Due: %{customdata[1]|%d-%b-%y}<br>"
"Scheduled: %{customdata[2]|%d-%b-%y}<br>"
"Percentage Req: %{customdata[3]}<br>"
)
fig.update_traces(width= 0.3,offset=0.3 + (0.3 * (idx-1)))
else:
fig1 = px.timeline(df,
x_start = 'DateDue',
x_end = 'Start Date',
y = 'System',
color = 'ActivityCode',
color_discrete_map = color_dict_list[idx],
text = df['ActivityCode'] + " " + df['Percent'].astype(str) + '%',
custom_data=['ActivityCode', 'DateDue', 'Start_Date', 'Percent' ],
category_orders = {'System': sorted(df['System'].unique())})
fig1.update_traces(
hovertemplate=
"<b>***%{y} %{customdata[0]} REQUIRES Extension***</b><br>"
"Due: %{customdata[1]|%d-%b-%y}<br>"
"Scheduled: %{customdata[2]|%d-%b-%y}<br>"
"Percentage Req: %{customdata[3]}<br>"
)
fig1.update_traces(width= 0.3,offset=0.3 + (0.3 * (idx-1)))
# Combine the two Gantt charts
for trace in fig1.data:
fig.add_trace(trace)
# Update text position to left
fig.update_traces(textposition='inside')
fig.update_traces(insidetextanchor="start")
#Adjust position on y axis
fig.update_yaxes(anchor = "free")
fig.update_layout(margin=dict(l=0, r=0, t=100, b=0),bargroupgap=1) #<<<Does not seem to have any effect
fig.update_yaxes(position = 1) #<<<Does not seem to have any effect
fig.update_yaxes(side= "right") #<<<Does not seem to have any effect
fig.update_layout(legend=dict(
yanchor="top",
y=0.99,
xanchor="left",
x=0.92
))
fig.show()
This another (2nd and 3rd) data sets, I generate based on your first dataset.
Is this what you are looking for ?