Error using PGFPlots.jl

Hi, please help me with this annoying and incomprehensive behavior. When I run

using PGFPlots
p = []
fun = [sin, cos]
x = 0:10
for i = 1:2
    push!(p, Plots.Linear(x, fun[i].(x)))
end
Axis(p)

I get the following error:

ERROR: LoadError: MethodError: no method matching Axis(::Array{Any,1})

But if I replace Axis(p) by Axis([p[1], p[2]]), it actually plots the two curves. Moreover, adding println([p[1], p[2]] == p) it actually prints true as the answer.

So, if [p[1], p[2]] == p, why do I get an error with Axis(p)? I can’t really understand this behavior.

This isn’t actually a PGFPlots specific issue, but rather to do with the way you construct your arrays:

julia> p = []  # No type information supplied, hence our array is of type Any
0-element Array{Any,1}

julia> for i ∈ 1:2
       push!(p, 5)
       end

julia> p
2-element Array{Any,1}:   # Type stays Array{Any} as this is what was defined above
 5
 5

julia> [p[1], p[2]]
2-element Array{Int64,1}:  # Here we are constructing a new Array, the type of which is inferred from the elements and is therefore Array{Int64,1}
 5
 5

julia> [p[1], p[2]] == p   # This compares the contents of the Arrays
true

julia> typeof(p) == typeof([p[1], p[2]])  # This compares the types - they differ, as the array comprehension infers the type for us. This causes your issue in dispatch.
false

# You should be doing the equivalent of this:
julia> q = Array{Int64, 1}() # Instantiate the Array with the type you want
0-element Array{Int64,1}

julia> for i ∈ 1:2
       push!(q, 5)
       end

julia> typeof(q) == typeof([p[1], p[2]])
true

I don’t have PGFPlots installed on this machine, but you’ll want to check typeof(p[1]) and then replace p = [] with p = Array{ result_of_typeof[p[1]] , 1}() to ensure p has the right type for the call to Axis to work.

1 Like

There is a simpler and more convenient way of creating empty typed arrays. Rather than writing Array{T,1}() or Vector{T}(), you just write T[]. E.g.

julia> Float64[]
0-element Array{Float64,1}

Sometimes it might be a bit hard to get T right, though, so it is perhaps easier to do something like this:

p = [plot(x, fun.(x)) for fun in (sin, cos)]

(I couldn’t find any function Plots.Linear in either Plots or PGFPlots.)

1 Like

Thank you very much for all your advice. I’ve changed the code as follows and it works fine:

using PGFPlots
p = Array{PGFPlots.Plots.Linear}(undef, 2)
fun = [sin, cos]
x = 0:10
for i = 1:2
    p[i] = Plots.Linear(x, fun[i].(x))
end
Axis(p)

It’s up to you, of course, but to me it looks a bit overly complicated and verbose. The comprehension relieves you of having to specify complex types, having to repeat yourself, and potentially getting it subtly wrong.

Whenever I can leave type stuff to the compiler, I tend to find that the resulting code is cleaner and more generic.

1 Like

That’s my matlabish background working. My code is slightly more complicated and it was a bit of a challenge to code it as a comprehension. It looks very neat now. Thank you again for your help.