How to bind data with plot function?

This is my first posting here, and I apologize if this is silly.

I am quite familiar with R, and a scheme I use there is to compute something and assign it a class, so that I can then define a plot generic. You can see that below. The idea of this is that the foo and plot.foo functions would normally go in a library, so that the user simply has to call that library and then execute the last two lines in my example.

My question is how this ought to be done in Julia. I sort of think what I want to do is to return a named tuple that perhaps holds x, y and a function named plot that does the plotting. Is that the recommended approach? I’m hoping to learn to write Julia in a way that will not just work, but also be familiar to users, and that means I’m hoping that someone can advise me on what is the β€œJulia way”.

# R code
foo <- function(x)
{
    res <- list(x=x, y=sin(x))
    class(res) <- "Foo"
    res
}
plot.Foo <- function(x)
{
    plot(x$x, x$y)
}

s <- foo(seq(0, 2*pi, pi/64))
plot(s)

I think you want something like this, which is creating a overload of the plotting function for a specific type you create (here exemplified with UnicodePlots):

julia> using UnicodePlots
       struct Foo
           x::Vector{Float64}
           y::Vector{Float64}
       end
       Foo(x) = Foo(x,sin.(x))
       plot(f::Foo) = lineplot(f.x,f.y)
plot (generic function with 1 method)

julia> f = Foo(0:pi/64:2*pi)
Foo([0.0, 0.04908738521234052, 0.09817477042468103, 0.14726215563702155, 0.19634954084936207, 0.2454369260617026, 0.2945243112740431, 0.3436116964863836, 0.39269908169872414, 0.44178646691106466  …  5.841398840268521, 5.890486225480862, 5.939573610693203, 5.988660995905543, 6.037748381117884, 6.086835766330224, 6.135923151542564, 6.1850105367549055, 6.234097921967246, 6.283185307179586], [0.0, 0.049067674327418015, 0.0980171403295606, 0.14673047445536175, 0.19509032201612825, 0.24298017990326387, 0.29028467725446233, 0.33688985339222005, 0.3826834323650898, 0.4275550934302821  …  -0.42755509343028253, -0.3826834323650904, -0.33688985339222, -0.2902846772544625, -0.24298017990326418, -0.19509032201612872, -0.1467304744553624, -0.0980171403295605, -0.04906767432741809, -2.4492935982947064e-16])

julia> plot(f)
      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” 
    1 β”‚β €β €β €β €β €β €β‘€β Šβ ‰β ‰β ‘β’€β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β”‚ 
      β”‚β €β €β €β €β’€β žβ €β €β €β €β €β €β ³β‘€β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β”‚ 
      β”‚β €β €β €β’ β ƒβ €β €β €β €β €β €β €β €β ±β‘„β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β”‚ 
      β”‚β €β €β’€β Žβ €β €β €β €β €β €β €β €β €β €β ±β‘€β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β”‚ 
      β”‚β €β’€β žβ €β €β €β €β €β €β €β €β €β €β €β €β’£β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β”‚ 
      β”‚β €β‘œβ €β €β €β €β €β €β €β €β €β €β €β €β €β €β’§β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β”‚ 
      β”‚β£°β β €β €β €β €β €β €β €β €β €β €β €β €β €β €β ˆβ‘†β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β”‚ 
      β”‚β §β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β Όβ‘€β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β’€β €β €β €β €β”‚ 
      β”‚β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β’Ήβ‘€β €β €β €β €β €β €β €β €β €β €β €β €β €β €β’ β ‡β €β €β €β €β”‚ 
      β”‚β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β’£β €β €β €β €β €β €β €β €β €β €β €β €β €β €β‘žβ €β €β €β €β €β”‚ 
      β”‚β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β ˆβ’†β €β €β €β €β €β €β €β €β €β €β €β €β‘œβ €β €β €β €β €β €β”‚ 
      β”‚β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β ˜β’†β €β €β €β €β €β €β €β €β €β €β‘΄β β €β €β €β €β €β €β”‚ 
      β”‚β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β ˜β‘„β €β €β €β €β €β €β €β €β‘°β β €β €β €β €β €β €β €β”‚ 
      β”‚β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β ˆβ’¦β €β €β €β €β €β €β‘΄β β €β €β €β €β €β €β €β €β”‚ 
   -1 β”‚β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β ³β’„β£€β£€β‘ β Šβ €β €β €β €β €β €β €β €β €β €β”‚ 
      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ 
      β €0β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €7β € 

julia> 

You can also make the evaluation lazy, for example:


julia> struct Foo2{T}
           r::T
       end

julia> plot(f::Foo2) = lineplot(f.r,sin.(f.r))
plot (generic function with 1 method)

julia> plot(f2)
      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” 
    1 β”‚β €β €β €β €β €β €β‘€β Šβ ‰β ‰β ‘β’€β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β”‚ 
      β”‚β €β €β €β €β’€β žβ €β €β €β €β €β €β ³β‘€β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β €β”‚ 
...

then the structure only stores the step range, not the actual data.

1 Like

Thanks so much for this, @Imiq. Not only did you answer my question quickly and clearly, but you also taught me about UnicodePlots, which is very handy. I really appreciate the help.

1 Like

I guess the main difference in Julia is that there is no built-in plotting functionality, so you can’t just call plot unless you’ve loaded a package that does the plotting.

How exactly you’d implement what you’re after then depends on the plotting package you’re using. One potential issue with this is that you might have to add the plotting package as a dependency of your package, which is often quite heavy. One way around that is to use plot recipes, defined in the RecipesBase package:

https://github.com/JuliaPlots/RecipesBase.jl

This allows you to just add the very lightweight RecipesBase dependency to your package, and then the user can do using YourPackage, Plots; plot(object_of_your_type) if they want to plot, but don’t have to deal with the large Plots dependency if they want to use the package without plotting.