Open IgorDouven opened 6 years ago
Here is a wip vstack Mk 2 function - feedback welcome!
using Colors, Compose, DataFrames, Gadfly
function vstack2(plots::Vector{Plot}; spacing::Float64=0.0, heights::Vector{<:Real}=Float64[])
n = length(plots)
heights==Float64[] && (heights = fill(1/n, n))
sumh = cumsum([0;heights])
vpos = sumh + spacing.*[0; 1:n-1; n-1]
M = [(context(0,v,1,h), render(p)) for (v,h,p) in zip(vpos[1:n], heights, plots)]
return compose(context(units=UnitBox(0,0,1,vpos[n+1])), M...)
end
srand(123)
D = vcat([DataFrame(x=1:5, y=rand(5), g=g) for g in [" A "," B "," C "," D "]]...)
bgcol = RGBA(0.99,0.96,0.9,0.4)
pa = plot(D[1:10,:], y=:y, xgroup=:g, Geom.subplot_grid(layer(x=:x, Geom.point, Geom.hair)), style(panel_fill=bgcol))
pb = plot(D[11:20,:], y=:y, xgroup=:g, Geom.subplot_grid(layer(x=:x, Geom.point, Geom.hair)))
# spacing and heights are in the same "relative" units
p = vstack2([pa,pb,pa], spacing=-0.8, heights=[3,4,3])
draw(PNG(6inch,6inch),p)
Thanks, but this didn't work for me. I would now have to first use hstack
to collect some plots into rows and then I would want to use vstack2
to collate those rows. However, in that case the input for the latter function is of the wrong type (Compose.Context
instead of Vector{Plot}
). Is there a way around this?
I tested this one with hstack
ed plots:
using Compose, Gadfly
function vstack2(plots::Vector{T}; spacing::Float64=0.0, heights::Vector{<:Real}=Float64[]) where T<:Union{Context,Plot}
n = length(plots)
heights==Float64[] && (heights = ones(n))
sumh = cumsum([0;heights])
vpos = sumh + spacing.*[0; 1:n-1; n-1]
i = isa.(plots, Plot)
any(i) && (plots[i] = render.(plots[i]))
M = [(context(0,v,1,h), p) for (v,h,p) in zip(vpos[1:n], heights, plots)]
return compose(context(units=UnitBox(0,0,1,vpos[n+1])), M...)
end
This worked for me as well -- great many thanks! I'm closing the issue now, though it would be nice to have this function in the documentation.
let's leave this open as adding this functionality would be useful
Yes I would like to open a PR for this sometime (with new functions for Gadfly.vstack
, Gadfly.hstack
and Gadfly.gridstack
). There is an issue here that currently Gadfly.{hv}stack
use Compose.{hv}stack
, but the new functions (e.g. vstack2
above) do not use Compose.{hv}stack
. I think Compose.{hv}stack
should remain as they are1, The question then is: what is the best way of incorporating the new Gadfly._stack
functions into Gadfly
? e.g. should they be renamed?
1Just adding that I think the idea here is to discontinue the use of the Compose._stack
functions in Gadfly
, but leave the Compose._stack
functions as they are, for people who are using Compose
to make graphics/pictures.
i'd suggest making a new function, say figure()
, which when given a column vector of Plot
s acts like vstack
, given a row vector like hstack
, and given a matrix like gridstack
. keyword arguments could replace title
and specify spacing.
would be great to also be able to do insets, as well as to align plots by their axis origins.
see https://cran.r-project.org/web/packages/egg/vignettes/Ecosystem.html for how R implements all of this functionality.
my only hesitation is that this approach is not declarative.
I like the suggestion of a figure()
function (tbd). I view it as a helper function, for users who want that. If you want declarative graphics than that functionality is in Compose
e.g. here is an inset plot:
using Compose, DataFrames, Gadfly
pa = plot(XPI, x=:Year, y=:TCC, Geom.line,
Coord.cartesian(ymax=150),
Theme(default_color="red", panel_fill="CornSilk") )
pb = plot(XPI, x=:Year, y=:CPI, Geom.line, Coord.cartesian(ymin=10),
Guide.ylabel("CPI (\$US '000s)"),
Guide.title("Xmas Price Index
(https://en.wikipedia.org/wiki/Christmas_Price_Index)"),
Guide.annotation(compose(context(0w,0h,0.75w,0.5h), render(pa))),
Theme(default_color="green")
)
One issue with figure
is that it's used to open and manage figures in Matlab-like figure manager. But since it's either without argument or just an index it could be fine.
What about a inset
function for inset, that work like layer
with additional position/size arguments ?
plot(
layer(x=x, y=y, Geom.point)
inset(x=z, y=w, Geom.line, position=(0w,0h,0.75w,0.5h) ),
)
In Gadfly Grammar of Graphics, my plot above could be done perhaps like this:
plot( layer(x=Year, y=CPI, Geom.line, Theme(...)),
layer(x=Year, y=TCC, Geom.inset(Geom.line, pos=(0.0w,0.0h,0.75w,0.5h)), Theme(...) )
)
similar in syntax to Geom.subplot_grid()
.
Let me put this here too. It would be really nice, if one had the option to use the same legend or title or axis labels for a stack of plots. Geom.subplot_grid
assumes there is some sort of categorized structure to your data, so it doesn't satisfy the simple cases where you basically just want to display the same data in different scales without repeating the legend.
Also, I don't know how to use the compose
workarounds for a legend.
After some googling, I figured an R's ggplot2
approach would be to extract the legend from one plot and then create a custom layout and place each plot and the legend in their corresponding cells. grid_arrange_shared_legend
Is there a way to do the same in Julia's Gadfly?
Here's my quick attempt at such a plot:
using Compose, DataFrames, Gadfly, RDatasets
mtcars = sort(dataset("datasets", "mtcars"), :Cyl)
myplot(D) = plot(D, x=:MPG, y=:WT, color=:Cyl, Geom.point,
Scale.color_discrete, Theme(key_position=:none, plot_padding=[1mm]))
Dp = by(mtcars, :Cyl, myplot)
p1 = plot(mtcars, x=:MPG, y=:WT, color=:Cyl, Geom.point,
Scale.color_discrete, Theme(key_position=:none, plot_padding=[1mm]))
p2 = gridstack(reshape(Union{Plot,Compose.Context}[Dp.x1; context()], 2, 2))
gck = Guide.manual_color_key("", ["4","6","8"].*"\t\t", Scale.color_discrete().f(3))
p3 = compose(context(0.4,0.5), render(gck, Theme(), Gadfly.Aesthetics())[1].ctxs[3])
# vstack2 is in a post above
vstack2([hstack(p1, p2); p3], heights=[9,1])
There are some color issues in p2, and the development of a Geom.subplot_wrap
would help (#855).
Example 2
gck = Guide.manual_color_key("Cylinders", ["4","6","8"].*"\t\t", Scale.color_discrete().f(3))
p3 = compose(context(),(context(0.5,0), render(gck, Theme(), Gadfly.Aesthetics())[1].ctxs[1]))
p2 = gridstack(reshape(Union{Plot,Compose.Context}[Dp.x1; p3], 2, 2))
hstack(p1, p2)
This actually helps a lot. Thanks.
Is there a way to fine-tune spacings when using
hstack
/vstack
/gridstack
? I tried to control this viaplot_padding
for the individual plots that are being stacked, but this doesn't allow me to reduce vertical space between the rows (which is badly needed in my plot). Something likeSpacings
in Mathematica'sGraphicsGrid
would be nice to have.