I have an n-dimensional array A and want to slice it dynamically, i.e., given a list of array dimensions, like [2 4], and a list of values, like [6 8], I want
B = A(:,6,:,8,:,:,:,:,...)
List lengths are unknown. Using eval would work but is not an option. This question is a generalization of a previous post to multiple indices and dimensions without a for-loop.
You can still use the previous post I linked to (which I originally flagged as a duplicate) to answer your question. This original post only slices in one dimension. I originally flagged it as a duplicate and closed it because all you need to do is replace one line of code in the original post's accepted answer to achieve what you want. However, because it isn't that obvious, I have decided to reopen the question and answer the question for you.
Referring to the previous post, this is what Andrew Janke (the person with the accepted answer on the linked post) did (very clever I might add):
function out = slice(A, ix, dim)
subses = repmat({':'}, [1 ndims(A)]);
subses{dim} = ix;
out = A(subses{:});
Given a matrix A, an index number ix and the dimension you want to access dim, the above function would equivalently perform:
out = A(:, :, ..., ix, :, :,...:);
^ ^ ^ ^
dimensions --> 1 2 dim dim+1
You would access your desired dimension in dim, and place what value you want to use to slice into that dimension. As such, you'd call it like this:
out = slice(A, ix, dim);
How the function works is that subses would generate a cell array of ':' strings (that will eventually be converted into ':' operators) that is as long as the total number of dimensions of A. Next, you would access the element at dim, which corresponds to the dimension you want and you would replace this with ix. You would then unroll this cell array so that we would access A in the manner that you see in the above equivalent statement.
Who would have thought that you can use strings to index into an array!?
Now, to generalize this, all you have to do is make one small but very crucial change. ix would now be a vector of indices, and dim would be a vector of dimensions you want to access. As such, it would look something like this:
function out = slice(A, ix, dim)
subses = repmat({':'}, [1 ndims(A)]);
subses(dim) = num2cell(ix);
out = A(subses{:});
The only difference we see here is the second line of the code. We have to use num2cell so that you can convert each element into a cell array, and we slice into this cell array to replace the : operators with your desired dimensions. Note that we are using () braces and not {} braces. () braces are used to slice through cell arrays while {} are used to access cell array contents. Because we are going to assign multiple cells to subses, () is needed. We then perform our slicing in A accordingly.
As such, given your problem and with the above modifications, you would do:
out = slice(A, [6 8], [2 4]);
Be advised that ix and dim must contain the same number of elements and they must be 1D. Also, ix and dim should be sensible inputs (i.e. not floating point and negative). I don't do this error checking because I'm assuming you know what you're doing and you're smart enough to know how to use this properly.
Good luck!
Related
If I want eachindex but only of a specific dimension, what's a good way to accomplish this?
E.g. x is a 3x5x7 Array
x = rand(3,5,7)
And I'd like to get the 2nd dimension's indexes of 1:5, ideally in a way that doesn't assume that the indexing starts at 1
The axes function is a generic way to get that.
axes(A, d)
Return the valid range of indices for array A along dimension d.
julia> A = fill(1, (5,6,7));
julia> axes(A, 2)
Base.OneTo(6)
New to julia, so this is probably very easy.
I have an n-by-m array and a vector of length n and want to repeat each row of the array the number of times in the corresponding element of the vector. For example:
mat = rand(3,6)
v = vec([2 3 1])
The result should be a 6-by-6 array. I tried the repeat function but
repeat(mat, inner = v)
yields a 6×18×1 Array{Float64,3}: array instead so it takes v to be the dimensions along which to repeat the elements. In matlab I would use repelem(mat, v, 1) and I hope julia offers something similar. My actual matrix is a lot bigger and I will have to call the function many times, so this operation needs to be as fast as possible.
It has been discussed to add a similar thing to Julia Base, but currently it is not implemented yet AFAIK. You can achieve what you want using the inverse_rle function from StatsBase.jl:
julia> row_idx = inverse_rle(axes(v, 1), v)
6-element Array{Int64,1}:
1
1
2
2
2
3
and now you can write:
mat[row_idx, :]
or
#view mat[row_idx, :]
(the second option creates a view which might be relevant in your use case if you say that your mat is large and you need to do such indexing many times - which option is faster will depend on your exact use case).
I have a big cell array A=cell(a,b,c,d) and a row vector B with dimensions 1-by-b.
I want to build a loop in MATLAB that does the following:
for i=1:n
B = Calculate_row(input1,input2) %this is a function that creates my B row
A{a,:,c,i} = B(:)
end
anyway if I try to do A{a,:,c} = B(:) I receive the following error:
Expected one output from a curly brace or dot indexing expression, but there were b results.
And if I try to do A(a,:,c) = B(:) I receive the following error:
Conversion to cell from double is not possible.
Is there a way to do this? (I know a less elegant way that probably works would be to assign each value to the cell separately, but I would prefer not to do it).
One way to do this is to make B a cell array and then take advantage of comma-separated-lists:
B_cell = num2cell(B);
[A{a,:,c}] = B_cell{:} %// or [A{a,:,c,i}] = B_cell{:} if tim's comment is correct
Have a look at Loren Shure's article Deal or No Deal and also this answer for more.
The problem with your syntax, A{a,:,c} = B(:), is that the RHS (i.e. B(:)) is just one single matrix whereas the LHS is a comma-separated-list of b results. So you are basically requesting that 1 output be assigned to b variables and MATLAB doesn't like that, also hence the error message.
The problem with A(a,:,c) = B(:) is that indexing a cell array with () returns a cell array and you can't just assign a matrix (i.e. B(:)) to a cell array hence you second error.
I would like to create a column vector X by repeating a smaller column vector G of length h a number n of times. The final vector X will be of length h*n. For example
G = [1;2;3;4] #column vector of length h
X = [1;2;3;4;1;2;3;4;1;2;3;4] #ie X = [G;G;G;G] column vector of
length h*n
I can do this in a loop but is there an equivalent to the 'fill' function that can be used without the dimensions going wrong. When I try to use fill for this case, instead of getting one column vector of length h*n I get a column vector of length n where each row is another vector of length h. For example I get the following:
X = [[1,2,3,4];[1,2,3,4];[1,2,3,4];[1,2,3,4]]
This doesn't make sense to me as I know that the ; symbol is used to show elements in a row and the space is used to show elements in a column. Why is there the , symbol used here and what does it even mean? I can access the first row of the final output X by X[1] and then any element of this by X[1][1] for example.
Either I would like to use some 'fill' equivalent or some sort of 'flatten' function if it exists, to flatten all the elements of the X into one column vector with each entry being a single number.
I have also tried the reshape function on the output but I can't get this to work either.
Thanks Dan Getz for the answer:
repeat([1, 2, 3, 4], outer = 4)
Type ?repeat at the REPL to learn about this useful function.
In older versions of Julia, repmat was an alternative, but it has now been deprecated and absorbed into repeat
As #DanGetz has pointed out in a comment, repeat is the function you want. From the docs:
repeat(A, inner = Int[], outer = Int[])
Construct an array by repeating the entries of A. The i-th element of inner specifies the number of times that the individual entries of the i-th dimension of A should be repeated. The i-th element of outer specifies the number of times that a slice along the i-th dimension of A should be repeated.
So an example that does what you want is:
X = repeat(G; outer=[k])
where G is the array to be repeated, and k is the number of times to repeat it.
I will also attempt to answer your confusion about the result of fill. Julia (like most languages) makes a distinction between vectors containing numbers and numbers themselves. We know that fill(5, 5) produces [5, 5, 5, 5, 5], which is a one-dimensional array (a vector) where each element is 5.
Note that fill([5], 5), however, produces a one-dimensional array (a vector) where each element is [5], itself a vector. This prints as
5-element Array{Array{Int64,1},1}:
[5]
[5]
[5]
[5]
[5]
and we see from the type that this is indeed a vector of vectors. That of course is not the same thing as the concatenation of vectors. Note that [[5]; [5]; [5]; [5]; [5]] is syntax for concatenation, and will return [5, 5, 5, 5, 5] as you might expect. But although ; syntax (vcat) does concatenation, fill does not do concatenation.
Mathematically (under certain definitions), we may imagine R^(kn) to be distinct (though isomorphic to) from (R^k)^n, for instance, where R^k is the set of k-tuples of real numbers. fill constructs an object of the latter, whereas repeat constructs an object of the former.
As long as you are working with 1-dimensional arrays (Vectors)...
X=repmat(G,4) should do it.
--
On another note, Julia makes no distinction between row and column vector, they are both one-dimensional arrays.
[1,2,3]==[1;2;3] returns true as they are both 3-element Array{Int64,1} or vectors (Array{Int,1} == Vector{Int} returns true)
This is one of the differences between Matlab and Julia...
If, for some specific reason you want to do it, you can create 2-dimensional Arrays (or Matrices) with one of the dimensions equal to 1.
For example:
C = [1 2 3 4] will create a 1x4 Array{Int64,2} the 2 there indicates the dimensions of the Array.
D = [1 2 3 4]' will create a 4x1 Array{Int64,2}.
In this case, C == D returns false of course. But neither is a Vector for Julia, they are both Matrices (Array{Int,2} == Matrix{Int} returns true).
I have a string array:
size(entries)
ans =
1 19413
I would like to rearrange the array to 4853 rows and 4 columns:
output=permute(entries,[4853 4]);
but get following error:
Error using permute ORDER contains an invalid permutation index.
What is the (probably obvious thing) I am doing wrong? thanks
You currently have 19413 elements, yet you wish to reshape this into a 4853 x 4 matrix that consists of 4853 * 4 = 19412 elements. No function in the world will help you do this because the original and target amount of elements don't match - they're off by one element. If you remove one of the elements...say... the last one, then we're getting somewhere.
Supposing you made a mistake and included that extra element by accident, you don't use permute here, but you use reshape. The second argument to reshape is the amount of elements to spread out for each target dimension, and that's what you're looking for. First remove the extraneous element that appears at the end of the array, then reshape the matrix:
output = reshape(entries(1:end-1),[4853 4]);
I'm 3 years late, but here's to anyone still looking for an answer.
In your case as mentioned above, yes you should use reshape() while minding that you preserve the total number of elements.
You use permute() when you want to reorder the dimensionality of an n-dimensional (ND) matrix.
The ORDER parameter specifies the order of the columns.
For example, if matrix A is LxMxN, the following line would make it MxLxN.
A = permute(A,[2 1 3]);
Hope this clears things.