How to make a gantt plot with Plots.jl

Hi,

I would like to make a gantt plot in Plots.jl. I came across a few old posts concerning gantt plots, but no solution. Here is what I have so far:

using StatsPlots
using DataFrames

df = DataFrame(label=["A","B"],
                start_time = [1.0,3.0],
                end_time = [4.0, 5.0],
                color = [:blue, :green])

@df df bar(:start_time, yticks = (1:2,:label), orientation=:h, leg=false, color=:color)

The problem is that the y labels are backwards and the bars are not correctly displayed between the start time and end times. Any help would be greatly appreciated.

Using Plots.jl:

using Plots, DataFrames

rect(w, h, x, y) = Shape(x .+ [0,w,w,0], y .+ [0,0,h,h])

df = DataFrame(label=["A","B"], start_time=[1.0,3.0], end_time=[4.0,5.0], color=[:blue,:green])
df.duration = df.end_time - df.start_time
r = [rect(t[1],1,t[2],t[3]) for t in zip(df.duration, df.start_time, 1:nrow(df))]

plot(r, c=permutedims(df.color), yticks=(1.5:(nrow(df) + 0.5), df.label), xlabel="Time")

2 Likes

Thanks again Rafael! :slightly_smiling_face:

1 Like

Fyi, I have updated the code according to the best practice of plotting performance: plot all objects at once if possible.

1 Like

Awesome. Much appreciated!

@rafael.guerra, I was hoping I could ask you a follow up question. How do I force all labels of the same type on to the same row. Here is a simple example:

using Plots, DataFrames

rect(w, h, x, y) = Shape(x .+ [0,w,w,0], y .+ [0,0,h,h])

df = DataFrame(label=["A","B","B"], start_time=[1.0,3.0,6.0], end_time=[4.0,5.0,8.0], color=[:blue,:green,:green])

df.duration = df.end_time - df.start_time

r = [rect(t[1],1,t[2],t[3]) for t in zip(df.duration, df.start_time, 1:nrow(df))]

plot(r, c=permutedims(df.color), yticks=(1.5:(nrow(df) + 0.5), df.label), xlabel="Time")

The problem is that two rows are created for B instead of 1. Any help would be greatly appreciated!

Hi Christopher, I am lacking time now, but please try the following:

using Plots, DataFrames

rect(w, h, x, y) = Shape(x .+ [0,w,w,0], y .+ [0,0,h,h])

df = DataFrame(label=["A","B","B"], start_time=[1.0,3.0,6.0], end_time=[4.0,5.0,8.0], color=[:blue,:green,:green])
df.duration = df.end_time - df.start_time
u = unique(df.label)
dy = Dict(u .=> 1:length(u))
r = [rect(t1, 1, t2, dy[t3]) for (t1,t2,t3) in zip(df.duration, df.start_time, df.label)]

plot(r, c=permutedims(df.color), yticks=(1.5:(nrow(df) + 0.5), df.label), xlabel="Time", labels=false)
1 Like

Thanks for your help. I was able to remove the third empty row with the following code.

using Plots, DataFrames

rect(w, h, x, y) = Shape(x .+ [0,w,w,0], y .+ [0,0,h,h])

df = DataFrame(label=["A","B","B"], start_time=[1.0,3.0,6.0], end_time=[4.0,5.0,8.0], color=[:blue,:green,:green])

df.duration = df.end_time - df.start_time

nl = length(unique(df.label))

dy = Dict(unique(df.label) .=> 1:nl)

r = [rect(t[1],1,t[2], dy[t[3]]) for t in zip(df.duration, df.start_time, df.label)]

y_axis_labels = unique(df.label)

plot(r, c=permutedims(df.color), yticks=(1.5:(length(y_axis_labels) + 0.5), y_axis_labels), xlabel="Time", labels=false)

The only thing I need to do is figure how to enclose the boarder around the rectangles. For some reason the left border is missing. I’ll report back with an update if I find a solution.

1 Like

I never figured out how to enclose the border around the bars with the pyplot backend. I wanted to see if anyone has a solution for this problem. It works for gr, but I think pyplot + eps looks better.

I tried that and linecolor. I believe they are aliases. In matplotlib it is called edgecolor, but I did not find any insights when searching for that keyword in the source code. Do you think it is a bug?

Sorry, I misread your message.

For some reason, the pythonplot() backend doesn’t actually close the rectangle. All you have to do is redefine the function:

rect(w, h, x, y) = Shape(x .+ [0,w,w,0,0], y .+ [0,0,h,h,0])
1 Like

I would not have guessed that as a solution. Thanks!

1 Like