How can I increase the space between subplots in Plots.jl?
Minimal non-working example:
julia> using Plots; pyplot()
Plots.PyPlotBackend()
julia> data = [rand(100), rand(100)];
histogram(data, layout=2, title=["Dataset A" "Dataset B"], legend=false)
ylabel!("ylabel")
If you make the figure small enough, the y label of the second plot collides with the first plot.
In the attributes part of Plots.jl documentation, there is a section called Subplot. There, you will find the keywords margin, top_margin, bottom_margin, left_margin and right_margin that might help you.
Minimal working example would be, then:
using Plots, Measures
pyplot()
data = [rand(100), rand(100)];
histogram(data, layout = 2,
title = ["Dataset A" "Dataset B"], legend = false,
ylabel = "ylabel", margin = 5mm)
Note the using Measures part, by the way. I hope this helps.
Another workaround would be using the bottom_margin keyword argument holding the pyplot backend like this:
using Plots
pyplot()
x1 = rand(1:30, 20);
x2 = rand(1:30, 20);
# subplot 1
p1 = plot(
x1,
label="x1 value",
title="x1 line plot",
ylabel="x1",
bottom_margin=50*Plots.mm,
);
# subplot 2
p2 = plot(
x2,
label="x2 value",
title="x2 line plot",
xlabel="sample",
ylabel="x2",
);
plot(
p1,
p2,
layout=grid(
2,1,
)
)
An alternative is to use the empty figure object _ to occupy the space. It works well when a long legend name overlaps with another subplot with PGFPlotsX backend,
pgfplotsx()
p1 = plot(1:10, label = "a long label name")
p2 = plot(1:10)
plot(p1, p2)
we can use _ in #layout to leave more space for the legend of the first subplot,
plot(p1, p2, layout=#layout([a{0.5w} _ b{0.3w}]))
It can even handle more complicated cases. For example, you might just want to increase the space between two specific subplots instead of all subplots. For example, I use the setting
layout = #layout([grid(2, 1){0.3w} _ grid(2, 1){0.3w} b{0.33w}])
to leave more space via _ for the legend for the left two subplots grid(2,1), but do not touch other subplots.
Related
I would like to create one legend for a layout plot in Julia. Here is some reproducible code:
using Plots
plot(rand(100, 4), layout = 4, color = [:red :blue :green :yellow])
Output:
As you can see it shows nicely a legend to each subplot, but I would like to have one shared legend, using :shared doesn't work. So I was wondering if anyone knows how to add a shared legend to a layout plot in Julia?
Maybe this answer can give an idea. I can do it by a trick (creating a transparent plot for the legend). The only problem is that I don't know how I can move the legend (actually, the transparent plot) to the location I want:
colors = [:red :blue :green :yellow]
p = [
plot(col, legend=false, color=colors[idx])
for (idx, col) in enumerate(eachcol(data))
]
p1 = plot((1:4)', legend=true, framestyle=:none, color=colors)
plot(
p...,
p1,
layout = #layout[
[
grid(2, 2)
p
] p1
],
size = (1000, 1000)
)
result of savefig:
If I change the layout like the following:
layout = #layout[
p1 [
grid(2, 2)
p
]
]
# And change the order of the plots
plot(
p1,
p...,
⋮
)
Then I get this:
Note that I cropped the redundant area of the image above.
When using latticeExtra:::c.trellis to combine plots, the right-side tick marks and text/numeric labels go missing, and I'd like to bring them back:
library(latticeExtra)
set.seed(1)
foo <- data.frame(x = 1:100,
y = 1:100 + rnorm(100))
foo$resid <- with(foo, x-y)
## Plot 1 -----
(p1 <- xyplot(y~x, foo))
## Plot 2 -----
(p2 <-
xyplot(resid~x, foo,
scales = list(rot = 0, tck = c(1,1), alternating = 3),
between = list(y = 1), ylab.right = "ylab.right",
# par.settings = list(axis.components =
# list(right = list(pad1 = 2, pad2 = 2)))
# Note: this padding attempt does not restore the missing ticks,
# pad arguments get ignored when using c.trellis below
))
# tick marks appear on all four sides (as desired)
## Combine -----
(p12 <- latticeExtra:::c.trellis(p2, p1,layout = c(1,2)))
# right tick marks are missing
Is there a way to restore the right-side ticks and/or labels manually, say, by modifying the combined trellis object?
From the help file ?c.trellis:
Description
Combine the panels of multiple trellis objects into one.
and later,
Note that combining panels from different types of plots does not really fit the trellis model. Some features of the plot may not work as expected. In particular, some work may be needed to show or hide scales on selected panels. An example is given below.
It looks to me that you really aren't trying to combine panels into one object. You even use between to put some separation. Rather, you are trying to combine two plots.
You can use print,
print(p1,split=c(1,1,1,2),more=TRUE)
print(p2,split=c(1,2,1,2),more=FALSE)
See ?print.trellis.
I have a list of plots that I have assigned names to, and then converted to plot titles as suggested by https://stackoverflow.com/a/14790376/9335733. The titles happen to appear over the top x-axis title and so I attempt to move them as suggested here: https://stackoverflow.com/a/44618277/9335733. The overall code looks as follows:
lapply(names(Cast.files), function (x) plot(Cast.files[[x]],
main = x,
adj = 0, #adjust title to the farthest left
line =2.5 #adjust title up 2.5
)
)
It should be noted that plot is now converted from base R to the oce package for analyzing oceanographic data, but calls the same arguments from base R plot.
The problem becomes that in trying to move the title, the axis labels move as well and overlap. Any suggestions?
Edit: Here is what the image looks like before:
And after:
You might also want to look into the oma= argument in par(), which provides an "outer" margin which can be used to put a nice title. Something like:
library(oce)
data(ctd)
par(oma=c(0, 0, 1, 0))
plot(ctd)
title('Title', outer=TRUE)
This was solved by adding a title argument outside of the plot function as follows:
lapply(names(Cast.files), function (x) plot(Cast.files[[x]],
which = c("temperature", "salinity", "sigmaT","conductivity"),
Tlim = c(11,12),
Slim = c(29,32),
col = "red")
+ title(main = x, adj = 0.48, line = 3.5)#adding the titles at a specific location
)
This allowed for plots that looked like:
If you use the title function, rather than setting main within plot, it would allow you to change the line without affecting anything else in the plot.
I would like to add a global title to a group of subplots using Plots.jl.
Ideally, I'd do something like:
using Plots
pyplot()
plot(rand(10,2), plot_title="Main title", title=["A" "B"], layout=2)
but, as per the Plots.jl documentation, the plot_title attribute is not yet implemented:
Title for the whole plot (not the subplots) (Note: Not currently implemented)
In the meanwhile, is there any way around it?
I'm currently using the pyplot backend, but I'm not especially tied to it.
This is a bit of a hack, but should be agnostic to the backend. Basically create a new plot where the only contents are the title you want, and then add it on top using layout. Here is an example using the GR backend:
# create a transparent scatter plot with an 'annotation' that will become title
y = ones(3)
title = Plots.scatter(y, marker=0,markeralpha=0, annotations=(2, y[2], Plots.text("This is title")),axis=false, grid=false, leg=false,size=(200,100))
# combine the 'title' plot with your real plots
Plots.plot(
title,
Plots.plot(rand(100,4), layout = 4),
layout=grid(2,1,heights=[0.1,0.9])
)
Produces:
More recent versions of Plots.jl support the plot_title attribute, which provides a title for the whole plot. This can be combined with individual titles of individual plots.
using Plots
layout = #layout [a{0.66w} b{0.33w}]
LHS = heatmap(rand(100, 100), title="Title for just the heatmap")
RHS = plot(1:100, 1:100, title="Only the line")
plot(LHS, RHS, plot_title="Overall title of the plot")
Alternatively, you can set the title for an existing plot directly.
p = plot(LHS, RHS)
p[:plot_title] = "Overall title of the plot"
plot(p)
When using the pyplot backend, you can use PyPlot commands to alter a Plots figure, cf. Accessing backend specific functionality with Julia Plots.
To set a title for the whole figure, you could do something like:
using Plots
p1 = plot(sin, title = "sin")
p2 = plot(cos, title = "cos")
p = plot(p1, p2, top_margin=1cm)
import PyPlot
PyPlot.suptitle("Trigonometric functions")
PyPlot.savefig("suptile_test.png")
One needs to explicitly call PyPlot.savefig to see the effect of the PyPlot functions.
Note that all changes made using the PyPlot interface will be overwritten when you use a Plots function.
subplots are fields of the Plot type, and each subplot has a field called :attr that you can modify and re-display() the plot. Try the following:
julia> l = #layout([a{0.1h} ;b [c; d e]])
Plots.GridLayout(2,1)
julia> p = plot(randn(100,5),layout=l,t=[:line :histogram :scatter :steppre :bar],leg=false,ticks=nothing,border=false)
julia> p.subplots
5-element Array{Plots.Subplot,1}:
Subplot{1}
Subplot{2}
Subplot{3}
Subplot{4}
Subplot{5}
julia> fieldnames(p.subplots[1])
8-element Array{Symbol,1}:
:parent
:series_list
:minpad
:bbox
:plotarea
:attr
:o
:plt
julia> for i in 1:length(p.subplots)
p.subplots[i].attr[:title] = "subtitle $i"
end
julia> display(p)
You should now see a title in each subplot
I would like the title to be centered above the two plots in my mfrow object. Indeed, I have just that working, but the title is too far above the plots, and is only half included because of this. Below is my code showing the issue. How can I move the title down to just above the plots in the center?
a=1
p = c(rnorm(1000,5,10))
l = c(rnorm(1000,10,5))
par(mfrow=c(1,2))
hist(p,main=NULL)
hist(l,main=NULL)
title(main=print(paste0("Histogram a=", a)),outer=T)
If you set the line parameter in the title call to, for example, -2, you can place the title closer to the histograms:
a=1
p = c(rnorm(1000,5,10))
l = c(rnorm(1000,10,5))
par(mfrow=c(1,2))
hist(p,main=NULL)
hist(l,main=NULL)
title(main=print(paste0("Histogram a=", a)),outer=T, line=-2)