3D line plot of the elements of 3 vectors

I’d like to generate a 3D line plot using data in three vectors labelled x, y and z. I’ve tried using PyPlot’s plot3D and I received the error:

PyError ($(Expr(:escape, :(ccall(#= /home/fusion809/.julia/packages/PyCall/zqDXB/src/pyfncall.jl:43 =# @pysym(:PyObject_Call), PyPtr, (PyPtr, PyPtr, PyPtr), o, pyargsptr, kw))))) <class 'ValueError'>
ValueError('input operand has more dimensions than allowed by the axis remapping')
  File "/usr/lib/python3.8/site-packages/mpl_toolkits/mplot3d/axes3d.py", line 1421, in plot
    zs = np.broadcast_to(zs, len(xs))
  File "<__array_function__ internals>", line 5, in broadcast_to
  File "/usr/lib/python3.8/site-packages/numpy/lib/stride_tricks.py", line 182, in broadcast_to
    return _broadcast_to(array, shape, subok=subok, readonly=True)
  File "/usr/lib/python3.8/site-packages/numpy/lib/stride_tricks.py", line 125, in _broadcast_to
    it = np.nditer(


Stacktrace:
 [1] pyerr_check at /home/fusion809/.julia/packages/PyCall/zqDXB/src/exception.jl:60 [inlined]
 [2] pyerr_check at /home/fusion809/.julia/packages/PyCall/zqDXB/src/exception.jl:64 [inlined]
 [3] _handle_error(::String) at /home/fusion809/.julia/packages/PyCall/zqDXB/src/exception.jl:81
 [4] macro expansion at /home/fusion809/.julia/packages/PyCall/zqDXB/src/exception.jl:95 [inlined]
 [5] #110 at /home/fusion809/.julia/packages/PyCall/zqDXB/src/pyfncall.jl:43 [inlined]
 [6] disable_sigint at ./c.jl:446 [inlined]
 [7] __pycall! at /home/fusion809/.julia/packages/PyCall/zqDXB/src/pyfncall.jl:42 [inlined]
 [8] _pycall!(::PyCall.PyObject, ::PyCall.PyObject, ::Tuple{Array{Float64,2},Array{Float64,2},Array{Float64,2}}, ::Int64, ::Ptr{Nothing}) at /home/fusion809/.julia/packages/PyCall/zqDXB/src/pyfncall.jl:29
 [9] _pycall!(::PyCall.PyObject, ::PyCall.PyObject, ::Tuple{Array{Float64,2},Array{Float64,2},Array{Float64,2}}, ::Base.Iterators.Pairs{Union{},Union{},Tuple{},NamedTuple{(),Tuple{}}}) at /home/fusion809/.julia/packages/PyCall/zqDXB/src/pyfncall.jl:11
 [10] pycall(::PyCall.PyObject, ::Type{PyCall.PyAny}, ::Array{Float64,2}, ::Vararg{Array{Float64,2},N} where N; kwargs::Base.Iterators.Pairs{Union{},Union{},Tuple{},NamedTuple{(),Tuple{}}}) at /home/fusion809/.julia/packages/PyCall/zqDXB/src/pyfncall.jl:83
 [11] pycall(::PyCall.PyObject, ::Type{PyCall.PyAny}, ::Array{Float64,2}, ::Vararg{Array{Float64,2},N} where N) at /home/fusion809/.julia/packages/PyCall/zqDXB/src/pyfncall.jl:83
 [12] plot3D(::Array{Float64,2}, ::Vararg{Array{Float64,2},N} where N; kws::Base.Iterators.Pairs{Union{},Union{},Tuple{},NamedTuple{(),Tuple{}}}) at /home/fusion809/.julia/packages/PyPlot/XHEG0/src/plot3d.jl:62
 [13] plot3D(::Array{Float64,2}, ::Vararg{Array{Float64,2},N} where N) at /home/fusion809/.julia/packages/PyPlot/XHEG0/src/plot3d.jl:60
 [14] top-level scope at In[25]:1

and I must admit I’m a little stumped as to how I am meant to get around this error. If you need to know how x, y and z were created in order to help me, here’s the Julia script I’m using to create them:

using Pkg;

# Import PyPlots
Pkg.add("PyPlot")
using PyPlot;

N     = 10000;
sigma = 10.0
rho   = 28.0
beta  = 8.0/3.0;
t0    = 0;
tf    = 100;
dt    = (tf-t0)/N;
t     = t0:dt:tf;

function f(beta, rho, sigma, t, x, y, z)

	# The Lorenz equations
	dx_dt = sigma.*(y - x);
	dy_dt = -y + x.*(-z + rho);
	dz_dt = - beta*z + x*y;

	# Return the derivatives as a vector
	return dx_dt, dy_dt, dz_dt
end;

function RK4(beta, rho, sigma, dt, t, x, y, z)
    K1 = dt.*f(beta, rho, sigma, t, x, y, z);
    k1 = K1[1];
    l1 = K1[2];
    m1 = K1[3];
    K2 = dt.*f(beta, rho, sigma, t + dt/2, x + k1/2, y + l1/2, z + m1/2);
    k2 = K2[1];
    l2 = K2[2];
    m2 = K2[3];
    K3 = dt.*f(beta, rho, sigma, t + dt/2, x + k2/2, y + l2/2, z + m2/2);
    k3 = K3[1];
    l3 = K3[2];
    m3 = K3[3];
    K4 = dt.*f(beta, rho, sigma, t + dt/2, x + k3, y + l3, z + m3);
    k4 = K4[1];
    l4 = K4[2];
    m4 = K4[3];
    dx = 1/6 * (k1 + 2*k2 + 2*k3 + k4);
    dy = 1/6 * (l1 + 2*l2 + 2*l3 + l4);
    dz = 1/6 * (m1 + 2*m2 + 2*m3 + m4);
    
    return dx, dy, dz
end

x     = zeros(N+1,1);
x[1]  = -2.0;
y     = zeros(N+1,1);
y[1]  = 3.0;
z     = zeros(N+1,1);
z[1]  = 0.5;

for i=1:N
    diff = RK4(beta, rho, sigma, dt, t[i], x[i], y[i], z[i]);
    x[i+1] = x[i] + diff[1];
    y[i+1] = y[i] + diff[2];
    z[i+1] = z[i] + diff[3];
end

PyPlot.figure(1)
PyPlot.plot3D(x, y, z);
PyPlot.xlabel("x")
PyPlot.ylabel("y")
PyPlot.zlabel("z")

From what little I gathered from the error it seems like my x, y and z are of the wrong type for this plotting function but I have no idea how I’m meant to correct this. I have tried adding the lines:

convert(Array{Float64,1}, x)
convert(Array{Float64,1}, y)
convert(Array{Float64,1}, z)

before I called the plotting function, based on this answer on StackOverflow and the fact that another script of mine who’s x, y, and z are of the Array{Float64,1} type runs plot3D without a problem, but I just received the error:

MethodError: no method matching Array{Float64,1}(::Array{Float64,2})
Closest candidates are:
  Array{Float64,1}(::AbstractArray{S,N}) where {T, N, S} at array.jl:541
  Array{Float64,1}() where T at boot.jl:424
  Array{Float64,1}(!Matched::UndefInitializer, !Matched::Int64) where T at boot.jl:405
  ...

Stacktrace:
 [1] convert(::Type{Array{Float64,1}}, ::Array{Float64,2}) at ./array.jl:533
 [2] top-level scope at /data/GitHub/mine/maths/julia-scripts/Chaos/Lorenz/RK4.jl:65
 [3] top-level scope at In[33]:1

which suggests to me that this bit of code on StackOverflow is likely invalid for my usecase.

Thanks for your time.

Simply define x = zeros(N+1) and similarly for y and z and obtain a Vector{Float64} instead of a Matrix{Float64}:

julia> x = zeros(N+1,1); typeof(x)
Array{Float64,2} # a matrix

julia> x = zeros(N+1); typeof(x)
Array{Float64,1} # a vector
1 Like

Thank you! This works perfectly, I can’t believe it was that simple.