How to perform automatic array reshaping in Julia? - 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

Related

Broadcast over Array Dimension

Let's say I have a two-dimensional array
a = [1 2 3; 1 2 3]
2×3 Array{Int64,2}:
1 2 3
1 2 3
and I would like sum along a dimension, e.g. along dimension 1 yielding
[2, 4, 6]
or along dimension 2 yielding
[6, 6]
How is this done properly in Julia?
julia> sum(a; dims=1)
1×3 Array{Int64,2}:
2 4 6
julia> sum(a; dims=2)
2×1 Array{Int64,2}:
6
6
You can drop the dimension with vec.
What Jun Tian suggests is the standard way to do it. However, it is also worth to know a more general pattern:
julia> sum.(eachrow(a))
2-element Array{Int64,1}:
6
6
julia> sum.(eachcol(a))
3-element Array{Int64,1}:
2
4
6
In this case sum can be replaced by any collection aggregation function.

Expanding array along a dimension

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

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]

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.

I want to create 2D array with 5 rows by 1 column

If I want to create 2D array with 1 row by 5 columns.
I could do this
julia> a = [1 2 3 4 5]
1×5 Array{Int64,2}:
1 2 3 4 5
But to create 2D array with 5 rows by 1 column. I have tried
julia> b = [1; 2; 3; 4; 5]
5-element Array{Int64,1}:
1
2
3
4
5
But I got back a 1D array which is NOT what I wanted
The only way to get it to work is
julia> b=reshape([1 2 3 4 5],5,1)
5×1 Array{Int64,2}:
1
2
3
4
5
Perhaps I am missing some crucial information here.
You could also do a = [1 2 3 4 5]'.
On a side note, for Julia versions > 0.6 the type of a wouldn't be Array{Int64, 2} but a LinearAlgebra.Adjoint{Int64,Array{Int64,2}} as conjugate transpose is lazy in this case. One can get <= 0.6 behavior by a = copy([1 2 3 4 5]').
AFAIK there is no syntactic sugar for it.
I usually write:
hcat([1, 2, 3, 4, 5])
which is short and I find it easy to remember.
If you use reshape you can replace one dimension with : which means you do not have to count (it is useful e.g. when you get an input vector as a variable):
reshape([1 2 3 4 5], :, 1)
Finally you could use:
permutedims([1 2 3 4 5])

Resources