How to create a dynamic array in MATLAB - arrays

Description of this array:
10 dimensions, which represent 10 clusters.
Each dimension's length is not fixed, and should be zero at first.
After clustering, one coordinate (x,y) will be assigned to the corresponding cluster.
Example:
If (1,1) belongs to cluster 10, (2,2) belongs to cluster 9, and given a sequence of coordinates
(1,1) (1,1) (1,1) (1,1) (1,1) (2,2)
then A(1) to A(8) have no element while A(9) has 1 element with value (2,2) and A(10) has 5 elements with value (1,1).
I tried to use cell array, and I "kind of" get one, here is my code.
A = cell(10,0)
%create empty cell array
A(10,end+1) = {[1,1]}
%assign (1,1) to cluster 10
So now A is:
Then suppose we have (2,2) and it should assigned to A(9)
A(9,end) = {[2,2]}
Looks fine, but then if we assign (1,1) to A(10) again,
A(10,end) = {[1,1]}
Then the length is still the same and A(1) to A(8) are not empty!
My question is, is there any other method that can help me to create a dynamic array?

In MATLAB, we'll often store multiple coordinates into a single array, such that p(3,:) is the 3rd point. [One of the reasons for this is that this is a much more efficient way of storing data, as each array has an overhead, and storing many points as individual arrays within a e.g. cell array therefore is very wasteful of memory.]
I would suggest you use that method to store coordinates within each cluster. For example:
A = cell(10,1); % 10 clusters, we won't change the size of A
A{10}(end+1,:) = [1,1];
A{9}(end+1,:) = [2,2];
A{10}(end+1,:) = [1,1];
Now we can see what is inside A:
>> A{1}
ans =
[]
>> A{9}
ans =
2 2
>> A{10}
ans =
1 1
1 1
Note that A{10} is the contents of the cell (in this case a numeric array), whereas A(10) is a cell array with one cell.
A{10}(1,:) is the first coordinate in cluster 10. size(A{10},1) is the number of coordinates in cluster 10.
A{10}(end+1,:) = [1,1] is one way of appending an element. end+1 is a non-existing location, meaning the array will be extended to accommodate the new data assigned there. An alternative method is A{10} = [A{10} ; 1,1]. I'm not sure if these two methods are equivalent or not. In the case of a vector, or when appending a column, then the end+1 method is much more efficient, so I always recommend that method in all cases.

You need to create a cell array of cells.
Use repmat to create a 10 x 1 array of empty cells:
A = repmat({{}},10,1);
In order to access nested cells you need to chain indexes:
A{10}(end+1) = {[1,1]};
A{9} (end+1) = {[2,2]};
A{10}(end+1) = {[1,1]};
Or more simple indexing:
A{10}{end+1} = [1,1];
A{9} {end+1} = [2,2];
A{10}{end+1} = [1,1];

You should probably take a look at matlab's map https://www.mathworks.com/help/matlab/ref/containers.map.html
E.G. For your question:
A = containers.Map('KeyType','int32','ValueType','any');
A(9) = [2 2];
A(10) = [1 1;1 1;1 1;1 1;1 1];
Now A(9) has a vector with values (2,2) and A(10) has 10 row vectors with values (1,1).
You can retrieve the values the same way that you input the values:
>> A(9)
ans =
2 2
>> A(10)
ans =
1 1
1 1
1 1
1 1
1 1
Adding vectors to already defined keys:
>> A(9) = [A(9); 2 2];
>> A(9)
ans =
2 2
2 2

Related

Argmax of a multidimensional array along a subset of dimensions in Matlab

Say, Y is a 7-dimensional array, and I need an efficient way to maximize it along the last 3 dimensions, that will work on GPU.
As a result I need a 4-dimensional array with maximal values of Y and three 4-dimensional arrays with the indices of these values in the last three dimensions.
I can do
[Y7, X7] = max(Y , [], 7);
[Y6, X6] = max(Y7, [], 6);
[Y5, X5] = max(Y6, [], 5);
Then I have already found the values (Y5) and the indices along the 5th dimension (X5). But I still need indices along the 6th and 7th dimensions.
Here's a way to do it. Let N denote the number of dimensions along which to maximize.
Reshape Y to collapse the last N dimensions into one.
Maximize along the collapsed dimensions. This gives argmax as a linear index over those dimensions.
Unroll the linear index into N subindices, one for each dimension.
The following code works for any number of dimensions (not necessarily 7 and 3 as in your example). To achieve that, it handles the size of Y generically and uses a comma-separated list obtained from a cell array to get N outputs from sub2ind.
Y = rand(2,3,2,3,2,3,2); % example 7-dimensional array
N = 3; % last dimensions along which to maximize
D = ndims(Y);
sz = size(Y);
[~, ind] = max(reshape(Y, [sz(1:D-N) prod(sz(D-N+1:end))]), [], D-N+1);
sub = cell(1,N);
[sub{:}] = ind2sub(sz(D-N+1:D), ind);
As a check, after running the above code, observe for example Y(2,3,1,2,:) (shown as a row vector for convenience):
>> reshape(Y(2,3,1,2,:), 1, [])
ans =
0.5621 0.4352 0.3672 0.9011 0.0332 0.5044 0.3416 0.6996 0.0610 0.2638 0.5586 0.3766
The maximum is seen to be 0.9011, which occurs at the 4th position (where "position" is defined along the N=3 collapsed dimensions). In fact,
>> ind(2,3,1,2)
ans =
4
>> Y(2,3,1,2,ind(2,3,1,2))
ans =
0.9011
or, in terms of the N=3 subindices,
>> Y(2,3,1,2,sub{1}(2,3,1,2),sub{2}(2,3,1,2),sub{3}(2,3,1,2))
ans =
0.9011

reshape 2d array to 3d array in matlab /octave

How can I reshape a 2d array to a 3d array with the last column being used as pages?
All data found in array2d should be in pages
example:
array2d=[7,.5,12; ...
1,1,1; ...
1,1,1; ...
4,2,4; ...
2,2,2; ...
2,2,2; ...
3,3,3; ...
3,3,3; ...
3,3,3];
The first page in the array would be
7,.5,12;
1,1,1;
1,1,1;
The second page in the array would be
4,2,4;
2,2,2;
2,2,2;
The third page in the array would be
3,3,3;
3,3,3;
3,3,3;
This is a 9x3 array how can I get it to be a 9x3x? (not sure what this number should be so I placed a question mark as a place holder) multidimensional array?
What I'm trying to get is to have
All the ones would be on one dimension/page all the two's would be another dimension/page etc... –
I tried reshape(array2d,[9,3,1]) and it's still a 9x3
Use permute with reshape -
N = 3; %// Cut after every N rows to form a "new page"
array3d = permute(reshape(array2d,N,size(array2d,1)/N,[]),[1 3 2]) %// output
Assuming that each slice of your matrix is the same in dimensions, we can do this very easily. Let's call the number of rows and columns that each slice would have to be M and N respectively. In your example, this would be M = 3 and N = 3. As such, assuming array2d is of the above form, we can do the following:
M = 3;
N = 3; %// This is also simply the total number of columns we have,
%// so you can do size(array2d, 2);
outMatrix = []; %// Make this empty. We will populate as we go.
%// Figure out how many slices we need
numRows = size(array2d,1) / M;
for k = 1 : numRows
%// Extract the k'th slice
%// Reshape so that it has the proper dimensions
%// of one slice
sliceK = reshape(array2d(array2d == k), M, N);
%// Concatenate in the third dimension
outMatrix = cat(3,outMatrix,sliceK);
end
With your example, we thus get:
>> outMatrix
outMatrix(:,:,1) =
1 1 1
1 1 1
1 1 1
outMatrix(:,:,2) =
2 2 2
2 2 2
2 2 2
outMatrix(:,:,3) =
3 3 3
3 3 3
3 3 3
This method should generalize for any number of rows and columns for each slice, provided that each slice shares the same dimensions.
Your array is already of size 1 in the 3rd dimension (in other words, it is already 9x3x1, to prove this try entering array2d(1,1,1)). If you want to concatenate 2d matrices along the 3rd dimension you can use cat.
For example:
a = [1,2;3,4];
b = [5,6;7,8];
c = cat(3,a,b);
c will be a 2x2x2 matrix.
This piece of code is specific for this example, I hope you will be able to understand how to go for other data samples.
out2 = [];
col = size(array2d,2);
for i = 1:3
temp2 = reshape(array2d(array2d == i),[],col);
out2 = cat(3,out2,temp2);
end

Accessing elements in matlab, get pixels of color image (array) from indices stored in another array

A and B are mask indices (row and column respectively) and C is an image and I want to note the color values stored in C for which the indices are stored in A and B. If A and B would be something like [1, 2, 3] and [20, 30, 40] so I would like to find C(1, 20, :), C(2, 30, :) and C(3, 40, :).
If I do D = C(A, B, :), I get an array of size 3x3x3 in this case, however I want an array of size 3x1x3. I know I am messing with the indexing, is there a simple way to do this without writing a loop?
Simply stating, is there a way to do the following without a loop:
for i = 1:10
D(i, :) = C(A(i), B(i), :)
end
You need to convert subindices to linear indices. You can use sub2ind for that:
r = C(sub2ind([size(C,1) size(C,2) 1],A,B,1*ones(1,length(A))));
g = C(sub2ind([size(C,1) size(C,2) 2],A,B,2*ones(1,length(A))));
b = C(sub2ind([size(C,1) size(C,2) 3],A,B,3*ones(1,length(A))));
The n x 1 x 3 result you want would be simply cat(3, r.',g.',b.').
Why not something like
C = C(A,B(i),:);
You could use a for statement to get the value of i or set it some other way.
It sounds like everything is working as it should. In your example you've indexed 9 elements of C using A and B. Then D is a 3x3x3 array with the dimensions corresponding to [row x col x color_mask(RGB)]. Why would the second dimension be reduced to 1 unless B only contained one value (signifying you only want to take elements from one column)? Of course A and B must not contain values higher than the number of rows and columns in C.
A = [1 2 3];
B = [20];
D = C(A,B,:);
size(D)
>> 3 1 3
EDIT: Ok, I see what you mean now. You want to specify N number of points using A(Nx1) and B(Nx1). Not NxN number of points which is what you are currently getting.

Matlab Sparse Array Index Reassignment

Does anyone know if there is a way to do a simple reordering of the row-column positions in a sparse array in Matlab?
I have a sparse array which corresponds to the adjacency matrix of a graph that I am trying to analyze, and I would like to reorder the vertices in my graph by some calculated metric (while hopfully preserving the way that the mapping was constructed)
Does anyone have any suggestions on a way to do this? I am new to Matlab and am not yet completely familiar with all of the tools that it has for Matrix manipulations.
With a sparse matrix you assign entry values the same way as you would with a normal matrix. For example:
>> a = sparse(1:2, 3:4, [1 1], 4, 5, 7)
a =
(1,3) 1
(2,4) 1
a(1,3) = 0; a(1,2) = 1; % move the "1" from (1,3) to (1,2)
>> a
a =
(1,2) 1
(2,4) 1
You can also assign whole columns or rows. For example, this swaps columns 2 and 3:
aux = a(:,3);
a(:,3) = a(:,2);
a(:,2) = aux;

How to sum matrix (vector) elements in a structure

I have a M x N sized structure array with fields var and val which are vectors.
What I would like to do is to get an M x N sized matrix A where each element A(i, j) contains the sum value of vector var (or val) from structure array
For example:
myStructure(1,5)
ans =
var: 1
val: [0.0100 0.1800 0.8100]
sum(myStructure(1,5).val)
ans =
1
myStructure(7,8)
ans =
var: [1 3]
val: [1x9 double]
myStructure(7,8).val
ans =
Columns 1 through 6
0.1111 0.1111 0.1111 0.1111 0.1111 0.1111
Columns 7 through 9
0.1111 0.1111 0.1111
Therefore A(1,5) should be 1 and the same way all elements A(i,j) should be equal to sum(myStructure(i,j).val).
Does anyone know how this could be done in Matlab without using for loops?
I've tried to use sum function in a number of ways (sum(messages.val) and sum(messages(:,:).val) ...) but couldn't get the desired result.
You can get the field elements into one matrix using:
svals = [myStructure.val];
If val is always the same length (let's name it P), this'll be a numel(myStructure)*P x 1 vector containing all values of all fields in sequence. You can reshape it of course back:
[N,M]=size(myStructure);
P = numel(myStructure(1,1).val);
svals = reshape(svals,[P M N]);
and now just sum the first dimension, which leaves you the MxN sized A matrix:
A = squeeze(sum(svals,1));
squeeze is applied in this last step to remove the resulting singleton dimension (otherwise A would be of size 1xMxN).
If the vallength can vary, I see no other way than looping it, or using arrayfun, which is essentially the same as looping:
A = arrayfun(#(x) sum(x.val),myStructure);
Here is a slightly different solution. First lets create an array structure for testing:
s = struct();
for i=1:5
for j=1:3
s(i,j).var = i+j;
s(i,j).val = rand(1,randi(10)); %# different lengths vectors
end
end
Now we do the sum:
A = cellfun(#sum, reshape({s.val}, size(s)))
A =
1.9278 3.0719 5.8731
3.2377 0.43874 2.2374
3.0661 2.8892 4.1455
1.9093 1.4758 1.441
4.8731 0.5308 3.4076

Resources