Matlab- moving numbers to new row if condition is met - arrays

I have a variable like this that is all one row:
1 2 3 4 5 6 7 8 9 2 4 5 6 5
I want to write a for loop that will find where a number is less than the previous one and put the rest of the numbers in a new row, like this
1 2 3 4 5 6 7 8 9
2 4 5 6
5
I have tried this:
test = [1 2 3 4 5 6 7 8 9 2 4 5 6 5];
m = zeros(size(test));
for i=1:numel(test)-1;
for rows=1:size(m,1)
if test(i) > test(i+1);
m(i+1, rows+1) = test(i+1:end)
end % for rows
end % for
But it's clearly not right and just hangs.

Let x be your data vector. What you want can be done quite simply as follows:
ind = [find(diff(x)<0) numel(x)]; %// find ends of increasing subsequences
ind(2:end) = diff(ind); %// compute lengths of those subsequences
y = mat2cell(x, 1, ind); %// split data vector according to those lenghts
This produces the desired result in cell array y. A cell array is used so that each "row" can have a different number of columns.
Example:
x = [1 2 3 4 5 6 7 8 9 2 4 5 6 5];
gives
y{1} =
1 2 3 4 5 6 7 8 9
y{2} =
2 4 5 6
y{3} =
5

If you are looking for a numeric array output, you would need to fill the "gaps" with something and filling with zeros seem like a good option as you seem to be doing in your code as well.
So, here's a bsxfun based approach to achieve the same -
test = [1 2 3 4 5 6 7 8 9 2 4 5 6 5] %// Input
idx = [find(diff(test)<0) numel(test)] %// positions of row shifts
lens = [idx(1) diff(idx)] %// lengths of each row in the proposed output
m = zeros(max(lens),numel(lens)) %// setup output matrix
m(bsxfun(#le,[1:max(lens)]',lens)) = test; %//'# put values from input array
m = m.' %//'# Output that is a transposed version after putting the values
Output -
m =
1 2 3 4 5 6 7 8 9
2 4 5 6 0 0 0 0 0
5 0 0 0 0 0 0 0 0

Related

MATLAB: Remove specific elements from array

Question 1: I have a 1x15 array, comprising of positive integers and negative integers. I wish to implement a MATLAB code which keeps all positive integers and skips the cells with negative contents.
I have tried the following:
X = [1 2 3 4 5 -10 1 -5 4 6 8 9 2 4 -2];
[r c] = size(X);
for i=1:r
for j=1:c
if X(i,j)<0
X(i,j)=X(i,j+1)
end
end
end
The output should be:
X_new = [1 2 3 4 5 1 4 6 8 9 2 4]
How do I do this?
Question 2:
X = [1 2 3 4 5 -10 1 -5 4 6 8 9 2 4 -2]
Y = [5 3 8 9 4 5 6 7 4 7 9 5 2 1 4]
From Question 1,
X_new = [1 2 3 4 5 1 4 6 8 9 2 4]
I need to delete the corresponding values in Y so that:
Y_new = [5 3 8 9 4 6 4 7 9 5 2 1]
How do I perform this?
In MATLAB, manipulating arrays and matrices can be done much easier than for-loop solutions,
in your task, can do find and delete negative value in the array, simply, as follows:
Idx_neg = X < 0; % finding X indices corresponding to negative elements
X ( Idx_neg ) = []; % removing elements using [] operator
Y ( Idx_neg ) = []; % removing corresponding elements in Y array

Pairs of random numbers Matlab

I am trying to generate random numbers between 1 and 6 using Matlab's randperm and calling randperm = 6.
Each time this gives me a different array let's say for example:
x = randperm(6)
x = [3 2 4 1 5 6]
I was wondering if it was possible to create pairs of random numbers such that you end up with x like:
x = [3 4 1 2 5 6]
I need the vector to be arranged such that 1 and 2 are always next to each other, 3 and 4 next to each other and 5 and 6 next to each other. As I'm doing something in Psychtoolbox and this order is important.
Is it possible to have "blocks" of random order? I can't figure out how to do it.
Thanks
x=1:block:t ; %Numbers
req = bsxfun(#plus, x(randperm(t/block)),(0:block-1).'); %generating random blocks of #
%or req=x(randperm(t/block))+(0:block-1).' ; if you have MATLAB R2016b or later
req=req(:); %reshape
where,
t = total numbers
block = numbers in one block
%Sample run with t=12 and block=3
>> req.'
ans =
10 11 12 4 5 6 1 2 3 7 8 9
Edit:
If you also want the numbers within each block in random order, add the following 3 lines before the last line of above code:
[~, idx] = sort(rand(block,t/block)); %generating indices for shuffling
idx=bsxfun(#plus,idx,0:block:(t/block-1)*block); %shuffled linear indices
req=req(idx); %shuffled matrix
%Sample run with t=12 and block=3
req.'
ans =
9 8 7 2 3 1 12 10 11 5 6 4
I can see a simple 3 step process to get your desired output:
Produce 2*randperm(3)
Double up the values
Add randperm(2)-2 (randomly ordered pair of (-1,0)) to each pair.
In code:
x = randperm(3)
y = 2*x([1 1 2 2 3 3])
z = y + ([randperm(2),randperm(2),randperm(2)]-2)
with result
x = 3 1 2
y = 6 6 2 2 4 4
z = 6 5 2 1 3 4

Finding the distribution of length of islands in a 2D array?

I will explain my question using an example. Imagine you have a 2D matrix like below:
5 4 3 8 0 0
5 4 2 9 1 0
5 6 2 7 2 0
5 4 7 9 0 0
5 6 7 1 2 0
By islands I mean column groups of same elements (except zeros).
I would like to find the histogram of length of islands except those consisting of zero elements.
This matrix has
island-length occurrence
5 1
2 3
1 12
How can I realize this task using Matlab ?
Maybe there are shorter possibilities, but this will do - and it is fully vectorized:
A = [5 4 3 8 0 0
5 4 2 9 1 0
5 6 2 7 2 0
5 4 7 9 0 0
5 6 7 1 2 0]
%// pad zeros to first line of A
X(2:size(A,1)+1,:) = A;
%// differences of X
dX = diff(X)
%// cumulative sum of "logicalized" differences
cs = cumsum(logical(dX(:)))
%// filter out zeros
cs = cs(logical(A(:)))
%// count occurances
aa = accumarray(cs,1)
%// unique occurances
uaa = unique(aa)
%// count unique occurances
occ = hist(aa,uaa).'
%// accumarray may introduce new zeros, filter out
mask = logical(uaa)
%// output
out = [occ(mask) uaa(mask)]
out =
12 1
3 2
1 5
Needed a slight modification to one of my old snippets to filter the zeros. Here you go:
% Your Matrix
A = [ 5 4 3 8 0 0;
5 4 2 9 1 0;
5 6 2 7 2 0;
5 4 7 9 0 0;
5 6 7 1 2 0];
% Find Edges (Ends of Islands)
B = diff(A);
B = [ones(1,size(A,2));B~=0;ones(1,size(A,2))];
% At each column, find distances between island edges, filter out zero islands.
R = cell(size(A,2),1);
for i = 1:size(A,2)
[C ~] = find(B(:,i));
Ac = A(C(1:end-1),i);
D = diff(C);
D(Ac==0)=[];
R{i} = D;
end
% Find histogram of island lengths
R = R(find(~cellfun(#isempty,R)),1);
R = cell2mat(R);
[a,~,c] = unique(R);
out = [a, accumarray(c,ones(size(R)))];

How to remove extra duplicated elements in each row of a matrix in matlab?

Let's say I have a matrix
A = [2 3 2 5 6 7 2;
1 2 5 4 5 6 7;
7 5 3 9 8 1 2];
How do I remove 2s and keep one 2 in the first row and keep only one 5 in the second row?
The result can't be a matrix anymore, because each row will have a different length. You can obtain the result as a cell array of row vectors as follows:
B = mat2cell(A, ones(size(A,1),1)); %// convert matrix to cell array of its rows
B = cellfun(#(x) unique(x,'stable'), B, 'uniformoutput', 0); %// stably remove duplicates
For your example matrix
A = [2 3 2 5 6 7 2;
1 2 5 4 5 6 7;
7 5 3 9 8 1 2];
this gives
B{1} =
2 3 5 6 7
B{2} =
1 2 5 4 6 7
B{3} =
7 5 3 9 8 1 2
If you want to find out which values are duplicates within the row, you can do something like this:
[vals, col_idx] = sort(A,2);
idx = bsxfun(#plus,(col_idx-1)*size(A,1), (1:size(A,1))');
is_duplicate(idx(:,2:end)) = vals(:,1:end-1) == vals(:,2:end);
is_duplicate = reshape(is_duplicate, size(A));
is_duplicate =
0 0 1 0 0 0 1
0 0 0 0 1 0 0
0 0 0 0 0 0 0
From there, it depends what outcome you are looking for. You could set the duplicates to NaN or some other value, or you could set them to NaN, but then shift them to the end of the row, using something like the following:
col_idx = cumsum(~is_duplicate, 2);
idx = bsxfun(#plus,(col_idx-1)*size(A,1), (1:size(A,1))');
A_new = nan(size(A));
A_new(idx(~is_duplicate)) = A(~is_duplicate);
A_new =
2 3 5 6 7 NaN NaN
1 2 5 4 6 7 NaN
7 5 3 9 8 1 2

Vectorized Reshaping of Columns in an Array

I have an array A, and want to reshape the last four elements of each column into a 2x2 matrix. I would like the results to be stored in a cell array B.
For example, given:
A = [1:6; 3:8; 5:10]';
I would like B to contain three 2x2 arrays, such that:
B{1} = [3, 5; 4, 6];
B{2} = [5, 7; 6, 8];
B{3} = [7, 9; 8, 10];
I can obviously do this in a for loop using something like reshape(A(end-3:end, ii), 2, 2) and looping over ii. Can anyone propose a vectorized method, perhaps using something similar to cellfun that can apply an operation repeatedly to columns of an array?
The way I do this is to look at the desired indices and then figure out a way to generate them, usually using some form of repmat. For example, if you want the last 4 items in each column, the (absolute) indices into A are going to be 3,4,5,6, then add the number of rows to that to move to the next column to get 9,10,11,12 and so on. So the problem becomes generating that matrix in terms of your number of rows, number of columns, and the number of elements you want from each column (I'll call it n, in your case n=4).
octave:1> A = [1:6; 3:8; 5:10]'
A =
1 3 5
2 4 6
3 5 7
4 6 8
5 7 9
6 8 10
octave:2> dim=size(A)
dim =
6 3
octave:3> n=4
n = 4
octave:4> x=repmat((dim(1)-n+1):dim(1),[dim(2),1])'
x =
3 3 3
4 4 4
5 5 5
6 6 6
octave:5> y=repmat((0:(dim(2)-1)),[n,1])
y =
0 1 2
0 1 2
0 1 2
0 1 2
octave:6> ii=x+dim(1)*y
ii =
3 9 15
4 10 16
5 11 17
6 12 18
octave:7> A(ii)
ans =
3 5 7
4 6 8
5 7 9
6 8 10
octave:8> B=reshape(A(ii),sqrt(n),sqrt(n),dim(2))
B =
ans(:,:,1) =
3 5
4 6
ans(:,:,2) =
5 7
6 8
ans(:,:,3) =
7 9
8 10
Depending on how you generate x and y, you can even do away with the multiplication, but I'll leave that to you. :D
IMO you don't need a cell array to store them either, a 3D matrix works just as well and you index into it the same way (but don't forget to squeeze it before you use it).
I gave a similar answer in this question.

Resources