hline/vline with subplots in Julia - julia

I'm trying to add a horizontal line to a subplot, and from this discussion: https://discourse.julialang.org/t/vline-with-subplots/25479/2, I have the following
x = [1,2,3]
y1 = 2x
y2 = x.^2
plot([x, x], [y1, y2], layout = (2, 1))
hline!([4 4])
Which produces the plots.
Now what I'm trying to do is do the horizontal line on the bottom plot, but not the top one. If I just specify hline!([4]) , it defaults to the top one. Is there a way to do the bottom one only?

It's probably best practice to plot subplots separately (as mentioned on Slack by isentropic):
x = [1,2,3]
y1 = 2x
y2 = x.^2
p1 = plot(x, y1)
p2 = plot(x, y2)
hline!(p2, [4])
plot(p1, p2, layout = (2, 1))
But if you want it all in one go, you could have used
hline!([[NaN], [4]])

The trick is to keep track of the plot handles.
p = plot([x, x], [y1, y2], layout = (2, 1))
returns a plot handle (specifically, a Plots.Plot{Plots.GRBackend} object) p with two elements, p[1] (the first subplot) and p[2] (the second subplot). To add the hline to the bottom plot only, then, you can write:
x = [1,2,3]
y1 = 2x
y2 = x.^2
p = plot([x, x], [y1, y2], layout = (2, 1))
hline!(p[2], [4])

Related

GraphMakie.jl cuts off text

I would like to visualize some graphs with labeled vertices using GraphMakie.jl.
Sadly, makie doesn't seem to include the labels into its bounding box-calculation and the labels are therefore cut off. I would expect there to be some feature to add some padding to the Axis-object, but I can't find anything like this.
Minimal example:
using Graphs, GraphMakie
fig = Figure()
for i in 1:2
ax = Axis(fig[1,i])
g = wheel_graph(i+1)
graphplot!(ax, g, nlabels=["label" for _ in 1:i+1])
hidedecorations!(ax)
end
display(fig)
Things I tried that didn't work:
adding protrusions: ax.alignmode = Mixed(right = Makie.Protrusion(50))
refreshing limits: autolimits!(ax)
changing the layout gap: colgap!(fig.layout, 50)
manually overriding the size: ax.width = 400
The area inside the rectangle is determined by the plotted range of data. Therefore, to make the labels fit in the rectangle, you have to adjust the range in the "data domain", i.e., using xlims and ylims. To add a margin to these ranges, you have to first know these ranges and hence do the call to the graph layout-function manually. I've implemented a wrapper that does this and adds a margin-property to adjust the margins:
using Graphs, GraphMakie, NetworkLayout, CairoMakie
function graphplotWithMargin!(
ax::Axis,
g::SimpleGraph;
margin = (0.1, 0.1, 0.1, 0.1),
layout = Spring(),
args...)
local pos = layout(g)
graphplot!(ax, g; layout=_->pos, args...)
local x0, x1 = minimum(map(x->x[1],pos)), maximum(map(x->x[1],pos))
CairoMakie.xlims!(ax, x0 - margin[4] * (x1 - x0), x1 + margin[2] * (x1 - x0))
local y0, y1 = minimum(map(x->x[2],pos)), maximum(map(x->x[2],pos))
CairoMakie.ylims!(ax, y0 - margin[1] * (y1 - y0), y1 + margin[3] * (y1 - y0))
end
fig = Figure()
for i in 1:2
ax = Axis(fig[1,i])
g = wheel_graph(i+1)
graphplotWithMargin!(ax, g,
nlabels=["label" for _ in 1:i+1],
margin=(0.05, 0.15, 0.05, 0.05))
hidedecorations!(ax)
end
display(fig)

Placing arrow heads to the middle of the lines in R

I have a plot where I draw arrows from points to points. I would like to put this arrow heads not to the end of the line, but to middle. Is there a simple way to do it other than placing extra arrows with half length of the according line?
My code is this:
plot(x, y, xlim=range(x), ylim=range(y), xlab="x", ylab="y", pch=16,
main="Filled Plane")
for(i in 1:20){
arrows(x[i], y[i], x[i+1], y[i+1], length = 0.25, angle = 30, col = i)
}
Make a custom function myArrow() and add one new argument cut to control the proportion of the arrows
myArrow <- function(x0, y0, x1, y1, cut = 1, ...){
x.new <- (1 - cut) * x0 + cut * x1
y.new <- (1 - cut) * y0 + cut * y1
# segments(x0, y0, x1, y1, ...)
arrows(x0, y0, x.new, y.new, ...)
}
Note1 : The computation of x.new and y.new in this custom function uses a simple mathematical concept, i.e. the Section Formula. The value of cut must be between 0 to 1.
Note2 : The use of this function is equivalent to that of the original functionarrows() other than that it has one more new argument cut.
Note3 : If you want complete lines behind the arrows, just remove the hash(#) in the function.
Plot and try different cut value. For example, I use cut = 0.7. (If you want the arrowheads to the middle, use cut = 0.5.)
# Toy Data
x <- seq(1, 5.5, by = 0.5)
y <- rep(c(1, 5), 5)
plot(x, y, pch = 16)
for(i in 1:9){
myArrow(x[i], y[i], x[i+1], y[i+1], cut = 0.7, col = i, lwd = 2)
}
Since you do not provide your x and y, I made up some data. There is no need for the loop. arrows will handle a vector of coordinates. One way is to draw a full-length arrow with no arrowhead and another that just goes halfway but has the arrowhead.
## Some bogus data
set.seed(123)
x = runif(4)
y = runif(4)
## Compute the midpoints
midx = diff(x)/2 + x[-length(x)]
midy = diff(y)/2 + y[-length(y)]
## Draw it
plot(x,y,xlim=range(x), ylim=range(y), xlab="x", ylab="y",
main="Filled Plane",pch=16)
arrows(x[-length(x)], y[-length(y)],x[-1],y[-1],
angle = 0, col = 1:3)
arrows(x[-length(x)], y[-length(y)],midx,midy,
length = 0.25, angle = 30, col = 1:3)

How to fill area between curves with Plots.jl?

Suppose I have a curve y, and two other curves u and l in the form of vectors. How to plot:
plot(y, lab="estimate")
plot!(y-l, lab="lower bound")
plot!(y+u, lab="upper bound")
That is, an asymmetric confidence interval? I know how to plot the symmetric case with the option ribbon as explained here.
The current answers are NOT correct. Here are two ways that are correct (as of v1.10.1 of Plots.jl):
Method 1: Using fillrange
plot(x, l, fillrange = u, fillalpha = 0.35, c = 1, label = "Confidence band")
Method 2: Using ribbon
plot(x, (l .+ u) ./ 2, ribbon = (l .- u) ./ 2, fillalpha = 0.35, c = 1, label = "Confidence band")
(Here, l and u denote the the "lower" and "upper" y values, respectively, and x denotes their common x values.) The key difference between these two methods is that fillrange shades the region between l and u, while the ribbon argument is a radius, i.e. half the width of the ribbon (or in other words, the vertical deviation from the midpoints).
Example using fillrange:
x = collect(range(0, 2, length= 100))
y1 = exp.(x)
y2 = exp.(1.3 .* x)
plot(x, y1, fillrange = y2, fillalpha = 0.35, c = 1, label = "Confidence band", legend = :topleft)
Let's scatter y1 and y2 on top of the plot, just to make sure we're filling in the right region.
plot!(x,y1, line = :scatter, msw = 0, ms = 2.5, label = "Lower bound")
plot!(x,y2, line = :scatter, msw = 0, ms = 2.5, label = "Upper bound")
Result:
Example using ribbon:
mid = (y1 .+ y2) ./ 2 #the midpoints (usually representing mean values)
w = (y2 .- y1) ./ 2 #the vertical deviation around the means
plot(x, mid, ribbon = w , fillalpha = 0.35, c = 1, lw = 2, legend = :topleft, label = "Mean")
plot!(x,y1, line = :scatter, msw = 0, ms = 2.5, label = "Lower bound")
plot!(x,y2, line = :scatter, msw = 0, ms = 2.5, label = "Upper bound")
(Here, x, y1, and y2 are the same as before.)
Result:
Notice that the labels for ribbon and fillrange are different in the legends: the former labels the midpoints/means, while the latter labels the shaded region itself.
Some additional comments:
The OP's answer of plot(y, ribbon=(l,u), lab="estimate") is not correct (at least for Plots v1.10.1.). I realize this thread is over 3 years old, so perhaps it worked in the earlier version of Plots.jl that the OP was using at the time)
Similar to one of the answers given,
plot(x, [mid mid], fillrange=[mid .- w, mid .+ w], fillalpha=0.35, c = [1 4], label = ["Band 1" "Band 2"], legend = :topleft, dpi = 80)
will work, but this actually creates TWO ribbons (and hence, two icons in the legend) which may or may not be what the OP was looking for. To illustrate the point:
It turns out that the option ribbon accepts both lower and upper bounds:
plot(y, ribbon=(l,u), lab="estimate")
Notice that by passing l and u in the ribbon option, the filled area will correspond to the region between y-l and y+u. In other words, l and u should be the "deviations" from the mean curve y.
Something like this? (seen here).
plot([y y], fillrange=[y.-l y.+u], fillalpha=0.3, c=:orange)
plot!(y)
The fillrange solution in #leonidas 's answer might bring an additional boundary line (at least in Plots v1.35). To remove such a line, a workaround is to specify linealpha = 0, that is,
plot(x, l, fillrange = u, fillalpha = 0.35, c = 1, label = "Confidence band", linealpha = 0)

ggplot - altering the height of each overlapping variable on a density plot

I'm quite new to R and ggplot2 so apologies if this is an obvious question, but I've searched around and can't find anything about this exact issue
I have a ggplot density plot for 6 variables on the same plot, overlapping. What I am trying to do is to change the maximum height of each variable to be a certain value without changing the distribution. e.g. :
variable_1 - 1, //on Y axis
variable_2 - 0.5 etc.
This way I can get an idea of the distribution (across the x axis) whilst also showing a second independent parameter through the y axis
Is this possible at all?
Yes this is possible although I wouldn't recommend it. What you can do is just divide the distribution by it's maximum and then multiply with the target height.
# some example data:
x = seq(-5, 5, .1)
y1 = dnorm(x)
y2 = dnorm(x, .5, .2)
Y = cbind(y1, y2)
matplot(x, Y, type = 'l', bty = 'n', lty = 1, las = 1)
# now I want the red line to be max 1
# and the black line to be mack .5
y1 = .5*y1 / max(y1)
y2 = 1*y2 / max(y2)
Y = cbind(y1, y2)
matplot(x, Y, type = 'l', bty = 'n', lty = 1, las = 1)
The important part here is that I used two different transformations for y1 and y2. The consequence is that in the second figure the distributions cannot be compared anymore. You can avoid this by only applying the same transformation to all distributions.

How to fill colors in some specific area in R?

Here is the problem:
x<-seq(0,10,length.out = 1000)
y1<-dnorm(x,mean = 2,sd=1)
y2<-dnorm(x,mean = 6,sd=1)
plot(x,y1,type="l")
lines(x,y2)
abline(v=x[380])
The graph is shown below. How can I fill 2 different colors, say red and blue, on the each side of vertical line but still below two normal density functions. I thought I can use polygon, but failed.
This is the graph without filling colors:
Here's one way:
First, we'll get the parallel minimum of your densities - this is a vector of the top y coordinates for our polygons.
y = pmin(y1, y2)
# set up your plot as in the question
plot(x, y1, type="l")
lines(x, y2)
# define a re-usable variable for the vertical line placement
x_vert = 380
abline(v = x[x_vert])
# Now we'll draw 2 polygons, one for the left side, one for the right.
# The first (x,y) pairs of the polygon are just the (x,y) coords of the
# density we're filling to, until the vertical line
# Then we need to connect the "bottom" points, which have coordinates
# (x[x_vert], 0) and (x[1], 0)
polygon(x = c(x[1:x_vert], x[x_vert], x[1]),
y = c(y[1:x_vert], 0, 0),
col = "blue")
# similar for the right hand polygon, but now going from x_vert to length(x)
polygon(x = c(x[x_vert:length(x)], x[length(x)], x[x_vert]),
y = c(y[x_vert:length(x)], 0, 0),
col = "red")
Voila!

Resources