Plotting-package-independent API

293 views
Skip to first unread message

David van Leeuwen

unread,
Jan 22, 2015, 10:42:37 AM1/22/15
to julia...@googlegroups.com
Hello, 

I've just read up on most recent conversations mentioning plotting, after I went through a similar install-and-try-out cycle for the various plotting packages. 

I am busy with a package named ROC (Receiver Operating Characteristic).  It is mostly about decision cost functions and efficiently computing ROC statistics, but there is also some support for making various plots related to the ROC. 

I do not want to decide for a potential user of this package which plotting package she should use.  So it would be nice if there would be an abstract plotting API (I suppose this does not exist yet). 

Supposing that it is possible to make an API that includes some common plotting primitives, and that makes a runtime selection based on `isdefined(plottingpackagename)`, how would you 
 - overload a function, typically `plot()`, when at module-include time it is not yet known which plotting package the user wants
 - specify the dependence of this package to any one of the available plotting packages.  

Thoughts?

Thanks, 

---david

Simon Danisch

unread,
Jan 22, 2015, 11:13:05 AM1/22/15
to julia...@googlegroups.com
Well, I'm planning to introduce some functionality similar to show, building upon a rich type system.
What I have in mind is something like this:

show(::Matrix{Z}; parameters...)       => Surface
show(::Matrix{Red}; parameters...)   =>  Red channel
show(::Matrix{RGB}; parameters...) => Image
show(::Matrix{Vector2}; parameters...) => Vectorfield
show(::Vector{Y}; parameters...) => Line plot
show(::Vector{Point2D}; parameters...) => scatter plot
show(::Vector{Point3D}; parameters...) => 3D point cloud
show(::(Vector{Point3D}, Vector{Face}); parameters...) => mesh

This is nice for visual debugging, but probably not sufficient for more complicated plots and annotations.

David van Leeuwen

unread,
Jan 22, 2015, 12:20:57 PM1/22/15
to julia...@googlegroups.com
Hello, 


On Thursday, January 22, 2015 at 5:13:05 PM UTC+1, Simon Danisch wrote:
Well, I'm planning to introduce some functionality similar to show, building upon a rich type system.
What I have in mind is something like this:

show(::Matrix{Z}; parameters...)       => Surface
show(::Matrix{Red}; parameters...)   =>  Red channel
show(::Matrix{RGB}; parameters...) => Image
show(::Matrix{Vector2}; parameters...) => Vectorfield
show(::Vector{Y}; parameters...) => Line plot
show(::Vector{Point2D}; parameters...) => scatter plot
show(::Vector{Point3D}; parameters...) => 3D point cloud
show(::(Vector{Point3D}, Vector{Face}); parameters...) => mesh

This is nice for visual debugging, but probably not sufficient for more complicated plots and annotations.

This is cool, and I think you can do things this way because `show()` is defined in Base, and defines the API. 

However, the various `plot()` etc defined in modules Winston, PGFSPlots, etc do not overload a `Base.plot()`, i.e., there is no API defined for the most common plotting calls AFAIK.  So I am still struggling how I can overload a `plot()` without `using` a specific plotting module like Winston, PGFSPlots in my module---because I want to leave that choice to the user. 

---david

Daniel Jones

unread,
Jan 22, 2015, 12:28:04 PM1/22/15
to julia...@googlegroups.com

The issue now is that there are several plotting packages, each with advantages and disadvantages, forcing users to make a choice often from limited information. I'm afraid defining another plotting API in Base, rather than simplifying the matter, will just create another choice you'll have to evaluate. It pains me to reference xkcd 927, since it's so often over-applied, but it seems applicable here.

Simon's example of defining show functions for vaguely graphical types could be useful if it's kept simple, but the more complicated/featureful it becomes, the more it will start to exasperate the problem.

j verzani

unread,
Jan 22, 2015, 1:03:40 PM1/22/15
to julia...@googlegroups.com
I don't know if it is a good idea or not, but using the `Requires` package is one way to do this. That package's `@require` macro lets you define functions when another package is loaded, such as a plotting package.  A pattern like this can be used:

using Requires
Requires.@require PyPlot begin
    ....
end

Requires.@require Gadfly begin
    ....
end

Simon Danisch

unread,
Jan 22, 2015, 1:03:44 PM1/22/15
to julia...@googlegroups.com
The reference does seem to fit here, as a general plotting language seems to be awfully complicated. 
By the way, I totally forgot to mention, that I want to turn GLPlot more into a meta package, offering different plotting interfaces, since I'm generalizing more and more of the render code and a lot of people prefer different interfaces to the same functionality.
So it might become a lightweight package, which actually doesn't contain render code and only defines interfaces.
If someone has any API that he wants to implement, GLPlot might become the go to package. If this happens, I should rename it though ;)


Am Donnerstag, 22. Januar 2015 16:42:37 UTC+1 schrieb David van Leeuwen:

Matt Bauman

unread,
Jan 22, 2015, 3:36:49 PM1/22/15
to julia...@googlegroups.com
I've thought some about this in the past, too.  Coming from Matlab, I've imagined a Matlab-esque Base.Plots module that has just a handful of definitions, mirroring the IO system:

abstract AbstractPlot
immutable
NoPlots <: AbstractPlot end
STDPLOT
= NoPlots() # When a plotting package loads, it assigns its own AbstractPlot object to STDPLOT

line
(args...) = line(STDPLOT, args...)
line
(p::AbstractPlot, ys::AbstractArray) = line(p, 1:size(ys,1), ys)
# Then each plotting package would specialize on the following method:
line
(::NoPlots, xs, ys) = error("Plotting requires a plotting package to be installed.  Here, try one of these: …")

# … and so on for just a handful of very common (and specific!) names …
scatter
()
bar
()
surface
()

Of course, doing anything more complicated would require delving into documentation and using a more package-specific function.  But users could easily start from the source for these simple prototypes and grow from there.  Sure, this will be heretical for some plotting packages, but I think there's value here, especially for quick and dirty exploratory data analysis.

The plot function is tougher — there's not really a meaning to it beyond "show this thing graphically," and packages use keywords or other arguments to determine the kind of plot.  I could see it being useful in cases like yours, though, where you want to define a plotting routine for a your own ROC object type.  You would simply define `plot(p::AbstractPlot, t::ROCType)` and then use the above primitives.  The trouble is, as a package author, you'll want to add fancier features like labels, legends, colors, titles, bounds, error-bars, shaded regions, transforms, etc…. and now we're marching down that crazy never-ending path Daniel fears.  

So: yes, it's possible.  Is it useful? I'm not so sure.  I think no matter what you're going to have to learn a specific plotting API, and as a package author I think you're better off picking one to support well and extending its functions directly.  Maybe some cavalier users will come by and add support for others they like with lazy loading through Requires.jl. :)

Matt

Tom Short

unread,
Jan 22, 2015, 8:19:28 PM1/22/15
to julia...@googlegroups.com
Sims.jl uses the Requires.jl approach that Matt mentioned. There are a few helper plotting methods that are optional depending on what packages the user loads:

https://github.com/tshort/Sims.jl/blob/master/src/utils.jl#L158-L181

https://tshort.github.io/Sims.jl/plotting/


David van Leeuwen

unread,
Jan 23, 2015, 2:32:15 AM1/23/15
to julia...@googlegroups.com
Thanks for all the suggestions---the Require package is probably what I go for now.  Looking at Sims.jl, it is probably not the most fun implementing the various plots I want to make in the  many plotting environments available in Julia, the least of which are to get all those environments working locally, and writing tests that are both useful and manage to pass Travis...

I totally understand the problems of standardizing a plotting API, and I suppose every plotting package author sincerely believes her package is closest to the one and only true ideal general multipurpose API---at least, I would.  I've used many different plotting environments in the past (and of course, a long time ago, written some for my own use in different languages and targeted at different output hardware), but I would find it hard to give objective arguments as to what interface is better---so  much depends on what you're used to, and what your needs are.  

I think Matt's approach looks promising.  I can imagine different plotting packages might have completely different approaches to how a plot should be built up, what the order of things are, etc, so it won't be easy to do.  

Cheers, 

---david

Yuuki Soho

unread,
Jan 23, 2015, 5:16:59 AM1/23/15
to julia...@googlegroups.com
It seems there's two levels where things could be standardized, for me a good plotting API primarily need to provide good primitives: dot, line, rectangle, ellipse, patch, text, ...

I don't know if those could be standardized so that plotting methods could be written in a rendering back-end independent manner. 

Then there's the higher level methods like plot, show, piechart...
Reply all
Reply to author
Forward
0 new messages