Adapting code from Plots into PlotlyJS

Hi,

Using Plots, I have a simple old piece of code to plot a function with a parameter assuming different values. I want to plot it using PlotlyJS. I know how to do it trace-by-trace, but that is ugly and inefficient. Help would be very much appreciated. In the example below, I can do without the LaTeXStrings output.

The MWE using Plots can be found below and its output.
Thanks

using Plots
using LaTeXStrings

f(c,σ) = (-1 + c^(1-σ))/(1-σ); 
util = plot(); 
    for σ = [0.1, 0.4, 0.8, 0.99, 1.5]
        plot!(util, [f(c, σ) for c ∈ 1:1:15]) 
    end
pC = plot(util, 
	label = [L"\sigma=0.1" L"\sigma=0.4" L"\sigma=0.8" L"\sigma=1" L"\sigma=1.5"], 
    	fg_legend = :transparent, legend = :top, 
    	xlabel = L"Consumption", ylabel = L"Utility")

aaa

This is a PlotlyJS version of your code:

using PlotlyJS
using LaTeXStrings
f(c,σ) = (-1 + c^(1-σ))/(1-σ)
x=1:15
σ = [0.1, 0.4, 0.8, 0.99, 1.5]

label = [L"\sigma=0.1", L"\sigma=0.4", L"\sigma=0.8",  L"\sigma=1", L"\sigma=1.5"] 
fig = Plot(Layout(width=700, legend=attr( x=0.35, y=0.95),
                  xaxis_title="Consumption",
                  yaxis_title="Utility"))
for (k, s)  in enumerate(σ)
    addtraces!(fig, scatter(x=x, y=f.(x, s), hovertemplate="consumption: %{x}<br>utility: %{y:.2f}<extra></extra>",
                            name=label[k], mode="lines"))
end 
display(fig)
2 Likes

@empet, thank you so much for your help.

Your code works with perfection in Atom and VScode. Unfortunately, this is for teaching, and I need to use it inside Pluto. Pluto does not throw out any error, but the plot is nowhere to be seen. I am using @disberd turnaround to use PlotlyJS with Pluto. Thanks for your code; the problem rests with Pluto.

Yeah there are some conflicts between Pluto MathJax and Plotly MathJax, with more than one issue on the Pluto Repo.
Plotly started supporting MathJax 3 very recently but I have been doing some testing and it’s not so straightforward to make it work nicely with Pluto.

I am trying to find a fix and will post back if/when I manage :smiley:

1 Like

@disberd, thanks for your concerns. But do not worry; it is not very important. Actually, I know how to do it with individual traces.

The sad part of this story is that it seems complicated to understand why Pluto can use most plotting packages, but not PlotlyJS. PlotlyJS is currently one of the best plotting packages in the Julia environment. Not being available in Pluto’s plotting capabilities, it does not look like a good option as far as Julia is concerned. Without your contributions (which I find amazing), we could not use this package in an IDE … strictly developed for Julia.

@VivMendes you can now use PlutoPlotly.jl as replacement for PlotlyJS or PlotlyBase when directly working in Pluto.

This package basically creates a custom struct PlutoPlot that takes a Plot object from PlotlyBase/PlotlyJS as input and displays nicely and efficiently inside Pluto.

By using PlutoPlotly that is also in the General Registry, you don’t have to rely on pasting the original plotly hack from some time back on discourse, which is also quite outdated at the moment :smiley:

The package also exports a function that can force MathJax in Pluto to cache fonts locally, fixing the problem with latex not showing correctly from plolty inside Pluto.

That is not enabled by default, so if you want to use LaTeX you have to create a cell with the code force_pluto_mathjax_local(true).

Here is a picture of your example working almost unmodified inside a Pluto notebook:

And here is the notebook code for direct pasting for testing:

# ╔═╡ 403d5270-af5e-11ec-02ab-71b6376887cc
begin
	using PlutoPlotly
	using LaTeXStrings
end

# ╔═╡ 97c1c5d1-da17-4f00-8882-b72dcbba30b4
force_pluto_mathjax_local(true)

# ╔═╡ 705007d9-99c8-483c-900f-cab4e9682dc0
begin
f(c,σ) = (-1 + c^(1-σ))/(1-σ)
x=1:15
σ = [0.1, 0.4, 0.8, 0.99, 1.5]

label = [L"\sigma=0.1", L"\sigma=0.4", L"\sigma=0.8",  L"\sigma=1", L"\sigma=1.5"] 
fig = Plot(Layout(width=700, legend=attr( x=0.35, y=0.95),
                  xaxis_title="Consumption",
                  yaxis_title="Utility"))
for (k, s)  in enumerate(σ)
    addtraces!(fig, scatter(x=x, y=f.(x, s), hovertemplate="consumption: %{x}<br>utility: %{y:.2f}<extra></extra>",
                            name=label[k], mode="lines"))
end 
PlutoPlot(fig)
end
3 Likes

Great news. That is awesome. I have tested it already, and it looks beautiful and fast. Just a minor question: can we keep the general syntax of PlotlyJS (via PlotlyBase), or do we have to change it from case to case. For example, how do we save a plot? The old method:

import PlotlyJS:savefig plus savefig(my_plot, "my_plot.svg")

throws out:

MethodError: no method matching savefig(::PlutoPlotly.PlutoPlot, ::String)

Thanks for your contribution. As usual, great stuff.

For the moment add a cell with

PlotlyJS.savefig(p::PlutoPlot, args...) = savefig(p.Plot, args...) 

And your code should work, I’ll have to add saving functionality directly in PlutoPlotly in the next days

Sorry @disberd, despite your precious help, I have not figured out how to use the savefig function of PlotlyJS to save a plot using your nice PlutoPlotly.jl. I produced some variations on your suggestion above, but I am making some stupid mistakes. The best I could come up with was to have the code running with no errors spitted out, but the pdf file was nowhere to be seen.

Some help would be very much appreciated. I am on a Windows machine, Julia 1.8.0, Pluto 0.19.11, PlutoPlotly 0.2.0, PlotlyJS 0.18.8.

Can you show an example of what you are doing/trying to do so I might see what could be going wrong?

In the meantime, I realized I was using an older version of PlutoPlotly.jl. I upgraded it to version v0.3.4 but had no success to savefig.

These are the packages I am using:

#using PlotlyBase
using PlutoUI
using HypertextLiteral
using PlutoPlotly
using LaTeXStrings
using LinearAlgebra
using PlotlyJS

And this is the output I get (if the pdf file is produced, I can not find it):

I tried many variations on the last cell. With some_noise instead of p3, and also with

PlotlyJS.savefig(p3::PlutoPlot, "myplot.pdf") = savefig(p3, "myplot.pdf") 

in which case Pluto spits out:

syntax: ""myplot.pdf"" is not a valid function argument name around
G:\Disberd\Introducing_PlutoPlotly.jl#==#a5de6bba-17a4-40e4-868f-e63e9c7bd0cb:1

Thanks.

You are defining a new method for savefig for PlutoPlotly objects in that cell (which was what i suggested in my earlier post, but you slightly changed it in a way it wouldn’t work).

If you want to save the plot directly without defining the method, you can simply do:
PlotlyJS.savefig(p3.Plot, "myplot.pdf")

My suggestion above was just to define the method directly for PlutoPlot like this:
PlotlyJS.savefig(p::PlutoPlot, args...) = savefig(p.Plot, args...)

And then, you can simply call (in another command)
PlotlyJS.savefig(p3, "myplot.pdf")

The only advantage of the second approach is if you want to save more than one picture, as you don’t have to specifically write p3.Plot each time (where p3 would change each time to the name of the variable associated to the plot)

1 Like

Thanks. It works. I was using the savefig function instead of PlotlyJS.savefig.

PlutoPlotly.jl is working very well and is extremely handy. I am migrating many notebooks initially produced with the workaround (using PlotlyBase) you developed to integrate PlotlyJS with Pluto. There is only one small detail that I am missing in PlutoPlotly that was available in your workaround. Now, when I change the max-width of the notebook, the plots produced before this change was implemented do not react to the new notebook’s width. To have the plots react to the change, I have to run each individual cell that executes the plots. The automatic reactive plot’s width is a small detail, but sometimes it is convenient.

Many thanks for your prompt help.