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

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

Related

Create all possible n-tuples from n vectors in Matlab, ordered in a specific way

Consider n row vectors in Matlab, each of size 1xU. For example,
U=20;
n=3;
sU=[U U U];
vectors = arrayfun(#(x) {1:x}, sU);
where vector{1} is the first row vector, vector{2} is the second row vector,..., vector{n} is the last row vector.
We create the matrix Tcoord of size U^n x n reporting all the possible n-tuples from the n row vectors. For each row i of Tcoord, Tcoord(i,1) is an element of the first row vector, Tcoord(i,2) is an element of the second row vector, ... , Tcoord(i,n) is an element of the last row vector.
Tcoord_temp = cell(1,n);
[Tcoord_temp{:}] = ndgrid(vectors{:});
Tcoord_temp = cat(n+1, Tcoord_temp{:});
Tcoord = reshape(Tcoord_temp,[],n);
Suppose now that I augment each of the n row vectors of 3 elements. For example,
vectors_augmented{1}=[vectors{1} 8 9 10];
vectors_augmented{2}=[vectors{2} 11 12 13];
vectors_augmented{3}=[vectors{3} 14 15 16];
I then create a matrix similar to Tcoord but now using vectors_augmented.
Tcoord_temp = cell(1,n);
[Tcoord_temp{:}] = ndgrid(vectors_augmented{:});
Tcoord_temp = cat(n+1, Tcoord_temp{:});
Tcoord_augmented = reshape(Tcoord_temp,[],n); %(U+3)^nxn
I would like your help to re-order the rows of the matrix Tcoord_augmented in a matrix Tcoord_augmented_reshape such that
Tcoord_augmented_reshape(1:U^n,:) is equal to Tcoord.
The remaining rows of Tcoord_augmented_reshape contains the other left rows of Tcoord_augmented.
The simplest approach is to build an auxiliary zero-one matrix the same size as Tcoord_augmented and sort rows based on that:
aug_size = [3 3 3]; % augment size of each vector. Not necessarily equal
vectors_aux = arrayfun(#(a) {[false(1,U) true(1, a)]}, aug_size);
T_aux = cell(1,n);
[T_aux{:}] = ndgrid(vectors_aux{:});
T_aux = cat(n+1, T_aux{:});
T_aux = reshape(T_aux,[],n);
[~, ind] = sortrows(T_aux, n:-1:1); % indices of stably sorting the rows.
% Most significant column is rightmost, as per your code
Tcoord_augmented_reorder = Tcoord_augmented(ind, :);

i have a matrix of 18*9, I want to calculate the mean values of 6 rows each and get a resultant matrix of 3*9

I want to calculate the mean value of 6 rows each of a matrix 600*9 dimension. The new matrix should be of size 100*9. Can someone help me ?
I saw this code for calculating mean of 2 rows each and it is working fine foe 2 rows :-
x = rand(1028, 18);
result1 = zeros(1028/2, 18);
for ii = 1:1028/2;
result1(ii,:) = mean(x((2*ii-1):(2*ii),:));
end;
The output matrix will have the mean of first 6 rows ( of the input matrix) as the first row and next 6 rows as the 2nd row and so on
Let the data be defined as
x = rand(600, 9); % example data
N = 6; % group size
The desired result can be obtained very easily without loops:
Reshape the data matrix as a 3-D array, where the size along the first dimension is the desired group size;
Compute the mean along the first dimension;
Remove the first dimension, which is now a singleton.
result = reshape(mean(reshape(x, N, [], size(x,2)), 1), [], size(x,2));
Note how this single line contains the three steps described above:
reshape(x, N, [], size(x,2)) % step 1
mean( , 1) % step 2
result = reshape( , [], size(x,2)); % step 3
Using mean and a for loop:
data = rand(600,9);
num_groups = 6;
group_size = size(data,1)/num_groups;
mean_by_group = NaN(num_groups, size(data,2));
for k = 1:num_groups
mean_by_group(k,:) = mean(data((k-1)*group_size+(1:group_size),:),1)
end

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

Select entries from matrices according to indices in another matrix in MATLAB

I have matrices:
a= 0.8147 0.1270 0.6324
0.9058 0.9134 0.0975
b= 0.2785 0.9649 0.9572
0.5469 0.1576 0.4854
0.9575 0.9706 0.8003
c = 0.1419 0.7922
0.4218 0.9595
0.9157 0.6557
and also I have another matrix
I= 1 3 1 1
2 1 3 2
I want to get d matrix such that
d= a(1,3) b(3,1) c(1,1)
a(2,1) b(1,3) c(3,2)
where indices come as two consecutive entries of I matrix.
This is one example I get. However, I get different size matrices for a,b,c,.. and I.
Added: I is m x (n+3) which includes indices, and other (n+2) matrices which have corresponding entries are X,A1,A2,...,An,Y. When n is given, A1,A2,...,An matrices are generated.
Can someone please help me to write Matlab code for this task?
You can do it with varargin. Assuming that your matrices are constructed such that you can form your desired output in the way you want (Updated according to Carmine's answer):
function out = IDcombiner(I, varargin)
out = zeros(size(I, 1), nargin-1);
idx = #(m, I, ii) (sub2ind(size(m), I(:, ii), I(:, ii+1)));
for ii = 1:1:nargin-1
out(:, ii) = varargin{ii}(idx(varargin{ii}, I, ii));
end
Now using this function you can make your selection on a flexible number of inputs:
out = IDcombiner(I, a, b, c)
out =
0.6324 0.9575 0.1419
0.9058 0.9572 0.6557
There is also a one-liner solution, which I do not recommend, since it dramatically decreases the readability of the code and doesn't help you gain much:
IDcombiner = #(I,varargin) ...
cell2mat(arrayfun(#(x) varargin{x}(sub2ind(size(varargin{x}), ...
I(:,x), I(:,x+1))), 1:nargin-1, 'UniformOutput', false));
Normally a matrix is not interpreted as a list of indices, but you can have this if you use sub2ind. To use it you need the size of the matrix you are addressing. Let's make an example starting with a:
a(sub2ind(size(a), I(:,1), I(:,2)))
The code does not change if you first assign the newly generated matrices to a variable name.
will use the column I(:,1) as rows and I(:,2) as columns.
To make the code more readable you can define an anonymous function that does this, let's call it idx:
idx = #(m,I,i)(sub2ind(size(m), I(:,i), I(:,i+1)))
So finally the code will be
d = [a(idx(a,I,1)), b(idx(b,I,2)), c(idx(c,I,3))]
The code does not change if you first assign the newly generated matrices to a variable name.
Other details
Let's make an example with 2 central matrices:
a = rand(3,1) % 3 rows, 1 column
b = rand(3,3) % 3 rows, 3 columns
c = rand(3,3) % another squared matrix
d = rand(3,1) % 3 rows, 1 column
The definition of the anonymous function is the same, you just change the definition of the output vector:
output = [a(idx(a,I,1)), b(idx(b,I,2)), c(idx(c,I,3)), d(idx(d,I,3))]
Keep in mind that following that pattern you always need a I matrix with (n_matrices + 1) columns.
Generalization
Let's generalize this code for a number n of central matrices of size rxr and for "side matrices" of size rxc. I will use some values of those parameters for this example, but you can use what you want.
Let me generate an example to use:
r = 3;
c = 4;
n = 3;
a = rand(r,c); % 2D array
b = rand(r,r,n); % 3D array, along z = 1:n you have 2D matrices of size rxr
c = rand(r,c);
I = [1 3 1 2 1 3; 2 1 3 1 1 1];
The code I wrote can easily be extended using cat to append matrices (note the 2 in the function tells MATLAB to append on the direction of the columns) and a for cycle:
idx = #(m,I,i)(sub2ind(size(m), I(:,i), I(:,i+1)))
d = a(idx(a,I,1));
for i = 1:n
temp = b(:,:,i);
d = cat(2,d,temp(idx(tmp,I,i+1)));
end
d = cat(2,d,c(idx(c,I,n+1)));
If you really don't want to address anything "by hand", you can use cell arrays to put all the matrices together and then cyclically apply the anonymous function to each matrix in the cell array.

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