How to broadcast vectors (lists) into tuples in Julia? - julia

Is there a generator/iterator function that will turn
a = [1,2]
b = [3,4]
into [(1,3),(2,4)] and
a = 1
b = [3,4]
into [(1,3),(1,4)] using the same expression?
Is there a similar way to create a named tuple such as [(a=1,b=3),(a=1,b=4)]?

You can use broadcasting with Julia's dot syntax for this:
julia> tuple.(a, b)
2-element Array{Tuple{Int64,Int64},1}:
(1, 3)
(2, 4)
tuple here is a function that just creates a tuple from its arguments.
For NamedTuples you can either call the lower-level constructor directly on tuples with
julia> NamedTuple{(:a, :b)}.(tuple.(a, b))
2-element Array{NamedTuple{(:a, :b),Tuple{Int64,Int64}},1}:
(a = 1, b = 3)
(a = 2, b = 4)
where :a and :b are the sorted key names, or equivalently, using an anonymous function:
julia> broadcast((a_i, b_i) -> (a=a_i, b=b_i), a, b)
2-element Array{NamedTuple{(:a, :b),Tuple{Int64,Int64}},1}:
(a = 1, b = 3)
(a = 2, b = 4)
Hope that helps!

Just broadcast the tuple function.
julia> a = [1,2]; b=[3,4];
julia> tuple.(a,b)
2-element Array{Tuple{Int64,Int64},1}:
(1, 3)
(2, 4)
julia> tuple.(1, b)
2-element Array{Tuple{Int64,Int64},1}:
(1, 3)
(1, 4)
Second question - broadcast the constructor:
julia> NamedTuple{(:a, :b)}.(tuple.(1, b))
2-element Array{NamedTuple{(:a, :b),Tuple{Int64,Int64}},1}:
(a = 1, b = 3)
(a = 1, b = 4)

Related

How to interate over two or more vectors or tuples in julia?

Can we iterate over two or more vectors or tuples in julia?
julia> c=Tuple(x for x in a, b)
The above code does not work but shows what i want to do. I need to iterate over both a and b one after other.
Suppose,
julia> a=(1,2)
julia> b=(3,4)
and I want c to be:
julia> c=(1,2,3,4)
Use:
julia> c = Tuple(Iterators.flatten((a, b)))
(1, 2, 3, 4)
to get a Tuple as you requested. But if you are OK with a lazy iterator then just Iterators.flatten((a, b)) is enough.
Very short version:
julia> a=(1,2)
julia> b=(3,4)
julia> c = (a..., b...)
(1, 2, 3, 4)

Using Iterators.product on a variable number of lists

I'm trying to make a function that use Iterators.product() on a variable number of arrays.
This is my code:
function make_feature_space(dictionary, k)
dict_chars = collect(dictionary)
#Change bottom line
dict_product = collect(Iterators.product(dict_chars))
return dict_product
end
The behavior I'd like would be something along the lines of calling make_feature_space(dictionary, 3) would return Iterators.product(dict_chars, dict_chars, dict_chars), but calling make_feature_space(dictionary, 2) would get Iterators.product(dict_chars, dict_chars).
Thanks!!
Here's a solution that uses Iterators.repeated and splatting:
using Base.Iterators
make_feature_space(x, n) = product(repeated(x, n)...)
Here it is in action:
julia> x = 1:2;
julia> make_feature_space(x, 2) |> collect
2×2 Array{Tuple{Int64,Int64},2}:
(1, 1) (1, 2)
(2, 1) (2, 2)
julia> make_feature_space(x, 3) |> collect
2×2×2 Array{Tuple{Int64,Int64,Int64},3}:
[:, :, 1] =
(1, 1, 1) (1, 2, 1)
(2, 1, 1) (2, 2, 1)
[:, :, 2] =
(1, 1, 2) (1, 2, 2)
(2, 1, 2) (2, 2, 2)
Note that this implementation can use any iterator in the first argument of make_feature_space. Further note that a dictionary is an iterator of pairs, so you can do this:
julia> d = Dict(:a => 1, :b => 2)
Dict{Symbol,Int64} with 2 entries:
:a => 1
:b => 2
julia> make_feature_space(d, 2) |> collect
2×2 Array{Tuple{Pair{Symbol,Int64},Pair{Symbol,Int64}},2}:
(:a=>1, :a=>1) (:a=>1, :b=>2)
(:b=>2, :a=>1) (:b=>2, :b=>2)
Though it's not clear from your question if you're looking for a product over the keys, values, or pairs of the dictionary.

Efficiently convert Dict to NamedTuple in Julia

I would like to have an interface that accepts a Dict or a NamedTuple as input, but then always converts the input to a NamedTuple.
Given a Dict
julia> dd = Dict(:a => 1, :b => 2)
Dict{Symbol,Int64} with 2 entries:
:a => 1
:b => 2
I can covert it to a NamedTuple with
julia> (; dd...)
(a = 1, b = 2)
However, this both allocates a surprising (to me) amount
julia> using BenchmarkTools
julia> #btime (; $dd...);
1.033 μs (12 allocations: 896 bytes)
And it does not apply to nested Dicts, which I would like to convert to nested NamedTuples
julia> dd_nested = Dict(:a => 1, :b => Dict(:x => 3, :y => 4))
Dict{Symbol,Any} with 2 entries:
:a => 1
:b => Dict(:y=>4,:x=>3)
julia> (; dd_nested...)
(a = 1, b = Dict(:y => 4,:x => 3))
where the desired output is equal to
julia> (a = 1, b = (x = 3, y = 4))
(a = 1, b = (x = 3, y = 4))
What about:
unzip(d::Dict) = (;(p.first => unzip(p.second) for p in d)...)
unzip(d) = d
Sample test:
julia> unzip(dd)
(a = 1, b = (y = 4, x = 3))
Regarding memory allocations NamedTupleTools.jl seems to have a slightly smaller memory footprint. But in either case you are creating quite a bit of data structure here so most likely you will not be able to do it any cheaper.

How to iterate over two arrays in parallel

I have two arrays that I want to iterate over at the same time.
I'm using this:
julia> xs = [1,2,3];
julia> ys = [4,5,6];
julia> for i in 1:length(xs)
x = xs[i]
y = ys[i]
#show x, y
end
(x, y) = (1, 4)
(x, y) = (2, 5)
(x, y) = (3, 6)
Is there a better way to iterate over multiple arrays in Julia?
Use zip along with tuple destructuring:
julia> xs = [1,2,3];
julia> ys = [4,5,6];
julia> for (x, y) in zip(xs, ys)
#show x, y
end
(x, y) = (1, 4)
(x, y) = (2, 5)
(x, y) = (3, 6)
zip will stop iteration at the shortest array:
julia> for (x, y) in zip([1,2], [0,0,0])
#show x, y
end
(x, y) = (1, 0)
(x, y) = (2, 0)
This pattern can be generalized to an arbitrary number of lists:
julia> for (x, y, z) in zip([1,2], [3,4], [5,6])
#show x, y, z
end
(x, y, z) = (1, 3, 5)
(x, y, z) = (2, 4, 6)
One possibility consists in using the eachindex function: if it is given multiple Array-like arguments, it will return a iterable set of indices suitable to iterate on all arguments at once.
This is useful in particular in the following situations:
when you need to use the index itself (for example because you don't only need to access the elements of the collections, but also set some of them), or
when you want to check that both arrays indeed have the same number of elements (this might or might not be a desired property depending on your use case).
Example 1: using the index itself to fill the first array with values coming from the second
julia> x = [1,2,3];
julia> y = [4,5,6];
julia> #inbounds for i in eachindex(x, y)
x[i] = 2*y[i]
end
julia> x
3-element Array{Int64,1}:
8
10
12
Example 2: check that the arrays have the same range
julia> x = [1,2];
julia> y = [4,5,6];
julia> #inbounds for i in eachindex(x, y)
x[i] = 2*y[i]
end
ERROR: DimensionMismatch("all inputs to eachindex must have the same indices, got [1, 2] and [1, 2, 3]")
Example 3: note that eachindex generalizes well for multi-dimensional arrays too.
julia> x = zeros(2, 3);
julia> y = ones(2, 3);
julia> #inbounds for i in eachindex(x, y)
x[i] = 2*y[i]
end
julia> x
2×3 Array{Float64,2}:
2.0 2.0 2.0
2.0 2.0 2.0
You can iterate over multiple collections using map and foreach. For example, with map:
julia> x, y = 1:3, 4:6;
julia> map(hypot, x, y)
3-element Array{Float64,1}:
4.123105625617661
5.385164807134504
6.708203932499369
For more complicated multi-line anonymous functions, you can use do-block syntax:
julia> xs, ys = 1:4, 10:10:40;
julia> map(xs, ys) do x, y
if isodd(x)
x + y
else
x * y
end
end
4-element Array{Int64,1}:
11
40
33
160
foreach is very similar to map, but is intended for use when a function is applied for its side effect, like printing or plotting, rather than its return value. An example with foreach:
julia> x, y = ["a", "b", "c"], 1:3;
julia> foreach(println ∘ ^, x, y)
a
bb
ccc
Note the use of the function composition operator in the foreach call.

Circular permutations

Given a vector z = [1, 2, 3], I want to create a vector of vectors with all circular permutations of z (i.e. zp = [[1,2,3], [3,1,2], [2,3,1]]).
I can print all elements of zp with
for i in 1:length(z)
push!(z, shift!(z)) |> println
end
How can I store the resulting permutations? Note that
zp = Vector(length(z))
for i in 1:length(z)
push!(z, shift!(z))
push!(zp, z)
end
doesn't work as it stores the same vector z 3 times in zp.
One way would just be to copy the vector before pushing it:
z = [1, 2, 3];
zp = Vector();
for i in 1:length(z)
push!(z, shift!(z))
push!(zp, copy(z))
end
gives me
julia> zp
3-element Array{Any,1}:
[2,3,1]
[3,1,2]
[1,2,3]
But I tend to prefer avoiding mutating operations when I can. So I'd instead write this as
julia> zp = [circshift(z, i) for i=1:length(z)]
3-element Array{Array{Int64,1},1}:
[3,1,2]
[2,3,1]
[1,2,3]
This seems to execute pretty quick on my machine (faster than a comprehension):
julia> z=[1,2,3]
3-element Array{Int64,1}:
1
2
3
julia> zp=Vector{typeof(z)}(length(z))
3-element Array{Array{Int64,1},1}:
#undef
#undef
#undef
julia> for i=1:length(z)
zp[i]=circshift(z,i-1)
end
julia> zp
3-element Array{Array{Int64,1},1}:
[1,2,3]
[3,1,2]
[2,3,1]
julia>

Resources