Get Type in Array - julia

How can I get the type inside an array?
a = [1,2,3]
I can get the type of a
typeof(a)
Vector{Int64}
but I actually want Int64. First, I thought a newbie work-around could be
typeof(a[1])
Int64
but this is actually not correct, as can be seen here:
a = [1,2,3, missing]
typeof(a)
Vector{Union{Missing, Int64}}
The type of the vector is Union{Missing, Int64}, but the type of the first element is
typeof(a[1])
Int64
So, how do I get the type of the vector/array?

Use the eltype function:
julia> a = [1,2,3]
3-element Array{Int64,1}:
1
2
3
julia> eltype(a)
Int64
julia> a = [1,2,3, missing]
e4-element Array{Union{Missing, Int64},1}:
1
2
3
missing
julia> eltype(a)
Union{Missing, Int64}

Related

Julia--unrecognized subtype of nested unionall

While writing a function with the following signature,
f(x::Vector{Tuple{Vector{<:Real}, Vector{<:Real}}})
I ran into an error I do not understand. I tried calling this function f on z, defined as follows:
z = [([1,2], [3,4])]
(This is an array of tuples, where each tuple contains two arrays of real numbers; z as defined above only contains one such tuple.)
The type of z is
Array{Tuple{Array{Int64,1},Array{Float64,1}},1}
(as found by calling typeof(z)). I had expected this to be a subtype of
Vector{Tuple{Vector{<:Real}, Vector{<:Real}}}
, the type in the function f above.
However, when I run the code
z::Vector{Tuple{Vector{<:Real}, Vector{<:Real}}}
I see the following error:
ERROR: TypeError: in typeassert, expected Array{Tuple{Array{#s6,1} where #s6<:Real,Array{#s5,1} where #s5<:Real},1}, gotArray{Tuple{Array{Int64,1},Array{Float64,1}},1}
Likewise, I get a method error when calling f(z). Why isn't Array{Tuple{Array{Int64,1},Array{Int64,1}},1} a subtype of Vector{Tuple{Vector{<:Real}, Vector{<:Real}}}?
The reason is that:
julia> Tuple{Array{Int64,1},Array{Int64,1}} <: Tuple{Vector{<:Real}, Vector{<:Real}}
true
but clearly:
julia> Tuple{Array{Int64,1},Array{Int64,1}} >: Tuple{Vector{<:Real}, Vector{<:Real}}
false
and becasue types in Julia (except a Tuple, but here we have a Vector) are invariant (see here), you have that Vector{S} is not as subtype of Vector{T} even if S <: T.
so you need to write one additional subtyping qualification:
f(x::Vector{<:Tuple{Vector{<:Real}, Vector{<:Real}}})
and similarly:
julia> z::Vector{<:Tuple{Vector{<:Real}, Vector{<:Real}}}
1-element Array{Tuple{Array{Int64,1},Array{Int64,1}},1}:
([1, 2], [3, 4])
or extract the parameter using where:
julia> f2(x::Vector{Tuple{Vector{T}, Vector{T}}}) where {T<:Real} = x
f2 (generic function with 1 method)
julia> f2(z)
1-element Array{Tuple{Array{Int64,1},Array{Int64,1}},1}:
([1, 2], [3, 4])
or
julia> f3(x::Vector{Tuple{Vector{T}, Vector{S}}}) where {T<:Real, S<:Real} = x
f3 (generic function with 1 method)
julia> f3(z)
1-element Array{Tuple{Array{Int64,1},Array{Int64,1}},1}:
([1, 2], [3, 4])
(choose the first of second form against your decision if two elements of the tuple must have the same type or not)

Push SVector into vector of SVector in Julia

I want to push a SVector (provided by JuliaArrays/StaticArrays.jl) into a vector of SVector. The following code is my trial:
using StaticArrays
lst = Vector{SVector{2, Float64}}[]
a = SVector(1, 2)
push!(lst, a)
But it causes the following error:
ERROR: LoadError: MethodError: Cannot `convert` an object of type Int64 to an object of type SArray{Tuple{2},Float64,1,2}
How can I fix it?
The mistake you are making is that you create an Array of Array of an SVector. T[] creates an empty array of type T.
# This creates an empty array of type Float64
julia> lst = Float64[]
0-element Array{Float64,1}
# This creates an empty array of a Float64 array
julia> lst = Vector{Float64}[]
0-element Array{Array{Float64,1},1}
So you need to redefine your array as an array of SVector.
julia> lst = SVector{2, Float64}[] # an empty 1D array(i.e. Vector) of `SVector`
0-element Array{SArray{Tuple{2},Float64,1,2},1}
julia> a = SVector(1, 2)
2-element SArray{Tuple{2},Int64,1,2}:
1
2
julia> push!(lst, a)
1-element Array{SArray{Tuple{2},Float64,1,2},1}:
[1.0, 2.0]
You can also use this instead of your way of empty array definition:
lst = Vector{SVector{2, Float64}}(undef, 0) # this creates a `Vector` of `SVector` of size 0 (empty)

Julia: Assignment in Arrays

When indexing more than one level for an array, it works fine. But when I used it to assign values, it did not. Does anyone know why A does not change below?
In [4]: A = rand(6)
Out [4]: 6-element Array{Float64,1}:
0.111552
0.155126
0.78485
0.147477
0.362078
0.959022
In [5]: A[3:5][[true,false,true]]
Out [5]: 2-element Array{Float64,1}:
0.78485
0.362078
In [6]: A[3:5][[true,false,true]] = [99, 999]
Out [6]: 2-element Array{Int64,1}:
99
999
In [7]: A
Out [7]: 6-element Array{Float64,1}:
0.111552
0.155126
0.78485
0.147477
0.362078
0.959022
This is because indexing arrays by ranges and vectors returns a new array with the output (instead of a view into the original array). Your statement is equivalent to the following:
julia> A = rand(6)
6-element Array{Float64,1}:
0.806919
0.445286
0.882625
0.556251
0.719156
0.276755
julia> B = A[3:5]
3-element Array{Float64,1}:
0.882625
0.556251
0.719156
julia> B[[true,false,true]] = [99, 999]
2-element Array{Int64,1}:
99
999
julia> A'
1x6 Array{Float64,2}:
0.806919 0.445286 0.882625 0.556251 0.719156 0.276755
julia> B'
1x3 Array{Float64,2}:
99.0 0.556251 999.0
You can actually see that this is what Julia is doing through some of its expression utilities. Note the explicit parentheses — it's calling setindex! on the result of indexing, which has made a copy. (GenSym() is an internal way of specifying a temporary variable):
julia> :(A[3:5][[true,false,true]] = [99, 999])
:((A[3:5])[[true,false,true]] = [99,999])
julia> expand(:(A[3:5][[true,false,true]] = [99, 999]))
:(begin
GenSym(0) = (top(vect))(99,999)
setindex!(getindex(A,colon(3,5)),GenSym(0),(top(vect))(true,false,true))
return GenSym(0)
end)
The goal is to eventually have all array indexing return views instead of copies, but that's still a work-in-progress.

Julia: check whether array entry is undef

What is the best way in Julia to check whether an array entry is #undef?
Example:
julia> a = Array(Vector,2)
julia> isdefined(a[1]) # fails
julia> isempty(a[1]) # fails
You can push the access into isdefined by using isdefined(a, 1) instead of isdefined(a[1]):
julia> a = Array(Vector,2);
julia> a[2] = {10}
1-element Array{Any,1}:
10
julia> a
2-element Array{Array{T,1},1}:
#undef
{10}
julia> isdefined(a[1])
ERROR: access to undefined reference
in getindex at array.jl:246
julia> isdefined(a, 1)
false
julia> isdefined(a, 2)
true

Extracting parameter types in Julia

Suppose I write a function in Julia that takes a Dict{K,V} as an argument, then creates arrays of type Array{K,1} and Array{V,1}. How can I extract the types K and V from the Dict object so that I can use them to create the arrays?
Sven and John's answers are both quite right. If you don't want to introduce method type parameters the way John's code does, you can use the eltype function:
julia> d = ["foo"=>1, "bar"=>2]
["foo"=>1,"bar"=>2]
julia> eltype(d)
(ASCIIString,Int64)
julia> eltype(d)[1]
ASCIIString (constructor with 1 method)
julia> eltype(d)[2]
Int64
julia> eltype(keys(d))
ASCIIString (constructor with 1 method)
julia> eltype(values(d))
Int64
As you can see, there are a few ways to skin this cat, but I think that eltype(keys(d)) and eltype(values(d)) are by far the clearest and since the keys and values functions just return immutable view objects, the compiler is clever enough that this doesn't actually create any objects.
If you're writing a function that will do this for you, you can make the types a parameter of the function, which may save you some run-time lookups:
julia> function foo{K, V}(d::Dict{K, V}, n::Integer = 0)
keyarray = Array(K, n)
valarray = Array(V, n)
# MAGIC HAPPENS
return keyarray, valarray
end
foo (generic function with 2 methods)
julia> x, y = foo(["a" => 2, "b" => 3])
([],[])
julia> typeof(x)
Array{ASCIIString,1}
julia> typeof(y)
Array{Int64,1}
You can use keys and values in combination with typeof:
# an example Dict{K, V}
d = Dict{Int64, ASCIIString}()
# K
typeof(keys(d))
Array{Int64,1}
# V
typeof(values(d))
Array{ASCIIString,1}
If you are just interested in the types, you can use eltype(d), or define even more specific functions
keytype{K}(d::Dict{K}) = K
valuetype{K,V}(d::Dict{K,V}) = V
and find the type immediately through
keytype(d)
valuetype(d)
As far as I understand, this should be pretty efficient because the compiler can deduce most of this at compile time.
For iterables, the eltype approach that is already given in other answers works well enough. E.g.
julia> d = Dict(:a => 1, :b => 2)
Dict{Symbol, Int64} with 2 entries:
:a => 1
:b => 2
julia> K, V = eltype(keys(d)), eltype(values(d))
(Symbol, Int64)
julia> K
Symbol
julia> V
Int64
This approach is generalisable to all iterables. However I would like to offer another approach which will generalise to other kinds of object (and still doesn't use the method type signature approach):
julia> d = Dict(:a => 1, :b => 2)
Dict{Symbol, Int64} with 2 entries:
:a => 1
:b => 2
julia> typeof(d)
Dict{Symbol, Int64}
julia> K, V = typeof(d).parameters
svec(Symbol, Int64)
julia> K
Symbol
julia> V
Int64
You can see that the Dict{Symbol, Int64} type has a field parameters which you can unpack to obtain the type parameters. This works on structs too:
julia> struct MyStruct{S, T}
a::S
b::T
end
julia> x = MyStruct(1, 1.0)
MyStruct{Int64, Float64}(1, 1.0)
julia> S, T = typeof(x).parameters
svec(Int64, Float64)
julia> S
Int64
julia> T
Float64

Resources