Randomly permute matrix rows keeping same-valued rows adjacent - matlab

How can I randomly permute row keeping those with the same value adjacent to each other? I know that I can use randperm on the rows to randomly permute all rows, but I do not know how to keep the same-valued rows adjacent. Thanks.
A = [0 0 0;
0 0 0;
1 1 1;
1 1 1;
1 1 1;
2 2 2;
2 2 2];
permute_A = [0 0 0;
0 0 0;
2 2 2;
2 2 2;
1 1 1;
1 1 1;
1 1 1];

You have to identify the clusters and then permute them:
Finding the clusters can be done by finding the changes in their differences:
diffA = diff(A);
clusters_start = [1 ; find(any(diffA,2)~=0)+1];
Then the cluster ends are easily found by:
clusters_end = [clusters_start(2:end)-1 ;size(A,1)];
clusters_length = clusters_end-clusters_start+1;
now you know the number of clusters and you can permute them:
Nclusters = numel(clusters_start);
perm_idx = randperm(Nclusters );
clusters_start = clusters_start(perm_idx);
clusters_end = clusters_end(perm_idx);
clusters_length = clusters_length(perm_idx);
and put them in a new matrix:
newA = NaN(size(A));
for ii=1:Nclusters
newA(sum(clusters_length(1:ii-1))+(1:clusters_length(ii)),:) = A(clusters_start(ii):clusters_end(ii),:);
end

Related

Writing a loop that works for any value of N

I have a code for a 1D heat equation. Im trying to format a for loop so that the A matrix will follow a certain pattern of 1 -2 1 down the entire diagonal of a matrix that could be infinite. The pattern starts to take shape when I mess around with the initialized count at the beginning of the for loop but this changes the size of the matrix which fails the rest of the code.
My current code is below. The commented A matrix edits are what it should be.
N = 5;
%A(2,1:3) = [1 -2 1];
%A(3,2:4) = [1 -2 1];
%A(4,3:5) = [1 -2 1];
%A(5,4:6) = [1 -2 1];
A = zeros(N+1,N+1);
A(1,1) = 1;
for count=N:N+1
A(count+1,count:count+2) = [1 -2 1];
end
A(N+1,N+1) = 1;
In Matlab you can often avoid loops. In this case you can get the desired result with 2D convolution:
>> N = 6;
>> A = [1 zeros(1,N-1); conv2(eye(N-2), [1 -2 1]); zeros(1,N-1) 1]
A =
1 0 0 0 0 0
1 -2 1 0 0 0
0 1 -2 1 0 0
0 0 1 -2 1 0
0 0 0 1 -2 1
0 0 0 0 0 1
Or, depending on what you want,
>> A = conv2(eye(N), [1 -2 1], 'same')
A =
-2 1 0 0 0 0
1 -2 1 0 0 0
0 1 -2 1 0 0
0 0 1 -2 1 0
0 0 0 1 -2 1
0 0 0 0 1 -2
There are many simple ways of creating this matrix.
Your loop can be amended as follows:
N = 5;
A = zeros(N+1,N+1);
A(1,1) = 1;
for row = 2:N
A(row, row-1:row+1) = [1 -2 1];
end
A(N+1,N+1) = 1;
I've renamed count to row, we're indexing each row (from 2 to N, skipping the first and last rows), then finding with row-1:row+1 the three indices for that row that you want to address.
Directly indexing the diagonal and off-diagonal elements. Diagonal elements for an NxN matrix are 1:N+1:end. This is obviously more complex, I'd prefer the loop:
N = 6;
A = zeros(N,N);
A(1:N+1:end) = -2;
A(2:N+1:end-2*N) = 1; % skip last row
A(2*N+2:N+1:end) = 1; % skip first row
A(1,1) = 1;
A(N,N) = 1;
Using diag. We need to special-case the first and last rows:
N = 6;
A = diag(-2*ones(N,1),0) + diag(ones(N-1,1),1) + diag(ones(N-1,1),-1);
A(1,1:2) = [1,0];
A(end,end-1:end) = [0,1];

Sum parts of a column separated by 0

Suppose that you have a matrix with:
0 0 0 .... 0
A 0 0 .... 0
B 0 0 .... 0
C 0 0 .... 0
0 0 0 .... 0
D 0 0 .... 0
E 0 0 .... 0
If I want to get a new array with the output:
[A+B+C D+E]
how would you do it? Of course I can always do loops and check for 0 but I'd like to know if there is any other alternative.
Use cumsum to generate a vector of grouping values, and then accumarray to do the sums:
x = [0; 1; 2; 4; 0; 7; 3];
result = accumarray(cumsum(x==0) + (x(1)~=0), x);
gives
result =
7
10
The + (x(1)~=0) part is necessary if x may not start with a zero. This part ensures that for
x = [1; 2; 4; 0; 7; 3];
the result is the desired
result =
7
10
With the above approach, each zero starts a new group. So, for
x = [0; 1; 2; 4; 0; 7; 3; 0; 0; 5; 0];
the result is
result =
7
10
0
5
0
If you want each run of one or more zeros to start a new group: first collapse consecutive zeros in x, then apply the above:
x = [0; 1; 2; 4; 0; 7; 3; 0; 0; 5; 0];
ind = [true; x(2:end)~=0 | x(1:end-1)~=0]; % index to remove a zero if preceded by zero
t = x(ind);
result = accumarray(cumsum(t==0) + (x(1)~=0), t);
gives
result =
7
10
5
0

Cartesian product of row-indices in Matlab

I have a binary matrix A of dimension mxn with m>n in Matlab. I want to construct a matrix B of dimension cxn listing row wise each element of the Cartesian product of the row indices of the ones contained in A. To be more clear consider the following example.
Example:
%m=4;
%n=3;
A=[1 0 1;
0 0 1;
1 1 0;
0 0 1];
%column 1: "1" are at rows {1,3}
%column 2: "1" are at row {3}
%column 3: "1" are at rows {1,2,4}
%Hence, the Cartesian product {1,3}x{3}x{1,2,4} is
%{(1,3,1),(1,3,2),(1,3,4),(3,3,1),(3,3,2),(3,3,4)}
%I construct B by disposing row-wise each 3-tuple in the Cartesian product
%c=6
B=[1 3 1;
1 3 2;
1 3 4;
3 3 1;
3 3 2;
3 3 4];
You can get the cartesian product with the combvec command, for your example:
A=[1 0 1;...
0 0 1;...
1 1 0;...
0 0 1];
[x y]=find(A);
B=combvec(x(y==1).',x(y==2).',x(y==3).').';
% B =
% 1 3 1
% 3 3 1
% 1 3 2
% 3 3 2
% 1 3 4
% 3 3 4
You can expand this to an unknown number of columns by using the associative property of the product.
[x y]=find(A);
u_y=unique(y);
B=x(y==u_y(1)).';
for i=2:length(u_y)
B=combvec(B, x(y==u_y(i)).');
end
B=B.';
One solution (without toolbox):
A= [1 0 1;
0 0 1;
1 1 0;
0 0 1];
[ii,jj] = find(A)
kk = unique(jj);
for i = 1:length(kk)
v{i} = ii(jj==kk(i));
end
t=cell(1,length(kk));
[t{:}]= ndgrid(v{:});
product = []
for i = 1:length(kk)
product = [product,t{i}(:)];
end
You can use use accumarray to obtain vectors with the row indices of nonzero elements for each column. This works for an arbitrary number of columns:
[ii, jj] = find(A);
vectors = accumarray(jj, ii, [], #(x){sort(x.')});
Then apply this answer to efficiently compute the Cartesian product of those vectors:
n = numel(vectors);
B = cell(1,n);
[B{end:-1:1}] = ndgrid(vectors{end:-1:1});
B = cat(n+1, B{:});
B = reshape(B,[],n);
In your example, this gives
B =
1 3 1
1 3 2
1 3 4
3 3 1
3 3 2
3 3 4
In short, I would use find to generate the indices needed for the Cartesian product and then use ndgrid to perform the Cartesian product of these indices. The code to do so is:
clear
close all
clc
A = [1 0 1;
0 0 1;
1 1 0;
0 0 1];
[row,col] = find(A);
[~,ia,~] = unique(col);
n_cols = size(A,2);
indices = cell(n_cols,1);
for ii = 1:n_cols-1
indices{ii} = row(ia(ii):ia(ii+1)-1);
end
indices{end} = row(ia(end):end);
cp_temp = cell(n_cols,1);
[cp_temp{:}] = ndgrid(indices{:});
cp = NaN(numel(cp_temp{1}),n_cols);
for ii = 1:n_cols
cp(:,ii) = cp_temp{ii}(:);
end
cp = sortrows(cp);
cp

Fastest way of generating a logical matrix by given row indices of true values?

What is the most efficient way of generating
>> A
A =
0 1 1
1 1 0
1 0 1
0 0 0
with
>> B = [2 3; 1 2; 1 3]
B =
2 3
1 2
1 3
in MATLAB?
E.g., B(1, :), which is [2 3], means that A(2, 1) and A(3, 1) are true.
My attempt still requires one for loop, iterating through B's row. Is there a loop-free or more efficient way of doing this?
This is one way of many, though sub2ind is the dedicated function for that:
%// given row indices
B = [2 3; 1 2; 1 3]
%// size of row index matrix
[n,m] = size(B)
%// size of output matrix
[N,M] = deal( max(B(:)), n)
%// preallocation of output matrix
A = zeros(N,M)
%// get col indices to given row indices
cols = bsxfun(#times, ones(n,m),(1:n).')
%// set values
A( sub2ind([N,M],B,cols) ) = 1
A =
0 1 1
1 1 0
1 0 1
If you want a logical matrix, change the following to lines
A = false(N,M)
A( sub2ind([N,M],B,cols) ) = true
Alternative solution
%// given row indices
B = [2 3; 1 2; 1 3];
%// number if rows
r = 4; %// e.g. = max(B(:))
%// number if cols
c = 3; %// size(B,1)
%// preallocation of output matrix
A = zeros(r,c);
%// set values
A( bsxfun(#plus, B.', 0:r:(r*(c-1))) ) = 1;
Here's a way, using the sparse function:
A = full(sparse(cumsum(ones(size(B))), B, 1));
This gives
A =
0 1 1
1 1 0
1 0 1
If you need a predefined number of rows in the output, say r (in your example r = 4):
A = full(sparse(cumsum(ones(size(B))), B, 1, 4, size(B,1)));
which gives
A =
0 1 1
1 1 0
1 0 1
0 0 0
You can equivalently use the accumarrray function:
A = accumarray([repmat((1:size(B,1)).',size(B,2),1), B(:)], 1);
gives
A =
0 1 1
1 1 0
1 0 1
Or with a predefined number of rows, r = 4,
A = accumarray([repmat((1:size(B,1)).',size(B,2),1), B(:)], 1, [r size(B,1)]);
gives
A =
0 1 1
1 1 0
1 0 1
0 0 0

How to initialize a vector in MATLAB as per a pattern?

I am completely new to MATLAB.This may be a rather basic question.
Given numerical values for size, extras and max, I need to initialize a 1 X N vector such that the first size elements are 1, the next size are 2, the next size are 3 and so on till the last size elements are set to max. So I need to initialize size number of elements successively to x such that x increments from 1 to max. The extras are the number of leftover cells which are initialized to 0. To illustrate:
size = 3; %# (is same as the quotient of N/max)
extras = 1; %# (is same as remainder of N/max)
max = 3;
N = 10;
original_vector = [0 0 0 0 0 0 0 0 0 0];
The desired output is
Required_vector = [1 1 1 2 2 2 3 3 3 0]
Maybe something using the Kronecker product:
N = 10;
max = 3;
extras = rem(N, max);
size = floor(N/max);
v = [kron([1 : max], ones(1,size)) zeros(1, extras)];
I took a guess about how extras and size are calculated. You said size is N % max and extras is N rem max, but those are the same thing(?).
Some reshaping acrobatics should do it:
>> size = 3;
>> max = 3;
>> N = 10;
>> v = zeros(1, N);
>> v(1:size*max) = reshape(cumsum(ones(max, size))', size*max, 1)
v =
1 1 1 2 2 2 3 3 3 0
Another example:
>> size = 4;
>> max = 5;
>> N = 23;
>> v(1:size*max) = reshape(cumsum(ones(max, size))', size*max, 1)
v =
Columns 1 through 18
1 1 1 1 2 2 2 2 3 3 3 3 4 4 4 4 5 5
Columns 19 through 23
5 5 0 0 0
This is a quite dirty implementation, but as you say you are very new to MATLAB, it might be better for you to see how you can more or less brute force a solution out. The trick here is the index reference done on Vec to place the numbers in. I have ignored the parameter extras and instead fill the vector up as best can be with the elements
N = 23;
max = 3;
size = 4;
Vec = zeros(N,1);
for i=1:max
for j=1:size
Vec((i-1)*size +1 + (j-1)) = i;
end
end
Vec'
extra = sum(Vec==0)
Output:
ans =
1 1 1 1 2 2 2 2 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0
extra =
11
A slight modification of #b3's solution:
N = 10;
mx = 3;
sz = floor(N/mx);
v = zeros(1,N);
v(1:mx*sz) = repmat(1:mx,sz,1)