How to check if Plots.jl is installed and loaded?

I am writing a function that generates a plot. In case Plots.jl is not installed on the user’s machine or in case it is not loaded in the current Julia session I’d like to print a warning. What is a good way to achieve this?

Pkg.installed and isdefined(Main, :Plots) (although the latter will change with 0.7)

1 Like

Are you sure that’s a good idea? Why not just put your function in a module, and put using Plots in the module? If a user does not have Plots installed, then they will already get an error from Julia, and if they haven’t loaded it then using Plots will fix that.

1 Like

Thank you @mauro3, could you please explain how the latter will change in Julia v0.7?

All good suggestions @rdeits, my use case is as follows:

"""
    VisualComparison

Compare solvers by plotting the results side by side.
"""
struct VisualComparison <: AbstractSolverComparison
  plotspecs
end

VisualComparison() = VisualComparison(nothing)
VisualComparison(; kwargs...) = VisualComparison(kwargs)

function compare(solvers::AbstractVector{S}, problem::AbstractProblem,
                 cmp::VisualComparison) where {S<:AbstractSolver}
  @eval using Plots

  plts = []
  for solver in solvers
    solution = solve(problem, solver)
    push!(plts, plot(solution))
  end

  @show cmp.plotspecs
  plot(plts..., layout=(length(solvers),1))
end

Seems to me that’s a perfect case for a user recipe.

4 Likes

How would you reformulate this as a user recipe @ChrisRackauckas? You are probably right :slight_smile:

This is an example of a type recipe which plots the series of a bunch of solutions in an internal vector:

https://github.com/JuliaDiffEq/DiffEqBase.jl/blob/master/src/solutions/monte_solutions.jl#L94-L102

I think you just need to use the @series macro to build each series as well. That will make them all plot in the same window though, so you’ll need to have @mkborregaard fill in how you then format the plots to be separate windows in that layout you want.

Thank you @ChrisRackauckas, I am trying to find a solution that is general enough in the sense that not all comparison methods are visual. I am already very happy with the multiple dispatch on the comparison type. I pass in the solvers themselves rather than the solution (data) because some comparisons require calling the solvers multiple times for example (e.g. cross-validation).

For now I think I will just show a warning to the user and redesign this in the future if necessary.

Related to that code snippet I posted, how one can save the kwargs passed to the VisualComparison(; kwargs) constructor and use it later in the final plot call? Right now, I am saving the kwargs in a field of type Any inside of the VisualComparison object and it is missing the correct type by the time I use the field in the plot command:

plot(plts..., plotspecs...) # plotspecs from kwargs

You shouldn’t have to check whether Plots is installed - just write a user recipe like @chrisrackauckas suggests, and use the subplot keyword to place the series in the correct subplot (check the MarginalHist recipe here Recipes · Plots for how to do it). It will work if the user has Plots loaded and not affect the user’s system otherwise.

@mkborregaard I already have type recipes defined for the solution type, I only need the function plot available inside of the compare call. That is why I am checking if Plots.jl is installed, makes sense? This function that I am defining (i.e. compare) is in fact creating a plot out of a recipe that I already have defined in the package.

EDIT:

In other words, I could rename the function compare to something like execute_plots_on_solutions. Is there a way to achieve this without adding the @eval using Plots inside of the function body?

I am tempted to just check if Plots.jl is loaded, and if not, I print a warning.

You can use RecipesBase.plot if you just need the plot function and wrap the plot call in try-catch, but it’s a bit of a hack.

1 Like