Contour plots with varying locations for x-axis?

Hi, I have a set of 2-dimensional data, defined over an array of timesteps, but at each timestep the position x-values are different. I’m trying to get contour plots, but the plots come up blank, and each time I get the error message: Arrays have incorrect length or dimension. An example code model excerpt goes like:

using Plots

tNum = 4; xNum = 3; 
# These numbers will be >1000 in the real calc

FunctionForPlot = Array{Float64,2}(undef, tNum, xNum)
xValsArr = Array{Float64,2}(undef, tNum, xNum)
tValsArr =  Array{Float64}(undef, tNum)

for i in 1:tNum
        tValsArr[i] = i
    for j in 1:xNum
        xValsArr[i,j] = i + j^2
        FunctionForPlot[i,j] = i*sin(35*j)
    end
end

contour([xValsArr[i,j] for i=1:tNum, j=1:xNum],
         [tValsArr[i] for i=1:tNum, j=1:xNum],
        [FunctionForPlot[i,j] for i=1:tNum, j=1:xNum], fill=true)

Any suggestions on how to make this work, or why I’m not getting the plot I expected, would be welcome. Thank you!

To draw the contour plot you must interpolate from your initial data, x, y, z, that define a function f(x[i,j], y[i, j])=z[i,j], the value of f at a regular grid, defined by
xi= 2:0.5:13, yi=1:4. I tried Dierckx.jl, but the Spline2D cannot be called because the flattened x-array contains the repeating value 5. Maybe Interpolations.jl provides an adequate function for 2D interpolation.

1 Like

Hi empet, thanks for the response!

Thinking about it, it does seem likely that Julia cannot do contour plots (or similar 2D plots) on an irregularly-spaced grid (as defined by irregularly-spaced data), but that the data must somehow be interpolated to a regularly-spaced grid for contour (or similar) plots.

If that’s incorrect, and anyone does know how to extend contour plots to idiosyncratically-spaced data, please let me know…!

Since I was not able to use Dierckx.jl and have no experience with Interpolations.jl (I’m a Julia beginner) I tried with Python scipy.interpolate and your contour plot looks like this:
contour

The steps for getting a regular grid of values Xi, Yi, zi for which the Plots.jl contour could work:

x, y, z are the array passed by you as arguments for contour
xi = 2:0.5:13
yi = 1:0.5:4
fxy = scipy.interpolate.interp2d(x, y, z) #you should find the Interpolations.jl 
                                           #function that works like this scipy.interpolation function
zi = fxy(xi, yi)
Xi = xi' .* ones(length(yi))
Yi = ones(length(xi))'.*yi
contour(Xi, Yi, zi)

The commonly used algorithm (marching squares) requires a regular grid. There are other algorithms for eg triangle meshes, but for the purposes of plotting it is common to just approximate the surface and then compute on a regular grid.

1 Like

Hi Tamas, thanks for the clarification. I understand what I have to do a little better now.

Empet, thanks for the sample scipy code, I’m sure this will come in handy for interpolating the data, which I plan to do soon.

I think I know how to proceed, thanks again guys!

2 Likes

Hi again, I’m revisiting this attempt, trying to mimic what I want with a 2D scatterplot (with distinct x and y values for each point), where the value of the function for each point is indicated by its color, on a color scale. I’ve searched online and tried doing this with PGFPlotsX and Gadfly, but I can’t find enough info to get the syntax right. Does anyone know how to get a simple example like the following to work:

using Plots
xData = [100.0*rand(Float64) for i in 1:100]
yData = [50.0*rand(Float64) for j in 1:100]
zData = [(xData[i] + yData[j]) for i=1:100, j=1:100]

scatter(xData , yData , color = zData) # This does NOT work, simplest fix??

It would also be nice if I could get each (variously colored) point to be plotted as a filled square (instead of just a point), so it would kind of look like a heatmap or contour plot (with gaps I guess).

Thanks for any suggestions…!

@CosmoProf

I succeeded to define a plot following your requirement, using PlotlyJS.jl:

using PlotlyJS
m=75
n=100

xData = [100.0*rand(Float64) for i in 1:n]
yData = [50.0*rand(Float64) for j in 1:m]
X, Y = [x for y in yData, x in xData], [y for y in yData, x in xData]
zData = X+Y
trace = PlotlyJS.scatter(x=vec(X), y=vec(Y), mode="markers", 
                       marker=attr(symbol="square",  color=vec(zData),
                                   showscale=true, colorbar_thickness=23))
pl = PlotlyJS.plot(trace, Layout(width=500, height=500, title_text="Your title",
        xaxis=attr(range=[minimum(X), maximum(X)], showgrid=false),
        yaxis=attr(range=[minimum(Y), maximum(Y)], showgrid=false)))

scatter2

The above “heatmap” was defined with the default size for the marker.
Setting the marker attribute size=2.5, and the plasma colormap:

plasma=[[0.0, "#0c0786"],
 [0.1, "#40039c"],
 [0.2, "#6a00a7"],
 [0.3, "#8f0da3"],
 [0.4, "#b02a8f"],
 [0.5, "#cb4777"],
 [0.6, "#e06461"],
 [0.7, "#f2844b"],
 [0.8, "#fca635"],
 [0.9, "#fcce25"],
 [1.0, "#eff821"]]

the plot is as follows:
scatter2_5

Your intuition was good, and the heatmap with gaps is very interesting :slight_smile:

If I got you right, the following should work

using Plots
xData = [100.0*rand(Float64) for i in 1:100]
yData = [50.0*rand(Float64) for j in 1:100]
zData = [(xData[i] + yData[j]) for i=1:100, j=1:100]

scatter(xData , yData , marker_z = zData, markershape = :square)

Hello BeastyBlacksmith, I tried your suggestion, but it does not seem to produce a proper 2-dimensional plot. If you replace the zData function with something sinusoidally-varying instead, it varies in the x-direction but not in the y-direction (it should do both). See the following example, which does not produce the fully two-dimensionally varying result expected:

using Plots
xData = [100.0*rand(Float64) for i in 1:1000]
yData = [50.0*rand(Float64) for j in 1:1000]
zData = [(sin(xData[i]/5)*cos(yData[j]/3)) for i=1:1000, j=1:1000]

scatter(xData , yData , marker_z = zData, markershape = :square)

I also tried creating a hybrid solution by putting empet’s suggestions together with Beasty’s, and I’m getting the same problem (no variation in the y-direction):

using Plots
nPix = 50
xData = [(100.0*(i/50)) for i in 1:nPix, j in 1:nPix]
yData = [(50.0*(j/27)) for j in 1:nPix, j in 1:nPix]
zData = [(sin(xData[i]/5)*cos(yData[j]/3)) for i=1:nPix, j=1:nPix]

x2D = vec(xData)
y2D = vec(yData)
z2D = vec(zData)

scatter(x2D, y2D, marker_z = z2D, markershape = :square)

BTW, this is actually more of what I had in mind… but I still can’t get the proper sine/cosine variation in the y-direction, for some reason:

using Plots
nPix = 50
xData = [(100.0*(i/50))*(j^(1/4)) for i in 1:nPix, j in 1:nPix]
yData = [(50.0*(j/27))*(i^(1/4)) for i in 1:nPix, j in 1:nPix]
zData = [(sin(xData[i]/5)*cos(yData[j]/3)) for i=1:nPix, j=1:nPix]

x2D = vec(xData)
y2D = vec(yData)
z2D = vec(zData)

scatter(x2D, y2D, marker_z = z2D, markershape = :square)

1 Like

Oops, found my mistake…


using Plots
nPix = 50
xData = [(100.0*(i/50)) for i in 1:nPix, j in 1:nPix]
yData = [(50.0*(j/27)) for i in 1:nPix, j in 1:nPix]
zData = [(sin(xData[i,j]/5)*cos(yData[i,j]/3)) for i=1:nPix, j=1:nPix]

x2D = vec(xData)
y2D = vec(yData)
z2D = vec(zData)

scatter(x2D, y2D, marker_z = z2D, markershape = :square)

Even if it was generated by a mistake, the previous one looks great.