Count numbers of consecutive 1s and 0s in a vector - matlab

In Matlab I have a vectors that looks like this:
0 0 1 1 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 0 1 0 1
What I want to do now is to count the number of 1 in this vector. Consecutive 1s count as 1. Additionally, I want also to calculate the average and median numbers of 0s between 1s. So for this example:
1s: 5
Median 0s: 3.5
Average 0s: 3
I solved that with a brute force method, that is investigate each element in a loop and check the previous as well as the next element. But I'm sure there has to be a solution that is way faster. Any idea?

Given the data in vector v,
v = [ 0 0 1 1 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 0 1 0 1 ]; % data
compute as follows:
w = [ 1 v 1 ]; % auxiliary vector
runs_zeros = find(diff(w)==1)-find(diff(w)==-1); % lenghts of runs of 0's
% Desired results:
number_ones = length(runs_zeros)-1+v(1)+v(end);
% For average and median, don't count first run if v(1) is 0,
% or last run if v(end) is 0:
average_runs_zeros = mean(runs_zeros(2-v(1):end-1+v(end)));
median_runs_zeros = median(runs_zeros(2-v(1):end-1+v(end)));
This is faster than #TryHard's solution because it doesn't require converting to strings

Okay, so this seems to be working
>> a=[0 0 1 1 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 0 1 0 1];
>> %Remove traling and leading zeros
>> y = a(find(a,1,'first'):find(a,1,'last'));
>> q = diff([0 a 0] == 1);
>> v = find(q == -1) - find(q == 1);
>> length(v) % Consecutive Ones
ans =
5
>> q = diff([0 ~y 0] == 1);
>> v = find(q == -1) - find(q == 1);
>> v
v =
3 4 4 1
>> median(v)
ans =
3.5000
>> mean(v)
ans =
3

You can do it as follows:
dat=[0 0 1 1 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 0 1 0 1];
str = regexprep(num2str(dat),' ','');
[n1 istart1 iend1] = regexp(str,'[1]+','match','start','end');
[n0 istart0 iend0] = regexp(str(min(istart1):max(iend1)),'[0]+','match','start','end');
% number of strings of `1`s
length(n1)
% property of intercalated strings of `0`s
median([iend0-istart0+1])
mean([iend0-istart0+1])

Related

Spread elements of rows to multiple rows

I am working on graph theory using adjacency matrix, I want to split the edges between multiple nodes for instance I have the following initial adjacency matrix :
a= [ 0 2 3;
2 0 1;
3 1 0]
From that matrix its clear that we have 3 nodes, Now I want to split the aforementioned rows (edges) into new random nodes between (1-3) :
split= randi([1 3],1,length(A));
split = [ 2 2 1]
I know now that I need to split the elements of the first row into two rows, the elements of the second rows also into two rows, while the elements of th third row will remain as is, and I'll have new matrix with size 5X5 as following:
A = [0 0 2 0 3;
0 0 0 0 0;
2 0 0 0 1;
0 0 0 0 0;
3 0 1 0 0]
What I want to do is to split the non-zero elements in the first row between this row and the second row, and the third with the fourth, so my matrix will look like:
An = [0 0 2 0 0;
0 0 0 0 3;
2 0 0 0 0;
0 0 0 0 1;
0 3 0 1 0]
It's not fully clear to me what's the initial point, the prerequisites and conditions. I assume that every second row/column is a row/column of zeros. I furthermore assume that the non-zero rows/columns have exactly two non-zero values whereas the second value should be moved to the next row/column. For this, I'd suggest:
A = [0 0 2 0 3 ; 0 0 0 0 0 ; 2 0 0 0 1 ; 0 0 0 0 0 ; 3 0 1 0 0];
for n = 1:2
if n==2
A = A';
end % if
for k = 1:2:size(A,1)-1
m = find(A(k,:));
A(k+(0:1),m(end)) = flipud(A(k+(0:1),m(end)));
end % for
if n==2
A = A';
end % if
end % for
A
A =
0 0 2 0 0
0 0 0 0 3
2 0 0 0 0
0 0 0 0 1
0 3 0 1 0
Here An is directly generated from a without creating A:
a = [ 0 2 3;
2 0 1;
3 1 0];
split = [ 2 2 1];
L = length(a);
cum = cumsum([1 split(1:end-1)]);
%ro = rot90(split - (0:L-1).' + cum-1, -1); %MATLAB R2016b
ro = rot90(bsxfun(#minus,split + cum-1 , (0:L-1).') , -1);
co = repmat(cum, L, 1);
idx = triu(true(L), 1);
N = sum(split);
An = zeros(N);
sub = sub2ind([N,N], ro(idx), co(idx));
An(sub) = a(idx);
An = An + An.'
An =
0 0 2 0 0
0 0 0 0 3
2 0 0 0 0
0 0 0 0 1
0 3 0 1 0

Matlab - Shuffling matrix values based on some conditions

I have the following two matrices which are outputs of a procedure. The size of the matrices may change but both matrices will always be the same size: size(TwoHopMat_1) == size(Final_matrix)
Example:
TwoHopMat_1 =
0 0 0 0 1
0 0 1 1 0
0 1 0 1 0
0 1 1 0 0
1 0 0 0 0
Final_matrix =
1 0 0 0 1
1 0 0 0 1
1 0 0 0 1
1 1 0 0 0
1 0 0 0 1
Now I need to shuffle the final_matrix such that i meet the following conditions after shuffling:
Every column should have a minimum of one 1s
If i have a 1 in a particular position of TwoHopMat_1 then that particular position should not have 1 after shuffling.
The conditions should work even if we give matrices of size 100x100.
first step: set one element of each column of the result matrix ,that is not 1 in Final_matrix ,to 1
second step: then remaining ones randomly inserted into positions of the result matrix that are not 1 in Final_matrix and are not 1 in the first step result
TwoHopMat_1=[...
0 0 0 0 1
0 0 1 1 0
0 1 0 1 0
0 1 1 0 0
1 0 0 0 0];
Final_matrix=[...
1 0 0 0 1
1 0 0 0 1
1 0 0 0 1
1 1 0 0 0
1 0 0 0 1];
[row col] = size(Final_matrix);
result = zeros(row ,col);
%condition 1 & 2 :
notTwoHop = ~TwoHopMat_1;
s= sum(notTwoHop,1);
c= [0 cumsum(s(1:end - 1))];
f= find(notTwoHop);
r = floor(rand(1, col) .* s) + 1;
i = c + r;
result(f(i)) = 1;
%insert remaining ones randomly into the result
f= find(~(result | TwoHopMat_1));
i = randperm(numel(f), sum(Final_matrix(:))-col);
result(f(i)) =1
A possible solution:
function [result_matrix] = shuffle_matrix(TwoHopMat_1, Final_matrix)
% Condition number 2
ones_mat = ones(size(TwoHopMat_1));
temp_mat = abs(TwoHopMat_1 - ones_mat);
% Condition number 1
ones_to_remove = abs(sum(sum(temp_mat)) - sum(sum(Final_matrix)));
while ones_to_remove > 0
% Random matrix entry
i = floor((size(Final_matrix, 1) * rand())) + 1;
j = floor((size(Final_matrix, 2) * rand())) + 1;
if temp_mat(i,j) == 1
temp_mat(i,j) = 0;
ones_to_remove = ones_to_remove - 1;
end
end
result_matrix = temp_mat;
end
Note: this solution uses brute force.

Iteratively and randomly adding ones to a binary vector in matlab

In each iteration I want to add 1 randomly to binary vector,
Let say
iteration = 1,
k = [0 0 0 0 0 0 0 0 0 0]
iteration = 2,
k = [0 0 0 0 1 0 0 0 0 0]
iteration = 3,
k = [0 0 1 0 0 0 0 1 0 0]
, that goes up to length(find(k)) = 5;
Am thinking of for loop but I don't have an idea how to start.
If it's important to have the intermediate vectors (those with 1, 2, ... 4 ones) as well as the final one, you can generate a random permutation and, in your example, use the first 5 indices one at a time:
n = 9; %// number of elements in vector
m = 5; %// max number of 1's in vector
k = zeros(1, n);
disp(k); %// output vector of all 0's
idx = randperm(n);
for p = 1:m
k(idx(p)) = 1;
disp(k);
end
Here's a sample run:
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 1
1 0 0 1 0 0 0 0 1
1 0 0 1 1 0 0 0 1
1 1 0 1 1 0 0 0 1
I wouldn't even use a loop. I would generate a random permutation of indices that sample from a vector going from 1 up to the length of k without replacement then just set these locations to 1. randperm suits the task well:
N = 10; %// Length 10 vector
num_vals = 5; %// 5 values to populate
ind = randperm(N, num_vals); %// Generate a vector from 1 to N and sample num_vals values from this vector
k = zeros(1, N); %// Initialize output vector k to zero
k(ind) = 1; %// Set the right values to 1
Here are some sample runs when I run this code a few times:
k =
0 0 1 1 0 1 1 0 0 1
k =
1 0 0 0 1 0 1 1 0 1
k =
1 0 0 0 1 0 1 1 0 1
k =
0 1 1 1 0 0 1 0 0 1
However, if you insist on using a loop, you can generate a vector from 1 up to the desired length, randomly choose an index in this vector then remove this value from the vector. You'd then use this index to set the location of the output:
N = 10; %// Length 10 vector
num_vals = 5; %// 5 values to populate
vec = 1 : N; %// Generate vector from 1 up to N
k = zeros(1, N); %// Initialize output k
%// Repeat the following for as many times as num_vals
for idx = 1 : num_vals
%// Obtain a value from the vector
ind = vec(randi(numel(vec), 1));
%// Remove from the vector
vec(ind) = [];
%// Set location in output to 1
k(ind) = 1;
end
The above code should still give you the desired effect, but I would argue that it's less efficient.

Indexing of 2D array in matlab

I have a 6X4 matrix M1 containing only zeros.
I also have two 1D arrays Y1 and Y2 each with length 4.The two arrays contain the desired index values. Now, I want to set(convert to 1) the elements of matrix M1 such that
M1(Y1:Y2) is equal to 1
for ex: Y1=[1 2 2 1] and Y2=[3 4 5 3]
then, M1 should be
1 0 0 1
1 1 1 1
1 1 1 1
0 1 1 0
0 0 1 0
0 0 0 0
I can do this using for loop. But is there any optimised way to do it? (I intend to use much bigger matrices)
use cumsum!
>> szM = size(M1);
>> M1( sub2ind( szM, Y1, 1:szM(2) ) ) = 1
M1 =
1 0 0 1
0 1 1 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
>> M1( sub2ind( szM, Y2+1, 1:szM(2) ) ) = -1
M1 =
1 0 0 1
0 1 1 0
0 0 0 0
-1 0 0 -1
0 -1 0 0
0 0 -1 0
>> M = cumsum(M,1)
M =
1 0 0 1
1 1 1 1
1 1 1 1
0 1 1 0
0 0 1 0
0 0 0 0
A pitfall:
If any of Y2 equals 6 than setting Y2+1 to -1 will exceed matrix dimension.
To fix this you can add two lines before setting to -1 the elements of M:
>> cols = 1:szM(2);
>> sel = Y2 < szM(1);
>> M1( sub2ind( szM, Y2(sel)+1, cols(sel) ) ) = -1
A spin-off for Pavan Yalamanchili's answer using bsxfun: (hover to see:)
using bsxfun without offsets:
M1 = bsxfun( #ge, (1:size(M1,1))', Y1 ) & bsxfun( #le, (1:size(M1,1))', Y2 );
There may be other techniques, but this uses element wise operations which are insanely parallel.
A very simple solution. Thanks #Shai
>> [rows, cols] = size(M);
>> Y1=[1 2 2 1]; Y2=[3 4 5 3];
>> M = bsxfun(#ge, (1:rows)', Y1) & bsxfun(#le, (1:rows)', Y2)
M =
1 0 0 1
1 1 1 1
1 1 1 1
0 1 1 0
0 0 1 0
0 0 0 0
Unnecessarily complicated code
[rows, cols] = size(M);
offsets = ((1 : cols) - 1) * rows
Y1 = offsets + Y1;
Y2 = offsets + Y2;
M = reshape(1:numel(M), rows, cols);
M = bsxfun(#ge, M, Y1) & bsxfun(#le, M, Y2);

Is there any function in MATLAB for changing the form of a matrix?

I have to get the unknown matrix by changing the form of a known matrix considering the following rules:
H = [-P'|I] %'
G = [I|P]
where
H is a known matrix
G is an unknown matrix which has to be calculated
I is the identity matrix
So for example, if we had a matrix,
H = [1 1 1 1 0 0;
0 0 1 1 0 1;
1 0 0 1 1 0]
its form has to be changed to
H = [1 1 1 1 0 0;
0 1 1 0 1 0;
1 1 0 0 0 1]
So
-P' = [1 1 1;
0 1 0;
1 1 0]
and in case of binary matrices -P = P.
Therefore
G = [1 0 0 1 1 1;
0 1 0 0 1 0;
0 0 1 1 1 0]
I know how to solve it on paper by performing basic row operations but haven't figured out how to solve it using MATLAB yet.
What is the method for solving the given problem?
If the order of columns in -P' doesn't matter, here's one solution using the function ISMEMBER:
>> H = [1 1 1 1 0 0; 0 0 1 1 0 1; 1 0 0 1 1 0]; %# From above
>> pColumns = ~ismember(H',eye(3),'rows') %'# Find indices of columns that
%# are not equal to rows
pColumns = %# of the identity matrix
1
0
1
1
0
0
>> P = -H(:,pColumns)' %'# Find P
P =
-1 0 -1
-1 -1 0
-1 -1 -1
>> G = logical([eye(3) P]) %# Create the binary matrix G
G =
1 0 0 1 0 1
0 1 0 1 1 0
0 0 1 1 1 1
NOTE: This solution will work properly for integer or binary values in H. If H has floating-point values, you will likely run into an issue with floating-point comparisons when using ISMEMBER (see here and here for more discussion of this issue).