Julia comprehension: convert a 2d array of matrices to a 2d matrix - julia

I'm using Julia comprehension to achieve the following:
Given a matrix
A = [1 2; 3 4],
I want to expand it into
B =
[1, 1, 1, 2, 2;
1, 1, 1, 2, 2;
1, 1, 1, 2, 2;
3, 3, 3, 4, 4;
3, 3, 3, 4, 4].
Right now I'm doing this with
ns = [3, 2]
B = [fill(B[i, j], ns[i], ns[j]) for i = 1:2, j = 1:2]
However, instead of getting a 5x5 matrix, it gives me:
2×2 Array{Array{Int64,2},2}:
[0 0 0; 0 0 0; 0 0 0] [0 0; 0 0; 0 0]
[0 0 0; 0 0 0] [0 0; 0 0]
So how should I convert this 2d array of matrices to a 2d matrix? Or are there other ways to do the expansion I need?

Here are two example ways how you could do it (the first one uses your approach, the second one does not generate intermediate matrices):
julia> A = [1 2; 3 4]
2×2 Array{Int64,2}:
1 2
3 4
julia> ns = [3, 2]
2-element Array{Int64,1}:
3
2
julia> hvcat(2, [fill(A[j, i], ns[j], ns[i]) for i = 1:2, j = 1:2]...)
5×5 Array{Int64,2}:
1 1 1 2 2
1 1 1 2 2
1 1 1 2 2
3 3 3 4 4
3 3 3 4 4
julia> nsexpand = reduce(vcat, (fill(k, ns[k]) for k in axes(ns, 1)))
5-element Array{Int64,1}:
1
1
1
2
2
julia> [A[i, j] for i in nsexpand, j in nsexpand]
5×5 Array{Int64,2}:
1 1 1 2 2
1 1 1 2 2
1 1 1 2 2
3 3 3 4 4
3 3 3 4 4
EDIT
Here is an additional example:
julia> A = [1 4 7 10
2 5 8 11
3 6 9 12]
3×4 Array{Int64,2}:
1 4 7 10
2 5 8 11
3 6 9 12
julia> hvcat(3, A...)
4×3 Array{Int64,2}:
1 2 3
4 5 6
7 8 9
10 11 12
julia> vec(A)
12-element Array{Int64,1}:
1
2
3
4
5
6
7
8
9
10
11
12
So:
the first argument tells you how how many columns you want to produce
hvcat has h before v so it takes elements row-wise
however arrays store columns col-wise
so in effect you have to create the temporary array as a transpose of your target (because hvcat will take its columns to create rows of a target arrays). Actually this is only a coincidence - hvcat does not know that your original elements were storing in a matrix (it takes them as positional arguments to the call and at that time the fact that they were stored in a matrix is lost due to ... operation).

Related

"R" Warning message: In mapply longer argument not a multiple of length of shorter

I am trying to make a simple function to reverse code multiple columns in my R dataframe. I am using lapply and mapply
in the function, which seems to give me the expected outcome, except for the warning message
Warning message:
In mapply("-", max_value, data[, rev]) :
longer argument not a multiple of length of shorter
To illustrate, here is some sample data
{
A = c(3, 3, 3, 3, 3, 3, 3, 3, 3, 3)
B = c(9, 2, 3, 2, 4, 0, 2, 7, 2, 8)
C = c(2, 4, 1, 0, 2, 1, 3, 0, 7, 8)
df1 = data.frame(A, B, C)
print(df1)
}
A B C
1 3 9 2
2 3 2 4
3 3 3 1
4 3 2 0
5 3 4 2
6 3 0 1
7 3 2 3
8 3 7 0
9 3 2 7
10 3 8 8
The function to reverse-code is below:
## columns to reverse-code
revcode_cols = c("A", "B")
## function to reverse code variables
reverseCode <- function(data, rev){
# get maximum value per column
max_value = lapply(data, max)
# subtract values in designated cols from max value plus 1
data[, rev] = mapply("-", max_value, data[, rev]) + 1
return(data)
}
reverseCode(df1, revcode_cols)
A B C
1 1 1 2
2 1 8 4
3 1 7 1
4 1 8 0
5 1 6 2
6 1 10 1
7 1 8 3
8 1 3 0
9 1 8 7
10 1 2 8
and it gives the right output, but for the warning message. Just wondering which part of my script I need to fix to get rid of the warning message.
The unit for a data.frame is a column and for a vector is a single element. In the mapply/Map, we are passing two input, but in the OP's code, the max is calculated on the whole dataset creating a the 'max_value' as a list of 3 elements, which the data[, rev] is of length 2. We just need to subset the data for calculating the max
reverseCode <- function(data, rev){
# get maximum value per column
max_value = lapply(data[rev], max)
# subtract values in designated cols from max value plus 1
data[, rev] = mapply("-", max_value, data[, rev]) + 1
return(data)
}
-testing
reverseCode(df1, revcode_cols)
A B C
1 1 1 2
2 1 8 4
3 1 7 1
4 1 8 0
5 1 6 2
6 1 10 1
7 1 8 3
8 1 3 0
9 1 8 7
10 1 2 8
Instead of using two apply loops you can do this in one lapply.
reverseCode <- function(data, rev){
data[rev] <- lapply(data[rev], function(x) max(x) - x + 1)
data
}
reverseCode(df1, c("A", "B"))
# A B C
#1 1 1 2
#2 1 8 4
#3 1 7 1
#4 1 8 0
#5 1 6 2
#6 1 10 1
#7 1 8 3
#8 1 3 0
#9 1 8 7
#10 1 2 8

Julia problems with end, matrix

When I type this error jumps in julia but I don't know why, it should be working./
julia> A = [1 2 3 4; 5 6 7 8; 1 2 3 4; 5 6 7 8]
4×4 Array{Int64,2}:
1 2 3 4
5 6 7 8
1 2 3 4
5 6 7 8
julia> B = A[2:1:end; 2:1:end]
ERROR: syntax: missing last argument in "2:1:" range expression
Stacktrace:
[1] top-level scope at REPL[9]:0
The syntax to index a multidimensional array uses a comma , instead of semicolon ; as separator between dimensions, see https://docs.julialang.org/en/v1/manual/arrays/#man-array-indexing-1. Thus you want to do:
julia> A = [1 2 3 4; 5 6 7 8; 1 2 3 4; 5 6 7 8]
4×4 Array{Int64,2}:
1 2 3 4
5 6 7 8
1 2 3 4
5 6 7 8
julia> B = A[2:1:end, 2:1:end]
3×3 Array{Int64,2}:
6 7 8
2 3 4
6 7 8
Note also that you can omit 1 in the range specification, as step 1 is the default:
julia> A[2:end, 2:end]
3×3 Array{Int64,2}:
6 7 8
2 3 4
6 7 8

Add vector as row to matrix in Julia

I have a vector and a matrix (Array{T,1} and Array{T,2}) in my Julia code and I would like to append them such that the vector becomes a new row in the matrix (should be first row). I've tried several methods (cat, etc.) but keep getting errors which I believe are related to the different shape of the data. See the example below.
julia> v = Vector([1, 2, 3])
3-element Array{Int64,1}:
1
2
3
julia> m = Matrix([4 5 6; 7 8 9])
2×3 Array{Int64,2}:
4 5 6
7 8 9
julia> cat(v,m,dims=(1,2))
5×4 Array{Int64,2}:
1 0 0 0
2 0 0 0
3 0 0 0
0 4 5 6
0 7 8 9
What I actually want is
1 2 3
4 5 6
7 8 9
I realize that I can get this to work with transpose(v) but I was hoping to avoid extra calls.
Thanks!
As long as you can change the construction of v to a 1 x 3 array, you can avoid the transpose:
julia> v = [1 2 3]
1×3 Array{Int64,2}:
1 2 3
julia> m = [4 5 6; 7 8 9]
2×3 Array{Int64,2}:
4 5 6
7 8 9
julia> vcat(v, m)
3×3 Array{Int64,2}:
1 2 3
4 5 6
7 8 9
I think that just doing the transpose
julia> v2 = [1, 2, 3]
3-element Array{Int64,1}:
1
2
3
julia> vcat(v2', m)
3×3 Array{Int64,2}:
1 2 3
4 5 6
7 8 9
is almost as efficient though.

Make a matrix of matrices

I want to have a matrix whose elements are matrices too.
For example
A=[[1 2 3;3 4 1;2 3 6] [1 4 5;4 8 7;2 0 1];[1 5 8;6 4 7;2 0 0] [2 1 5;4 7 7;2 4 6]]
How can I make this matrix in Julia?
A = first.([([1 2 3;3 4 1;2 3 6],) ([1 4 5;4 8 7;2 0 1],);
([1 5 8;6 4 7;2 0 0],) ([2 1 5;4 7 7;2 4 6],)])
works (on Julia 0.6). Making elements tuples stops the fusing of the submatrices and then first. untuples them.
I'm not sure about a shorthand / literal, but you can construct it and then populate it:
B=Matrix{Matrix}(3,3)
Out[4]:
3×3 Array{Array{T,2} where T,2}:
#undef #undef #undef
#undef #undef #undef
#undef #undef #undef
B[1,1]=[1 2 ; 3 4]
B
Out[8]:
3×3 Array{Array{T,2} where T,2}:
[1 2; 3 4] #undef #undef
#undef #undef #undef
#undef #undef #undef
I don't know why Julia has this (from my POV strange) property:
julia> [1 2 [3 4]]
1×4 Array{Int64,2}:
1 2 3 4
But we could use it to make this trick:
julia> A=[[[1 2 3;3 4 1;2 3 6]] [[1 4 5;4 8 7;2 0 1]];
[[1 5 8;6 4 7;2 0 0]] [[2 1 5;4 7 7;2 4 6]]]
Another strange possibility is (be aware that it is visually transposed!):
julia> A=hcat([[1 2 3;3 4 1;2 3 6], [2 1 5;4 7 7;2 4 6]],
[[1 4 5;4 8 7;2 0 1], [1 5 8;6 4 7;2 0 0]])
or (this needs to be visually transposed too!)
julia> A=reshape([[1 2 3;3 4 1;2 3 6], [2 1 5;4 7 7;2 4 6],
[1 4 5;4 8 7;2 0 1], [1 5 8;6 4 7;2 0 0],],
(2,2))
Edit:
Ad your additional question - you could create Array of desired length and then use reshape:
U = reshape(Matrix{Float64}[zeros(8, 5) for i in 1:20*20], (20,20));
You can create an empty matrix of the aformentioned dimensions first by :
X = zeros(Int64, (3, 3,4))
You can further assign each matrix accordingly:
X[:,:,1] = [1 2 3;3 4 1;2 3 6]
X[:,:,2] = [1 4 5;4 8 7;2 0 1]
X[:,:,3] = [1 5 8;6 4 7;2 0 0]
X[:,:,4] = [2 1 5;4 7 7;2 4 6]
And the matrix X is :
julia > X
julia > 3×3×4 Array{Int64,3}:
[:, :, 1] =
1 2 3
3 4 1
2 3 6
[:, :, 2] =
1 4 5
4 8 7
2 0 1
[:, :, 3] =
1 5 8
6 4 7
2 0 0
[:, :, 4] =
2 1 5
4 7 7
2 4 6
An easier thing to do is to read every element as is by a vector and reshape it.
x = [1,2,3,3,4,1,2,3,6,1,4,5,4,8,7,2,0,1,1,5,8,6,4,7,2,0,0,2,1,5,4,7,7,2,4,6]
x = reshape(x, (3, 3, 4))
This will result in 4 matrices that need a transpose, so for that you can use the permutedims as folows to change the order of the first and second dimensions(each matrix):
permutedims(x,(2,1,3))

Sample rows from an array in Julia

I can sample from a 1-D array just fine. E.g.
julia> a = [1; 2; 3]
3-element Array{Int64,1}:
1
2
3
julia> sample(a, myweights, 5)
5-element Array{Int64,1}:
1
2
1
3
3
I can also take weighted samples:
julia> myweights = weights([0.8, 0.1, 0.1])
StatsBase.WeightVec{Float64,Array{Float64,1}}([0.8,0.1,0.1],1.0)
julia> sample(a, myweights, 5)
5-element Array{Int64,1}:
2
1
1
1
1
I'd like to do the same thing for a 2D array, but sampling by row and not by element. E.g. if I have the array
julia> b = [1 1 1; 2 2 2; 3 3 3]
3×3 Array{Int64,2}:
1 1 1
2 2 2
3 3 3
I'd like to be able to take unweighted and weighted samples that give me outputs like
1 1 1
2 2 2
1 1 1
1 1 1
3 3 3
How can I do this?
The simplest solution here is to sample from the indices of the rows, and then use that to index into your matrix:
julia> idxs = sample(axes(b, 1), myweights, 10)
10-element Array{Int64,1}:
1
1
1
2
1
1
3
1
1
1
julia> b[idxs, :]
10×3 Array{Int64,2}:
1 1 1
1 1 1
1 1 1
2 2 2
1 1 1
1 1 1
3 3 3
1 1 1
1 1 1
1 1 1

Resources