Macro for plotting simple curves

Do any of the plotting packages have a macro for plotting simple curves? I’m looking for something like this:

# plot the curve from x=-10 to x=10
@curve(x^3 - 6x^2 + 4x + 12, -10, 10)

Basically, I’m looking for something as quick and easy to use as going to Wolfram Alpha and typing this in:

plot x^3 - 6x^2 + 4x + 12

Probably not, because we can do this:

plot([x^3 - 6x^2 + 4x + 12 for x in -10:0.1:10])

which doesn’t have many more characters than the macro example. (But you know that already :slight_smile: )

1 Like

Unfortunately that doesn’t plot the right thing. When you only provide one vector, y, with length N, the plot function plots that y vector versus x=1:N. So the output of

plot([x^3 - 6x^2 + 4x + 12 for x in -10:0.1:10]; legend=false)

is this:

True, perhaps this is most character-economic version?

 x=-10:0.1:10; plot(x,@. x^3 - 6x^2 + 4x + 12)
2 Likes

Not bad! Though for some reason I find 10:0.1:10 hard to type. :stuck_out_tongue_closed_eyes:

1 Like

Fwiw I use Desmos for this kind of thing and like it.

1 Like

This is an exercise in my macro tutorials :slight_smile:

An example solution with no error handling might be:

import Plots

to_string(e) = String(Symbol(e))

macro curve(e, from, to)
    quote
        Plots.plot(
            range($(esc(from)), $(esc(to)), length = 512),
            $(
                Expr(
                    :->,
                    esc(:x),
                    esc(e),
                )
            ),
            ylabel = $(to_string(e)),
        )
    end
end

@curve(x^3 - 6x^2 + 4x + 12, -10, 10)

A more exciting variant is to analyze the expression so that an expression in only x is a 2D plot and an expression in x and y is a 3D plot.

5 Likes

With Plots:

julia> plot(f::F, rang) where {F<:Function} = scatter([x for x in rang], [f(x) for x in rang])
plot (generic function with 1 method)

julia> plot(x -> -x^2 + x - 1.0, -1:0.1:1)

image
Edit: Sorry, forgot to subtype.

5 Likes

Now what would be really great is if Makie or Plots had a @curve macro and some logic that determines a good scale for the axes like Wolfram Alpha does. Then I could just type this:

@curve(x^3 - 6x^2 + 4x + 12)

EDIT:

I think I could even get away with typing it like this:

@curve x^3 - 6x^2 + 4x + 12

EDIT #2:

Though the parsing might be too fragile without the parentheses. Note the difference between these two expressions:

julia> Meta.show_sexpr(:(@curve x^3 - 6x^2 + 4x + 12))
(:macrocall, Symbol("@curve"), :(#= REPL[30]:1 =#), (:call, :+, (:call, :-, (:call, :^, :x, 3), (:call, :*, 6, (:call, :^, :x, 2))), (:call, :*, 4, :x), 12))

julia> Meta.show_sexpr(:(@curve x^3 -6x^2 + 4x + 12))
(:macrocall, Symbol("@curve"), :(#= REPL[31]:1 =#), (:call, :^, :x, 3), (:call, :+, (:call, :*, -6, (:call, :^, :x, 2)), (:call, :*, 4, :x), 12))

There are some really cool things logic wise that you could do to find scales for plotting. One potentially interesting option would be to use autodiff and root-finding to make sure that all zeros and turning points were included in the plot.

3 Likes

Why not just

using Plots
plot(x -> x^3 - 6x^2 + 4x + 12, xlim=(-10,10))
12 Likes

Hey, that’s pretty good! I’m really not very proficient with plotting in Julia, so I either forgot about that plot method or I never knew it existed… :slight_smile:

1 Like

This seems to work too:

plot([x -> x^3 - 6x^2 + 4x + 12],-10,10)
3 Likes

Ah yes! Don’t even need the square brackets actually:

plot(x -> x^3 - 6x^2 + 4x + 12, -10, 10)

@CameronBieganek This is really close to what you wanted, just change @curves into plot, and add the x -> to make it explicit that it is a function of x.

6 Likes

Take that Wolfram! :joy:

2 Likes

If you omit the bounds, Plots will choose some (I think just -5 to 5):

plot(x -> x^3 - 2x^2 + 3x - 1)
2 Likes

Awesome! So I’ll start with

plot(x -> x^3 - 2x^2 + 3x - 1)

and then refine the window size as needed with

plot(x -> x^3 - 2x^2 + 3x - 1, -10, 10)
1 Like

The two gnuplot packages Gnuplot.jl and Gaston.jl also support direct plotting of functions.
One advantage is that they load very quickly.

x = 1.:20
@gp x x.^2 "with lines title 'Parabola'"https://github.com/mbaz/Gaston.jl
save(term="pngcairo size 480,360", output="examples/ex1.png")
save("parabola.gp")  # => save a script file with both data and command to re-create the plot.
using Gaston, SpecialFunctions
x = y = 0:0.075:10
surf(x, y, (x,y) -> besselj0(y)*x^2, with = "pm3d",
     Axes(view = (45, 45),
          pm3d = "lighting primary 0.5 specular 0.4",
          key = :off)
     )

download

2 Likes

Correct – and the syntax plot(f::Function, xmin, xmax; samples=100) will be available in the next release of Gaston.jl, with automatic calculation of number of samples (if I can get it to work). See Add `plot(::Function, x1::Real, x2::Real; samples::Real = 100)` syntax · Issue #137 · mbaz/Gaston.jl · GitHub

3 Likes

What is the link to your tutorial?