JuliaPlots / Plots.jl

Powerful convenience for Julia visualizations and data analysis
https://docs.juliaplots.org
Other
1.82k stars 351 forks source link

[FR] Gantt Charts #4381

Open jamblejoe opened 1 year ago

jamblejoe commented 1 year ago

Opening this to have gantt supported at some point in the future. My workaround is atm

bar([1,2,3]; linecolor=nothing)
bar!([0.5, 1.5, 2.5]; color=:white, linecolor=:white)
plot!(grid=:none)

resulting in temp E.g. this does not work with grids. I would be willing to spend some time implementing this, whenever I find some free time. I guess it will be mostly modifying the starting position of the bars. It would be nice if someone could point me to the relevant code. I would suggest one of the two

gantt(x, y_start, y_end)
gantt(x, y_start, y_length)

as some minimal interface. One has to discuss if one wants the bars by default horizontal or vertical. Thoughts?

gustaphe commented 1 year ago

Sounds like a good idea. One thing to consider:

julia> plot(1:5; fillrange=0:4, seriestype=:bar, orientation=:horizontal)

fillrange is better than overlapping a background coloured bar.

jamblejoe commented 1 year ago

fillrange somehow does not work with Dates, which would be commonly used in Gantt plots.

bar(1:2, [Date(2022,1,1), Date(2023,1,1)]; 
       fillrange=[Date(2021,1,1) , Date(2022,1,1)]
)

errors with

MethodError: Cannot `convert` an object of type Dates.Date to an object of type Float64

Closest candidates are:

convert(::Type{T}, !Matched::ColorTypes.Gray24) where T<:Real at ~/.julia/packages/ColorTypes/1dGw6/src/conversions.jl:114

convert(::Type{T}, !Matched::ColorTypes.Gray) where T<:Real at ~/.julia/packages/ColorTypes/1dGw6/src/conversions.jl:113

convert(::Type{T}, !Matched::T) where T<:Number at number.jl:6

...

    push!(::Plots.Segments{Float64}, ::Float64, ::Dates.Date, ::Dates.Date, ::Float64, ::Vararg{Float64})@utils.jl:42
    macro expansion@recipes.jl:482[inlined]
    apply_recipe(::AbstractDict{Symbol, Any}, ::Type{Val{:bar}}, ::Any, ::Any, ::Any)@RecipesBase.jl:290
    _process_seriesrecipe(::Any, ::Any)@series_recipe.jl:50
    _process_seriesrecipes!(::Any, ::Any)@series_recipe.jl:27
    recipe_pipeline!(::Any, ::Any, ::Any)@RecipesPipeline.jl:97
    _plot!(::Plots.Plot, ::Any, ::Any)@plot.jl:232
    #plot#149@plot.jl:107[inlined]
    #bar#450@RecipesBase.jl:411[inlined]
    top-level scope@[Local: 1](http://localhost:1235/edit?id=89ddbd2c-3e9f-11ed-0102-0dad099205a4#)[inlined]
gustaphe commented 1 year ago

I don't know the best way to solve that. It could be hardcoded to convert dates to Float64, but maybe it would be even better to use apply_recipe and throw away any extra information.

gustaphe commented 1 year ago

Actually, here's a start which will just automatically do the conversion. Maybe you could put something like this in a GanttCharts.jl package? It will probably need a bit of polish.

@recipe function f(::Type{Val{:gantt}}, x, y, z)
    fillrange := x
    seriestype := :bar
    orientation --> :horizontal
    if plotattributes[:orientation] == :horizontal
        yticks --> false
    else
        xticks --> false
    end
    x := Base.OneTo(length(y))
    return y
end