How to narrow element type of a Vector or Array in Julia?
Asked Answered
A

3

7

Consider a situation where I first create a Vector with eltype of Any incrementally; after that, I want to narrow the element type of it. How can I do that?

julia> vec = Any[1, 2, 3.]
3-element Vector{Any}:
 1
 2
 3.0

I can use something like convert(Vector{Real}, vec). But in this case, I'm specifying the type manually while I want Julia to decide the best suitable eltype for it.

Appressed answered 25/12, 2022 at 9:13 Comment(0)
P
2

Small type unions (such as Union{Int, Float64} are handled in Julia much faster and than abstract types and hence you should avoid vectors of abstract elements such as Vector{Real} in favor of unions of concrete types such as Union{Int, Float64}.

Having that said here is a code that makes such union:

julia> Vector{Union{Set(typeof.(vec))...}}(vec)
3-element Vector{Union{Float64, Int64}}:
 1
 2
 3.0

And here is a simple test that shows that for a 100 element vector the performance difference is 4x:

julia> a1 = Vector{Union{Int, Float64}}(rand(100));

julia> a2 = Vector{Real}(rand(100));

julia> @btime minimum($a1);
  428.643 ns (0 allocations: 0 bytes)

julia> @btime minimum($a2);
  2.000 μs (102 allocations: 1.59 KiB)
Pone answered 25/12, 2022 at 15:58 Comment(0)
A
6

This can be achieved by broadcasting the identity function on each element of the given container (either an Array or a Vector):

julia> narrowed = identity.(vec)
3-element Vector{Real}:
 1
 2
 3.0

Note that this doesn't lead to promoting (or demoting) the type of each individual element of the given container:

julia> typeof(narrowed[1]), typeof(narrowed[3])
(Int64, Float64)

Additional Point

However, in the case of acquainting with related functions in Julia, This can be done verbosely by using the typejoin function to achieve the type join of the container's elements. According to the concise doc of the function:

typejoin(T, S)
Return the closest common ancestor of T and S, i.e. the narrowest type from which they both inherit.

The argument of the typejoin should be a subtype of Core.Type{T} (However, it seems more sensible to define it as typejoin(T...) since it can get an indefinite number of positional arguments, not just two.)

julia> typeof.(vec)
3-element Vector{DataType}:
 Int64
 Int64
 Float64

julia> typejoin(typeof.(vec)...)
Real

julia> convert(Vector{typejoin(typeof.(vec)...)}, vec)
3-element Vector{Real}:
 1
 2
 3.0
Appressed answered 25/12, 2022 at 9:13 Comment(0)
P
2

Small type unions (such as Union{Int, Float64} are handled in Julia much faster and than abstract types and hence you should avoid vectors of abstract elements such as Vector{Real} in favor of unions of concrete types such as Union{Int, Float64}.

Having that said here is a code that makes such union:

julia> Vector{Union{Set(typeof.(vec))...}}(vec)
3-element Vector{Union{Float64, Int64}}:
 1
 2
 3.0

And here is a simple test that shows that for a 100 element vector the performance difference is 4x:

julia> a1 = Vector{Union{Int, Float64}}(rand(100));

julia> a2 = Vector{Real}(rand(100));

julia> @btime minimum($a1);
  428.643 ns (0 allocations: 0 bytes)

julia> @btime minimum($a2);
  2.000 μs (102 allocations: 1.59 KiB)
Pone answered 25/12, 2022 at 15:58 Comment(0)
C
0

You can use promote like this:

v = Any[1, 2, 3.0]
first.(promote.(v))
3-element Vector{Real}:
 1
 2
 3.0000

v = Any[1, 2, 3.0, 3 + 2im]
first.(promote.(v))
4-element Vector{Number}:
   1
   2
   3.0000
 3 + 2im

But, you might be interested more in getting a vector of concrete supertypes of the elements, especially for performance purposes. So, you can use this:

v = Any[1, 2, 3.0]
reduce(vcat, promote(v...))
3-element Vector{Float64}:
 1.0
 2.0
 3.0

v = Any[1, 2, 3.0, 3 + 2im]
reduce(vcat, promote(v...))
4-element Vector{ComplexF64}:
 1.0 + 0.0im
 2.0 + 0.0im
 3.0 + 0.0im
 3.0 + 2.0im

Or, simply:

v = Any[1, 2, 3.0];

[v...]
3-element Vector{Float64}:
 1.0
 2.0
 3.0

v = Any[1, 2, 3.0, 3+2im];

[v...]
4-element Vector{ComplexF64}:
 1.0 + 0.0im
 2.0 + 0.0im
 3.0 + 0.0im
 3.0 + 2.0im
Caucasia answered 25/12, 2022 at 14:59 Comment(2)
Comprehension also works for the first option: [i for i in v].Caucasia
Thank you. But this is not narrowing. This is literally promoting. You're changing the elements.Appressed

© 2022 - 2024 — McMap. All rights reserved.