Matlab: Cut Vector at missing values and create new vectors - matlab

I want to write a Matlab script.
In my example I have a vector A=[1 3 4 5 7 8 9 10 11 13 14 15 16 17 19 20 21]
Now I want to cut the vector automatically at the points where a number is missing(here the numbers 2, 6, 12, 18 are missing).
As a result I want to have the vectors [1] and [3 4 5] and [7 8 9 10 11] and [13 14 15 16 17] and [19 20 21]. So as you can see the new vectors have different lenghts.
I thought about using a for loop, but I am not sure how to write these new vectors.
Thank you for your help :)

One liner with diff, cumsum & accumarray -
out = accumarray(cumsum([0 ; diff(A(:))~=1])+1,A(:),[],#(x) {x})
Sample run -
>> A
A =
1 3 4 5 7 8 9 10 ...
11 13 14 15 16 17 19 20 21
>> celldisp(out)
out{1} =
1
out{2} =
3
4
5
out{3} =
7
8
9
10
11
out{4} =
13
14
15
16
17
out{5} =
19
20
21

This is one approach:
s = [find(diff(A(:).')>1) numel(A)]; %'// detect where consecutive difference exceeds 1
s = [s(1) diff(s)]; %// sizes of groups
result = mat2cell(A(:).', 1, s); %'// split into cells according to those sizes
In your example, this gives
>> celldisp(result)
result{1} =
1
result{2} =
3 4 5
result{3} =
7 8 9 10 11
result{4} =
13 14 15 16 17
result{5} =
19 20 21
Another approach (computes group sizes differently):
s = diff([0 sum(bsxfun(#lt, A(:), setdiff(1:max(A(:).'), A(:).')), 1) numel(A)]);
result = mat2cell(A(:).', 1, s);

Related

Reshaping vector to n-d matrix in row-wise order

I'm trying to convert a vector into a 3d matrix in a row-wise manner.
For example, my vector is:
a = 1:18;
and I'd like to convert this to a 2x3x3 matrix:
b(:,:,1) = [1 2 3; 4 5 6];
b(:,:,2) = [7 8 9; 10 11 12];
b(:,:,3) = [13 14 15; 16 17 18];
but the reshape function (i.e. reshape(a, 2,3,3)) arranges the elements in a column-wise fashion as:
val(:,:,1) =
1 3 5
2 4 6
val(:,:,2) =
7 9 11
8 10 12
val(:,:,3) =
13 15 17
14 16 18
How can I obtain the matrix b from vector a?
Use combination of reshape and permute.
b = permute(reshape(a,3,2,3),[2 1 3]);
b(:,:,1) =
1 2 3
4 5 6
b(:,:,2) =
7 8 9
10 11 12
b(:,:,3) =
13 14 15
16 17 18
I found a (or the) solution:
b = permute(reshape(reshape(a,3,[])',2,3,[]), [1,3,2])
b(:,:,1) =
1 2 3
4 5 6
b(:,:,2) =
7 8 9
10 11 12
b(:,:,3) =
13 14 15
16 17 18

Fastest code to merge two matrices of different dimension in Matlab

I have two matrices in Matlab A and B respectively of dimension MxN and GxN.
M can be greater or smaller than G.
A and B do not contain identical rows.
I want to construct a matrix C of dimension Hx(N+2) in the following way
C=[];
for i=1:size(A,1)
%if A(i,1:end-1) coincides with a row in B(:,1:end-1) (it can coincide with only one row at most)
%then C=[C;A(i,1:end) B(j,end)]; %where j is the index of the identical row in B
%otherwise impose C=[C;A(i,1:end) 0]
end
for i=1:size(B,1)
%if B(i,1:end-1) does not coincide with any row in A(:,1:end-1)
%then impose C=[C; B(i,1:end-1) 0 B(i,end)];
end
For example:
A=[1 2 3 4 5 100; 6 7 8 9 10 101; 11 12 13 14 15 102];
B=[6 7 8 9 10 103; 15 16 17 18 19 104]
C=[1 2 3 4 5 100 0; 6 7 8 9 10 101 103; 11 12 13 14 16 102 0; 15 16 17 18 19 0 104]
As M and G can be very high, I am looking for the fastest way to perform this.
You can use ismember + indexing to do your task:
[idx1,idx2] = ismember(A(:,1:end-1), B(:,1:end-1), 'rows');
idx3 = ~ismember(B(:,1:end-1), A(:,1:end-1), 'rows');
C(idx1,:) = [A(idx1,:) B(idx2(idx1),end)];
C(~idx1,:) = [A(~idx1,:) zeros(sum(~idx1),1)];
C=[C;B(idx3,1:end-1) zeros(sum(idx3),1) B(idx3,end)];
You could also use intersect with a bit of preallocation to speed up the assignment (if M or G gets really large).
A=[1 2 3 4 5 100; 6 7 8 9 10 101; 11 12 13 14 15 102];
B=[6 7 8 9 10 103; 15 16 17 18 19 104];
C=[1 2 3 4 5 100 0; 6 7 8 9 10 101 103; 11 12 13 14 16 102 0; 15 16 17 18 19 0 104];
[M,N] = size(A);
G = size(B,1);
[tmp, idxA, idxB] = intersect(A(:,1:end-1),B(:,1:end-1),'rows')
idxBnotA = setdiff([1:G],idxB);
H = M + G - length(idxA);
C1 = zeros(H,N+1);
C1(1:M,1:N) = A;
C1(idxA,end) = B(idxB,end);
C1(M+1:end,1:end-2) = B(idxBnotA,1:end-1);
C1(M+1:end,end) = B(idxBnotA,end)

Sorting every layer of 3D matrix by one column each

I have a 3D matrix. Say it is:A = randi(15,[4,3,2]). I want to sort the 2nd column of each layer in an ascending order, but the other columns simply stayed in their respective rows. How can I do that?
If the two layers are like this
val(:,:,1) =
6 12 13
10 14 8
15 8 2
4 3 14
val(:,:,2) =
10 1 8
2 15 12
14 11 1
1 6 11
Then I want a result like this
val(:,:,1) =
4 3 14
15 8 2
6 12 13
10 14 8
val(:,:,2) =
10 1 8
1 6 11
14 11 1
2 15 12
If you have the Image Processing Toolbox, using blockproc is one solution:
val(:,:,1) = [ ...
6 12 13
10 14 8
15 8 2
4 3 14]
val(:,:,2) = [ ...
10 1 8
2 15 12
14 11 1
1 6 11]
%// row indices to used for sorting
rowidx = 2;
[n,m,p] = size( val );
%// get a 2D matrix
val2D = reshape(val, n, [], 1)
%// sorting
out2D = blockproc(val2D,[n,m],#(x) sortrows(x.data,rowidx))
%// transform back to 3D
out3D = reshape(out2D, n, m, [])
Without the toolbox, maybe a little slower:
temp = arrayfun(#(x) sortrows(val(:,:,x),rowidx),1:size(val,3),'uni',0)
out3D = cat(3,temp{:})
out3D(:,:,1) =
4 3 14
15 8 2
6 12 13
10 14 8
out3D(:,:,2) =
10 1 8
1 6 11
14 11 1
2 15 12

How can I discard some unwanted rows from a matrix in Matlab?

I have a matrix
A= [1 2 3 4; 5 6 7 8; 9 10 11 12; 13 14 15 16; 17 18 19 20]
I want to do some calculation on this matrix. But actually I do not need all the rows. So I have to discard some of the rows from the above matrix before doing a calculation. After discarding 3 rows, we will have a new matrix.
B= [1 2 3 4; 9 10 11 12; 17 18 19 20];
Now I have to use B to make some other calculations. So how can I discard some of the unwanted rows from a matrix in matlab? Any suggestion will be helpful. Thanks.
Try this: (Use when no. of rows to keep is lesser)
%// Input A
A = [1 2 3 4; 5 6 7 8; 9 10 11 12; 13 14 15 16; 17 18 19 20];
%// Rows (1-3,5) you wanted to keep
B = A([1:3, 5],:)
Output:
B =
1 2 3 4
5 6 7 8
9 10 11 12
17 18 19 20
Alternative: (Use when no. of rows to discard is lesser)
%// rows 2 and 3 discarded
A([2,3],:) = [];
Output:
>> A
A =
1 2 3 4
13 14 15 16
17 18 19 20
Note: Here (in the alternate method), the output replaces the original A. So you need to back up A if you need it afterwards. You could do this before discarding operation to backup Input matrix
%// Input A is backed up in B
B = A;
You can select the indices of the rows you want to keep:
A([1,3,5],:)
ans =
1 2 3 4
9 10 11 12
17 18 19 20

Concatenate every n-th row

I got a data set in a matrix like the following (imported from Excel):
matrix =
Cat1 1 2 3 4
Cat2 9 10 11 12
Cat3 17 18 19 20
Cat1 5 6 7 8
Cat2 13 14 15 16
Cat3 21 22 23 24
I would like to reshape it into 3 vectors (one for every category) of the same size to do a stacked bar plot. Vectors should look like this after reshape operation (It would be nice if the vector had the name of the first column and the matrix could be of any size):
cat1 = [ 1 2 3 4 5 6 7 8]
cat2 = [ 9 10 11 12 13 14 15 16]
cat3 = [17 18 19 20 21 22 23 24]
I sincerely hope this is not duplicate. I couldn't produce a working solution with the help of the other reshape questions.
If your data is a matrix, you can manipulate the order of the rows when indexing, so you can do something like this:
rows = reshape(1:size(matrix, 1), n, []).';
res = reshape(matrix(rows, :).', [], n).';
The resulting matrix res is composed of the concatenated rows.
This solution holds for cell arrays as well, but you'll need an additional cell2mat to turn the result into a matrix.
Example
matrix = [1:4; 9:12; 17:20; 5:8; 13:16; 21:24];
n = 3;
rows = reshape(1:size(matrix, 1), n, []).';
res = reshape(matrix(rows, :).', [], n).';
The result is:
res =
1 2 3 4 5 6 7 8
9 10 11 12 13 14 15 16
17 18 19 20 21 22 23 24
EDIT:
Try the following:
%# dataset stored in a cell array
data = {
'Cat1' 1 2 3 4
'Cat2' 9 10 11 12
'Cat3' 17 18 19 20
'Cat1' 5 6 7 8
'Cat2' 13 14 15 16
'Cat3' 21 22 23 24
};
%# get all possible values of first column,
%# and map them to integer indices
[L,~,IDX] = unique(data(:,1));
%# for each possible "category"
groups = cell(max(IDX),1);
for i=1:max(IDX)
%# get the rows of numeric data matching current category
M = data(IDX==i, 2:end)';
%# flatten matrix into a vector and store in cell (row-major order)
groups{i} = [M{:}];
end
Now you can access the i-th "cat" vector as: groups{i}
>> [cat1,cat2,cat3] = deal(groups{:})
cat1 =
1 2 3 4 5 6 7 8
cat2 =
9 10 11 12 13 14 15 16
cat3 =
17 18 19 20 21 22 23 24
Note that the matching "cat" labels are stored in L{i} (the mapping keys)