Contour plot using gr() with an interpolated function in Julia - plot

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)

Related

Stack Heatmaps in one plot

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.

How do I make x and y axes thicker with Plots (Julia)?

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)

How do you color (x,y) scatter plots according to values in z using Plots.jl?

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)

Stacking of several Surface plots in 3D-View

Lets consider that I have five 2D-Matrices which describe the magnetic field at different z-Layers. A nice, smoothed version of a 2D-Surface plot can be obtained as follows:
data2_I<-matrix(c(1.0,1.0,0.6,0.6,0.7,0.9,0.9,0.5,0.5,0.5,0.7,0.9,0.9,0.6,0.3,0.4,0.7,0.9,0.9,0.7,0.5,0.5,0.6,0.9,0.9,0.7,0.6,0.6,1.0,1.0), nrow=5)
Z = as.vector(data2_I)
length(Z)
XY=data.frame(x=as.numeric(gl(5,1,30)),y=as.numeric(gl(5,6,30)))
t=Tps(XY,Z)
surface(t)
Now it would be great if I could get a 3D-plot where at different z-Positions these surfaces are plotted. Is there a possibility to do that?
I found an alternative approach: With the package rgl I and the function surface 3D I can stack several 3D-Surface plots within one open3d-window. Lets look at a small example:
library("rgl")
data2_I<-matrix(c(1.0,1.0,0.6,0.6,0.7,0.9,0.9,0.5,0.5,0.5,0.7,0.9,0.9,0.6,0.3,0.4,0.7,0.9,0.9,0.7,0.5,0.5,0.6,0.9,0.9,0.7,0.6,0.6,1.0,1.0), nrow=5)
data0_I<-matrix(c(1.0,1.0,0.6,0.6,0.7,0.9,0.9,0.5,0.5,0.5,0.7,0.9,0.9,0.6,0.3,0.4,0.7,0.9,0.9,0.7,0.5,0.5,0.6,0.9,0.9,0.7,0.6,0.6,1.0,1.0), nrow=5)
data1_I<-2*data0_I
data2_I<-1/data1_I
elv=0
offs=5*elv+1
z0 <- scale*data0_I
z1 <- scale*data1_I
z2 <- scale*data2_I
x <- 1:nrow(z0)
y <- 1:ncol(z0)
palette <- colorRampPalette(c("blue","green","yellow", "red"))
col.table <- palette(256)
open3d(windowRect=c(50,50,800,800))
surface3d(x, y, elv*z0, color = col.table[cut(z0, 256)], back = "lines")
surface3d(x, y, elv*z1+1*offs, color = col.table[cut(z1, 256)], back = "lines")
surface3d(x, y, elv*z2+2*offs, color = col.table[cut(z2, 256)], back = "lines")
axes3d()
aspect3d(1,1,2)
The variables offsand elv are included for cosmetic purposes: offs controls the space between two surface plots and elevation how the z-axes of the surface3d-plots should scale. As I wanted to have a 2D surface plot without any elevation I set it to zero.

levelplot - how to use it, any simple examples?

I woud like to understand how levelplot works. I have almost no experience with plots and R.
What confuses me, is how should I interpret for example x~y*z ?
Lets assume I have a function, and I would like to show how often certain value occurs by using 3d plot. I would have hence x = x, y = f(x) and z = count. How to obtain such simple plot by using levelplot (or something else if it is not appriopriate).
In addition, should I group "count" myself - 3 columns in my data from, or just have 2 columns - x and f(x) and have duplications?
Hope my question is clear, I tried to read levelplot documentation, however I could not find any tutorial that teaches basics.
The following example is from the ?levelplot documentation.
The formula z~x*y means that z is a function of x, y and the interaction between x and y. Had the function been z~x+y it would have meant that z is a function of x and y, ignoring any interaction.
You can read more about the formula interface in the help for ?formula.
x <- seq(pi/4, 5 * pi, length.out = 100)
y <- seq(pi/4, 5 * pi, length.out = 100)
r <- as.vector(sqrt(outer(x^2, y^2, "+")))
grid <- expand.grid(x=x, y=y)
grid$z <- cos(r^2) * exp(-r/(pi^3))
levelplot(z~x*y, grid, cuts = 50, scales=list(log="e"), xlab="",
ylab="", main="Weird Function", sub="with log scales",
colorkey = FALSE, region = TRUE)

Resources