In Julia, I want to stack heatmaps in one plot, just how it was done with Matlab in this stackoverflow post:
I need a function to display matrices stacked
Preferably with the same color bar for all heatmaps, and with the ability to specify the position of each plane (i.e. along the third dimension).
I know how to stack surface plots by adding an offset to each surface (see this page: https://plotly.com/julia/3d-surface-plots/), but this is not what I want to achieve as a surface plot is not flat, but, as the name suggests, a surface. My workaround currently is to use an offset large enough that each surface appears to be flat, but as the third axis relates to the real world height of my measurements, I am not happy with this fix.
What I would prefer is a parameter positions_z = [z1, z2, z3, ...] that specifies the location of all heatmaps along the third axis, but I am also happy with workarounds.
Does anyone know a solution?
Here's how you can do it in Makie.jl:
using GLMakie
xs = range(-1, 1, length=10)
heights = 1:5
data = reshape(heights, 1, 1, :) .* (xs .^2 .+ xs' .^2);
fig = Figure()
ax = Axis3(fig[1, 1], aspect=(1, 1, 1), elevation=π/16)
cr = (minimum(data), maximum(data)) # color range to use for all heatmaps
for i in 1:size(data, 3)
hm = heatmap!(ax, xs, xs, data[:, :, i], colorrange=cr)
translate!(hm, 0, 0, heights[i])
i == 1 && Colorbar(fig[1, 2], hm) # add the colorbar once
end
zlims!(ax, minimum(heights), maximum(heights))
fig
Here is the solution using PlotlyJS.jl. A plane is a surface, hence you can plot the heatmaps as surfaces:
using PlotlyJS
f(x,y,z) = cos(x)+cos(y)+cos(z)
n=200
xl = range(-2,2, length=n)
yl = range(-2,2, length=n)
y = yl .* ones(n)'
x = ones(n) * xl'
h = -2:1
fig = Plot()
for hp in h
z= hp*ones(size(x))
surfcolor = f.(x,y,z)
addtraces!(fig, surface(x=x, y=y, z=z, coloraxis="coloraxis", surfacecolor=surfcolor));
end
relayout!(fig, font_family="Open Sherif", font_size=11, width=400, height=400,
margin=attr(t=10, r=10, b=10, l=4),
coloraxis=attr(colorscale=colors.viridis,
colorbar_len=0.65,
colorbar_thickness=24),
scene_camera_eye=attr(x=1.8, y=1.8, z=1))
display(fig)
To save your figure as a png file you should use the following settings for savefig:
savefig(fig, "parallelheatmaps.png", width=400, height=400, scale=1)
But for publication perhaps you need a pdf file with dpi=300. In this case save as:
savefig(fig, "parallelheatmaps.pdf", width=300*3, height=300*3, scale=1)
where 3 is the width and height in inches.
Related
How can such a non-linear transformation be done?
here is the code to draw it
my.sin <- function(ve,a,f,p) a*sin(f*ve+p)
s1 <- my.sin(1:100, 15, 0.1, 0.5)
s2 <- my.sin(1:100, 21, 0.2, 1)
s <- s1+s2+10+1:100
par(mfrow=c(1,2),mar=rep(2,4))
plot(s,t="l",main = "input") ; abline(h=seq(10,120,by = 5),col=8)
plot(s*7,t="l",main = "output")
abline(h=cumsum(s)/10*2,col=8)
don't look at the vector, don't look at the values, only look at the horizontal grid, only the grid matters
####UPDATE####
I see that my question is not clear to many people, I apologize for that...
Here are examples of transformations only along the vertical axis, maybe now it will be more clear to you what I want
link Source
#### UPDATE 2 ####
Thanks for your answer, this looks like what I need, but I have a few more questions if I may.
To clarify, I want to explain why I need this, I want to compare vectors with each other that are non-linearly distorted along the horizontal axis .. Maybe there are already ready-made tools for this?
You mentioned that there are many ways to do such non-linear transformations, can you name a few of the best ones in my case?
how to make the function f() more non-linear, so that it consists, for example, not of one sinusoid, but of 10 or more. Тhe figure shows that the distortion is quite simple, it corresponds to one sinusoid
and how to make the function f can be changed with different combinations of sinusoids.
set.seed(126)
par(mar = rep(2, 4),mfrow=c(1,3))
s <- cumsum(rnorm(100))
r <- range(s)
gridlines <- seq(r[1]*2, r[2]*2, by = 0.2)
plot(s, t = "l", main = "input")
abline(h = gridlines, col = 8)
f <- function(x) 2 * sin(x)/2 + x
plot(s, t = "l", main = "input+new greed")
abline(h = f(gridlines), col = 8)
plot(f(s), t = "l", main = "output")
abline(h = f(gridlines), col = 8)
If I understand you correctly, you wish to map the vector s from the regular spacing defined in the first image to the irregular spacing implied by the second plot.
Unfortunately, your mapping is not well-defined, since there is no clear correspondence between the horizontal lines in the first image and the second image. There are in fact an infinite number of ways to map the first space to the second.
We can alter your example a bit to make it a bit more rigorous.
If we start with your function and your data:
my.sin <- function(ve, a, f, p) a * sin(f * ve + p)
s1 <- my.sin(1:100, 15, 0.1, 0.5)
s2 <- my.sin(1:100, 21, 0.2, 1)
s <- s1 + s2 + 10 + 1:100
Let us also create a vector of gridlines that we will draw on the first plot:
gridlines <- seq(10, 120, by = 2.5)
Now we can recreate your first plot:
par(mar = rep(2, 4))
plot(s, t = "l", main = "input")
abline(h = gridlines, col = 8)
Now, suppose we have a function that maps our y axis values to a different value:
f <- function(x) 2 * sin(x/5) + x
If we apply this to our gridlines, we have something similar to your second image:
plot(s, t = "l", main = "input")
abline(h = f(gridlines), col = 8)
Now, what we want to do here is effectively transform our curve so that it is stretched or compressed in such a way that it crosses the gridlines at the same points as the gridlines in the original image. To do this, we simply apply our mapping function to s. We can check the correspondence to the original gridlines by plotting our new curves with a transformed axis :
plot(f(s), t = "l", main = "output", yaxt = "n")
axis(2, at = f(20 * 1:6), labels = 20 * 1:6)
abline(h = f(gridlines), col = 8)
It may be possible to create a mapping function using the cumsum(s)/10 * 2 that you have in your original example, but it is not clear how you want this to correspond to the original y axis values.
Response to edits
It's not clear what you mean by comparing two vectors. If one is a non-linear deformation of the other, then presumably you want to find the underlying function that produces the deformation. It is possible to create a function that applies the deformation empirically simply by doing f <- approxfun(untransformed_vector, transformed_vector).
I didn't say there were many ways of doing non-linear transformations. What I meant is that in your original example, there is no correspondence between the grid lines in the original picture and the second picture, so there is an infinite choice for which gridines in the first picture correspond to which gridlines in the second picture. There is therefore an infinite choice of mapping functions that could be specified.
The function f can be as complicated as you like, but in this scenario it should at least be everywhere non-decreasing, such that any value of the function's output can be mapped back to a single value of its input. For example, function(x) x + sin(x)/4 + cos(3*(x + 2))/5 would be a complex but ever-increasing sinusoidal function.
How can I make the lines for the x- and y-axes thicker in Julia Plots?
Is there a simple way to achieve this?
MWE:
using Plots
Nx, Ny = 101,101
x = LinRange(0, 100, Nx)
y = LinRange(0, 100, Ny)
foo(x,y; x0=50, y0=50, sigma =1) = exp(- ((x-x0)^2 + (y-y0)^2)/(2*sigma^2) )
NA = [CartesianIndex()] # for "newaxis"
Z = foo.(x[:,NA], y[NA,:], sigma=10);
hm = heatmap(x, y, Z, xlabel="x", ylabel="y", c=cgrad(:Blues_9), clim=(0,1))
plot(hm, tickfontsize=10, labelfontsize=14)
Leads to:
The posts I found so far suggested that this was not possible:
https://discourse.julialang.org/t/plots-jl-modify-frame-thickness/24258/4
https://github.com/JuliaPlots/Plots.jl/issues/1099
It this still so?
The actual code for my plot is much longer.
I would not like to rewrite all of it in a different plot library.
Currently, there does not seem to be an attribute for axes thickness in Plots.jl.
As a workaround, you may use the attribute thickness_scaling, which will scale the thickness of everything: lines, grid lines, axes lines, etc. Since you only want to change the thickness of axes, you need to scale down the others. Here is your example code doing that using pyplot backend.
using Plots
pyplot() # use pyplot backend
Nx, Ny = 101,101
x = LinRange(0, 100, Nx)
y = LinRange(0, 100, Ny)
foo(x,y; x0=50, y0=50, sigma =1) = exp(- ((x-x0)^2 + (y-y0)^2)/(2*sigma^2) )
NA = [CartesianIndex()] # for "newaxis"
Z = foo.(x[:,NA], y[NA,:], sigma=10);
hm = heatmap(x, y, Z, xlabel="x", ylabel="y", c=cgrad(:Blues_9), clim=(0,1))
plot(hm, tickfontsize=10, labelfontsize=14) # your previous plot
# here is the plot code that shows the same plot with thicker axes on a new window
# note that GR backend does not support `colorbar_tickfontsize` attribute
plot(hm, thickness_scaling=2, tickfontsize=10/2, labelfontsize=14/2, colorbar_tickfontsize=8/2, reuse=false)
See Julia Plots Documentation for more about plot attributes.
A simple workaround where you do not need to add attributes for all the fonts is to add verticle and horizontal lines at the limits for x and y of the plots. For example, if I have a figure fig with 4 subplots, each with the same bounds, I can use this to get a thicker box frame:
for i ∈ 1:4
vline!(fig[i], [xlim_lb, xlim_ub],
linewidth=3,
color=:black,
label=false)
hline!(fig[i], [ylim_lb, ylim_ub],
linewidth=3,
color=:black,
label=false)
end
or for the original example here, add this to the end:
frame_thickness = 5
vline!([x[1], x[end]], color=:black, linewidth=frame_thickness, label=false)
hline!([y[1], y[end]], color=:black, linewidth=frame_thickness, label=false)
I am trying get familiar with packages in Julia like Interpolations.jl and Plots.jl. The contour plot in Julia using the GR backend is very simple as shown in the link:
https://docs.juliaplots.org/latest/examples/gr/#contours
And to carry out interpolation using Interpolations.jl:
https://github.com/JuliaMath/Interpolations.jl/blob/master/doc/Interpolations.jl.ipynb
I tried to make a contour plot using an interpolated function as following:
using Interpolations
using Plots
gr()
xs = 1:5
ys = 1:8
g = Float64[(3x + y ^ 2) * abs(sin(x) + cos(y)) for x in xs, y in ys]
gitp_quad2d = interpolate(g, BSpline(Quadratic(Line(OnCell()))))
xc = 1:0.1:5
yc = 1:0.1:5
p1 = contour(xc, yc, gitp_quad2d, fill=true)
plot(p1)
However this gives a plot without any contour curves on it, with a message saying "Arrays have incorrect length or dimension.". But the contour function seems to accept, as in the link above, arbitrary x, y arrays and a function of x, y, and returns a contour plot. This should not give rise to any dimension problems. What is wrong with the code?
[Edit]
using Interpolations
using Plots
gr()
xs = 1:0.5:5
ys = 1:0.5:8
g = Float64[(3x + y ^ 2) for x in xs, y in ys]
f(x, y) = (3x + y ^ 2)
g_int = interpolate(g, BSpline(Quadratic(Line(OnCell()))))
gs_int = scale(g_int, xs, ys)
xc = 1:0.1:5
yc = 1:0.1:5
println("gs_int(3.2, 3.2) = ", gs_int(3.2, 3.2))
println("f(3.2, 3.2) = ", f(3.2, 3.2))
p1 = contour(xs, ys, gs_int(xs, ys), fill=true)
p2 = contour(xc, yc, f, fill=true)
plot(p1, p2)
The result:
The interpolation seems to work fine but the result from the contour plot doesn't seem to convey the same message.
You need to specify at what points you want the interpolation to happen - otherwise you will just get the input resolution of the interpolated object, which is different from that of the new xc and yc (try size(gitp_quad2d). There isn't a recipe built into Plots for doing that automatically on the x and y inputs.
Try
contourf(xc, yc, gitp_quad2d[xc, yc])
EDIT: updated to reflect update of question
On your plot, the reason you have the contour looking strangely is that your interpolated matrix is transposed relative to the x and y variables. The transposition expected of contour/heatmaps is always a discussion in plotting (should it be the same as matrices, as a normal plot or as an image? - see this issue for a good discussion https://github.com/JuliaPlots/Makie.jl/issues/205). Anyway, transposing it back will help (either p1 = contourf(xs, ys, gs_int(xs, ys)') or p1 = contourf(xs, ys, gs_int(xs, ys), transpose = true)
Using the Plots.jl package in Julia, I am able to use various backends to make a scatter plot based on two vectors x and y
k = 100
x = rand(k)
y = rand(k)
scatter(x, y)
I am unable to find information about how to color them according to some length k vector z. How do you do that?
The following method will be much better than jverzani's (you don't want to create a new series for every data point). Plots could use some additional love for manually defining color vectors, but right now gradients are pretty well supported, so you can take advantage of that.
using Plots
pyplot(size=(400,200), legend=false) # set backend and set some session defaults
scatter(rand(30),
m = ColorGradient([:red, :green, :blue]), # colors are defined by a gradient
zcolor = repeat( [0,0.5,1], 10) # sample from the gradient, cycling through: 0, 0.5, 1
)
I would have thought if you defined k as a vector of color symbols this would work: scatter(x, y, markercolors=k), but it doesn't seem to. However, adding them one at a time will, as this example shows:
using Plots
xs = rand(10)
ys = rand(10)
ks = randbool(10) + 1 # 1 or 2
mcols = [:red, :blue] # together, mcols[ks] is the `k` in the question
p = scatter(xs[ks .== 1], ys[ks .== 1], markercolor=mcols[1])
for k = 2:length(mcols)
scatter!(xs[ks .== k], ys[ks .== k], markercolor=mcols[k])
end
p
If the elements in vector z are categorical rather than continuous values, you might want to consider using the group parameter to the plotting call as follows:
using Plots
# visualize x and y colouring points based on category z
scatter(x, y, group=z)
I am talking about this picture:
Questions:
This is R, not Matlab right? Below the page it says it was made with R....
How can I do this? I mean, how can I create such a 3d scatterplot with this advanced green surface and this grid? I now how to make simple scatterplots and also 3d scatterplots, but how can I create such an advanced picture? Which package is this?
I want to include it in a paper where this picture should rotate automatically. I know how to include this into my tex-distribution, but therefore I need single png. So e.g. 1000 single pictures which I animate. But how can I get those with R? I would need to rotate it and then save every single small rotation as a graphic file.
Thanks a lot for your help, my biggest problems are the creation of this graphic (packages?) and how to make it rotate (r code?)
To create this figure, you might check out persp function. You can change the parameter to rotate the figure. Here's one demo:
require(grDevices) # for trans3d
x <- seq(-10, 10, length= 30)
y <- x
f <- function(x,y) { r <- sqrt(x^2+y^2); 10 * sin(r)/r }
z <- outer(x, y, f)
z[is.na(z)] <- 1
persp(x, y, z, theta = 90, phi = 30, expand = 0.5, col = "lightgreen")
When change theta = 30:
persp(x, y, z, theta = 30, phi = 30, expand = 0.5, col = "lightgreen")
For color, you can type colors() to see what color you can use. Currently, I found lightgreen might be the closest color you want.