pythonplotlygantt-chart

How to show task dependencies when creating Gantt charts using `create_gantt()`?


When making Gantt charts was to use the create_gantt() figure factory, as follows:

import plotly.figure_factory as ff

df = [dict(Task="Job A", Start='2009-01-01', Finish='2009-02-28'),
      dict(Task="Job B", Start='2009-03-05', Finish='2009-04-15'),
      dict(Task="Job C", Start='2009-02-20', Finish='2009-05-30')]

fig = ff.create_gantt(df)
fig.show()

I got the following result:

Gantt charts

However, I also want to show the task dependencies that Job B depends on Job A, that is, I want to draw an arrow from Job A to Job B. How to show task dependencies in the above Gantt chart?


Solution

  • As mentioned in the comments, you can draw an arrow using annotations. You can make a styled arrow similar to ones available in matplotlib by using several line segments with an arrow at the end.

    For convenience, I converted all of the datetime strings to datetimes and placed them in dictionaries.

    And to make the solution more generalizable, I also wrapped the annotations in a function so you can create such annotations for any two jobs. Note that the function is a bit rigid in the sense that it assumes the beginning of the second job occurs after the start of the first job.

    import pandas as pd
    import plotly.figure_factory as ff
    
    jobA_dict = dict(Task="Job A", Start=pd.to_datetime('2009-01-01'), Finish=pd.to_datetime('2009-02-28'))
    jobB_dict = dict(Task="Job B", Start=pd.to_datetime('2009-03-05'), Finish=pd.to_datetime('2009-04-15'))
    jobC_dict = dict(Task="Job C", Start=pd.to_datetime('2009-05-01'), Finish=pd.to_datetime('2009-06-30'))
    
    df = [jobA_dict, jobB_dict, jobC_dict]
    
    fig = ff.create_gantt(df)
    
    ## draw an arrow from the end of the first job to the start of the second job
    
    def draw_arrow_between_jobs(fig, first_job_dict, second_job_dict):
        ## retrieve tick text and tick vals
        job_yaxis_mapping = dict(zip(fig.layout.yaxis.ticktext,fig.layout.yaxis.tickvals))
        jobs_delta = second_job_dict['Start'] - first_job_dict['Finish']
        ## horizontal line segment
        fig.add_shape(
            x0=first_job_dict['Finish'], y0=job_yaxis_mapping[first_job_dict['Task']], 
            x1=first_job_dict['Finish'] + jobs_delta/2, y1=job_yaxis_mapping[first_job_dict['Task']],
            line=dict(color="blue", width=2)
        )
        ## vertical line segment
        fig.add_shape(
            x0=first_job_dict['Finish'] + jobs_delta/2, y0=job_yaxis_mapping[first_job_dict['Task']], 
            x1=first_job_dict['Finish'] + jobs_delta/2, y1=job_yaxis_mapping[second_job_dict['Task']],
            line=dict(color="blue", width=2)
        )
        ## horizontal line segment
        fig.add_shape(
            x0=first_job_dict['Finish'] + jobs_delta/2, y0=job_yaxis_mapping[second_job_dict['Task']], 
            x1=second_job_dict['Start'], y1=job_yaxis_mapping[second_job_dict['Task']],
            line=dict(color="blue", width=2)
        )
        ## draw an arrow
        fig.add_annotation(
            x=second_job_dict['Start'], y=job_yaxis_mapping[second_job_dict['Task']],
            xref="x",yref="y",
            showarrow=True,
            ax=-10,
            ay=0,
            arrowwidth=2,
            arrowcolor="blue",
            arrowhead=2,
        )
        return fig
    

    Now to draw an arrow between Job A and Job B:

    fig = draw_arrow_between_jobs(fig, jobA_dict, jobB_dict)
    

    enter image description here

    And you can add an additional arrow between Job B and Job C:

    fig = draw_arrow_between_jobs(fig, jobB_dict, jobC_dict)
    

    enter image description here