Expanding array along a dimension - julia

The following Python function expands an array along a dimension
def expand(x, dim,copies):
trans_cmd = list(range(0,len(x.shape)))
trans_cmd.insert(dim,len(x.shape))
new_data = x.repeat(copies).reshape(list(x.shape) + [copies]).transpose(trans_cmd)
return new_data
>>x = np.array([[1,2,3],[4,5,6]])
>>x.shape
(2, 3)
>>x_new = expand(x,2,4)
>>x_new.shape
(2,3,4)
>>x_new
array([[[1, 1, 1, 1],
[2, 2, 2, 2],
[3, 3, 3, 3]],
[[4, 4, 4, 4],
[5, 5, 5, 5],
[6, 6, 6, 6]]])
How can this function be replicated in Julia?

Instead of doing a repeat -> reshape -> permutedims (like you have in numpy), I'd just do a reshape -> repeat. Accounting for the translation of row-major to column-major, it looks like this:
julia> x = [[1,2,3] [4,5,6]]
3×2 Array{Int64,2}:
1 4
2 5
3 6
julia> repeat(reshape(x, (1, 3, 2)), outer=(4,1,1))
4×3×2 Array{Int64,3}:
[:, :, 1] =
1 2 3
1 2 3
1 2 3
1 2 3
[:, :, 2] =
4 5 6
4 5 6
4 5 6
4 5 6
The tricky part about doing this efficiently in Julia is the construction of the tuples (1, 3, 2) and (4, 1, 1). While you could convert them to arrays and use array mutation (like you convert it to a python list), it's far more efficient to keep them as tuples. ntuple is your friend here:
julia> function expand(x, dim, copies)
sz = size(x)
rep = ntuple(d->d==dim ? copies : 1, length(sz)+1)
new_size = ntuple(d->d<dim ? sz[d] : d == dim ? 1 : sz[d-1], length(sz)+1)
return repeat(reshape(x, new_size), outer=rep)
end
expand (generic function with 1 method)
julia> expand(x, 1, 4)
4×3×2 Array{Int64,3}:
[:, :, 1] =
1 2 3
1 2 3
1 2 3
1 2 3
[:, :, 2] =
4 5 6
4 5 6
4 5 6
4 5 6
julia> expand(x, 2, 4)
3×4×2 Array{Int64,3}:
[:, :, 1] =
1 1 1 1
2 2 2 2
3 3 3 3
[:, :, 2] =
4 4 4 4
5 5 5 5
6 6 6 6
julia> expand(x, 3, 4)
3×2×4 Array{Int64,3}:
[:, :, 1] =
1 4
2 5
3 6
[:, :, 2] =
1 4
2 5
3 6
[:, :, 3] =
1 4
2 5
3 6
[:, :, 4] =
1 4
2 5
3 6

Related

How to perform automatic array reshaping in Julia?

In the Numpy I can say:
>>> import numpy as np
>>> arr = np.arange(10)
>>> arr.reshape((2, -1))
array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9]])
In the preceding code block, the -1 means "what ever is needed". How can I do the same thing in Julia?
In julia, we can use : instead of -1 in Numpy:
julia> arr = [0:9...];
julia> reshape(arr, 2, :)
2×5 Matrix{Int64}:
0 2 4 6 8
1 3 5 7 9
If I want to achieve the same thing as Numpy's output:
julia> permutedims(reshape(arr, :, 2))
2×5 Matrix{Int64}:
0 1 2 3 4
5 6 7 8 9

Operations with CartesianIndex

I'd like to know how can I operate with CartesianIndex. For example I have array
julia> A = rand(1:5, 10, 2)
10×2 Array{Int64,2}:
2 5
1 1
4 5
4 1
2 1
4 1
2 4
1 5
2 5
4 4
and I want to save all numbers which stay near (in pair) with number 1. I can use c=findall(x->x==1, A), but I will have a cartensian indexes of "1".
There is function x=getindex.(c, [1 2]) it makes an array which I can change, but I don't know how to convert it back to CartesianIndex. And I think that must be a better way to do this.
A[view(A.==1,:,[2,1])]
This literally returns "all numbers which stay in pair with number 1".
The order of returned numbers is columnar. If you want to return it by rows:
A'[view(A.==1,:,[2,1])']
Example:
julia> A = rand(1:5, 10, 2)
10×2 Array{Int64,2}:
1 4
3 3
1 3
3 3
5 1
1 5
2 1
3 3
1 3
2 3
julia> A'[view(A.==1,:,[2,1])']
6-element Array{Int64,1}:
4
3
5
5
2
3
If you rather want full rows than use filter!:
julia> filter!((x)->(1 in x), collect(eachrow(A)))
6-element Array{SubArray{Int64,1,Array{Int64,2},Tuple{Int64,Base.Slice{Base.OneTo{Int64}}},true},1}:
[1, 4]
[1, 3]
[5, 1]
[1, 5]
[2, 1]
[1, 3]

Functioning of permutedims in Julia unclear

What does permutedims() do when called upon a multidimensional array?
From its name, it is evident it has something to do with the dimentions of the array. However, when running the code below, the output is unexpected and not clear.
A = Array{Int64}(undef, 100,100,100)
B = permutedims(A, (1,2,3))
println(A == B)
Output:
`true`
So does it create a copy of the original array? and what is the use of the tuple passed?
The docs of Julia sometimes do not have a complete explanation on a given topic.
permutedims(A::AbstractArray, perm)
perms is a tuple specifying the new order for the dimensions of A, where 1 corresponds to the first dimension (rows), 2 corresponds to the second dimension (columns), 3 corresponds to pages, and so on i.e. this function will return a copy of the array with its dimensions according to the specified perms.
What happened in the code in the question is that by passing the tuple (1,2,3), we were telling Julia that place the first dim of A in the place of the first dim of B and the second in the place of second and so on. This basically created a copy of the array A.
USE CASE EXAMPLE
A = ones(10,20,30) # Creates an array full of 1 of the size (10,20,30)
B = permutedims(A, (3,1,2))
println(A == B)
println(size(B))
OUTPUT
false
(30, 10, 20)
There are a few ways of using permutedims. It is useful as a non-recursive version of LinearAlgebra.transpose. When you want to convert a vector to a row vector or vice-versa, and the vector elements are simple (not other vectors or arrays), do this:
julia> v = [1, 2]
2-element Array{Int64,1}:
1
2
julia> v_row = permutedims(v)
1×2 Array{Int64,2}:
1 2
To transpose a matrix where the elements are simple, do this:
julia> m = [ 1 2
3 4 ]
2×2 Array{Int64,2}:
1 2
3 4
julia> permutedims(m, (2,1))
2×2 Array{Int64,2}:
1 3
2 4
julia> m = [ 1 2 3
4 5 6 ]
2×3 Array{Int64,2}:
1 2 3
4 5 6
julia> permutedims(m, (2,1))
3×2 Array{Int64,2}:
1 4
2 5
3 6
To permute the values of an n-dimensional array by each dimension, use permutedims(array, (newfirst_dim, newsecond_dim, ..)), where newfirst_dim, newsecond_dim
... are each one of the available dimensions 1:n and all dimensions are used:
julia> a = reshape(Vector(1:(2*3*4)), (2,3,4))
2×3×4 Array{Int64,3}:
[:, :, 1] =
1 3 5
2 4 6
[:, :, 2] =
7 9 11
8 10 12
[:, :, 3] =
13 15 17
14 16 18
[:, :, 4] =
19 21 23
20 22 24
julia> permutedims(a, (1,2,3)) # identity
2×3×4 Array{Int64,3}:
[:, :, 1] =
1 3 5
2 4 6
[:, :, 2] =
7 9 11
8 10 12
[:, :, 3] =
13 15 17
14 16 18
[:, :, 4] =
19 21 23
20 22 24
julia> permutedims(a, (3,2,1)) # reverse the dims
4×3×2 Array{Int64,3}:
[:, :, 1] =
1 3 5
7 9 11
13 15 17
19 21 23
[:, :, 2] =
2 4 6
8 10 12
14 16 18
20 22 24
julia> # the new last dimension is the old first dimension (length=2)

Adding an additional dimension to an array

Note: This question/answer is copied from the Julia Slack channel.
If I have an arbitrary Julia Array, how can I add another dimension.
julia> a = [1, 2, 3, 4]
4-element Array{Int64,1}:
1
2
3
4
The desired output would be e.g.:
julia> a[some_magic, :]
1×4 Array{Int64,2}:
1 2 3 4
Or:
julia> a[:, some_magic]
4×1 Array{Int64,2}:
1
2
3
4
A less tricky thing I usually do to achieve this is:
julia> reshape(a, 1, :)
1×4 Array{Int64,2}:
1 2 3 4
julia> reshape(a, :, 1)
4×1 Array{Int64,2}:
1
2
3
4
(it also seems to involve less typing)
Finally a common case requiring transforming a vector to a column matrix can be done:
julia> hcat(a)
4×1 Array{Int64,2}:
1
2
3
4
EDIT also if you add trailing dimensions you can simply use ::
julia> a = [1,2,3,4]
4-element Array{Int64,1}:
1
2
3
4
julia> a[:,:]
4×1 Array{Int64,2}:
1
2
3
4
julia> a[:,:,:]
4×1×1 Array{Int64,3}:
[:, :, 1] =
1
2
3
4
The trick is so use [CartesianIndex()] to create the additional axes:
julia> a[[CartesianIndex()], :]
1×4 Array{Int64,2}:
1 2 3 4
And:
julia> a[:, [CartesianIndex()]]
4×1 Array{Int64,2}:
1
2
3
4
If you want to get closer to numpy's syntax, you can define:
const newaxis = [CartesianIndex()]
And just use newaxis.

Concatenating arrays in Julia

If the two Int arrays are, a = [1;2;3] and b = [4;5;6], how do we concatenate the two arrays in both the dimensions? The expected outputs are,
julia> out1
6-element Array{Int64,1}:
1
2
3
4
5
6
julia> out2
3x2 Array{Int64,2}:
1 4
2 5
3 6
Use the vcat and hcat functions:
julia> a, b = [1;2;3], [4;5;6]
([1,2,3],[4,5,6])
help?> vcat
Base.vcat(A...)
Concatenate along dimension 1
julia> vcat(a, b)
6-element Array{Int64,1}:
1
2
3
4
5
6
help?> hcat
Base.hcat(A...)
Concatenate along dimension 2
julia> hcat(a, b)
3x2 Array{Int64,2}:
1 4
2 5
3 6
Square brackets can be used for concatenation:
julia> a, b = [1;2;3], [4;5;6]
([1,2,3],[4,5,6])
julia> [a; b]
6-element Array{Int64,1}:
1
2
3
4
5
6
julia> [a b]
3×2 Array{Int64,2}:
1 4
2 5
3 6
You can use the cat function to concatenate any number of arrays along any dimension. The first input is the dimension over which to perform the concatenation; the remaining inputs are all of the arrays you wish to concatenate together
a = [1;2;3]
b = [4;5;6]
## Concatenate 2 arrays along the first dimension
cat(1,a,b)
6-element Array{Int64,1}:
1
2
3
4
5
6
## Concatenate 2 arrays along the second dimension
cat(2,a,b)
3x2 Array{Int64,2}:
1 4
2 5
3 6
## Concatenate 2 arrays along the third dimension
cat(3,a,b)
3x1x2 Array{Int64,3}:
[:, :, 1] =
1
2
3
[:, :, 2] =
4
5
6
when encountered Array{Array,1}, the grammer is a little bit different, like this:
julia> a=[[1,2],[3,4]]
2-element Array{Array{Int64,1},1}:
[1, 2]
[3, 4]
julia> vcat(a)
2-element Array{Array{Int64,1},1}:
[1, 2]
[3, 4]
julia> hcat(a)
2×1 Array{Array{Int64,1},2}:
[1, 2]
[3, 4]
julia> vcat(a...)
4-element Array{Int64,1}:
1
2
3
4
julia> hcat(a...)
2×2 Array{Int64,2}:
1 3
2 4
ref:
... combines many arguments into one argument in function definitions
In the context of function definitions, the ... operator is used to combine many different arguments into a single argument. This use of ... for combining many different arguments into a single argument is called slurping
Functional way to concatanate 2 arrays is to use reduce function.
a = rand(10, 1)
b = rand(10, 1)
c = reduce(hcat, [ a, b])

Resources