Matrix row/column manipulation in matlab - matlab

I have got the following function for spreading out the number of 1's in a matrix and if there are rows with all 0's or all 1's then that particular row has to be deleted
function ReducedMatrix = ReduceMatrix(result)
D1 = sum(result(:));
NumberOfOnes = floor(D1*0.3);
NewMatrix = zeros(size(result));
NewMatrix(randi(numel(NewMatrix),1,NumberOfOnes)) = 1;
ReducedMatrix = NewMatrix;
while numel(ReducedMatrix)/numel(NewMatrix) > 0.2
IndexOfFullRows = find(all(ReducedMatrix));
if isempty(IndexOfFullRows)
break
end
ReducedMatrix(:,IndexOfFullRows(1)) = [];
end
end
The input of the function and output are as follows
result =
0 1 1 1 1 1 1 1 1 1
1 1 1 1 1 0 1 0 1 1
1 1 0 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 0
1 1 1 1 1 1 0 1 0 1
1 0 1 1 1 1 1 0 1 1
1 1 1 1 0 1 1 1 0 1
1 0 1 1 1 0 1 1 1 1
1 1 1 1 0 1 0 1 1 1
1 1 1 0 1 1 1 1 1 1
ReducedMatrix =
0 1 1 0 0 0 0 0 1 0
0 1 0 0 0 0 0 1 0 0
1 1 1 0 0 0 0 0 0 0
0 0 0 1 0 0 1 0 0 0
0 0 0 0 0 0 1 0 0 0
0 1 0 0 0 0 1 0 1 1
1 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 1
0 0 0 0 0 0 0 0 0 0
1 0 1 0 0 0 0 0 0 1
row_sum =
3
2
3
2
1
4
2
2
0
3
col_sum =
3 4 4 1 0 0 3 2 2 3
Now if there exists a row or column with the row_sum/col_sum equal to either 0 or 1 then then the corresponding row has to be deleted.
For Example. Row-R4,R9 and Col-C4,C5,C6 have row_sum and col_sum as either 1,0. So adding them up R4,R9,C4,C5,C6 = 5 rows have to be eliminated from the matrix so my reduced matrix should be of the size 5x5. Please note column should not be eliminated and instead of removing columns having 0 and 1, the corresponding rows can be removed. Similarly this function has to run for larger matrices with the same constraints. I tried doing the above function however i do not possess enough skills to achieve my desired results, Any help is much appreciated

I see a number of potential problems and possible simplifications to your code.
For one thing, the way you construct the original matrix, NewMatrix(randi(numel(NewMatrix),1,NumberOfOnes)) = 1; may not behave the way you would expect. randi does not guarantee that the same index will not appear multiple times in the output, so your new matrix may have fewer ones than the original. To solve this, shuffle the elements using randperm:
ReducedMatrix = [ones(1, NumberOfOnes), zeros(1, numel(result) - NumberOfOnes)];
ReducedMatrix = ReducedMatrix(randperm(numel(ReducedMatrix)));
ReducedMatrix = reshape(ReducedMatrix, size(result));
Secondly, you do not need to construct the new matrix as NewMatrix and then reassign it with ReducedMatrix = NewMatrix;. Just do ReducedMatrix = zeros(size(result)); and skip the reassignment. For the while loop condition, where NewMatrix appears to be "used", remember that numel(NewMatrix) == numel(result).
If you are not removing homogeneous columns, only rows, you do not need a loop to do the removal:
rowSum = sum(ReducedMatrix, 2);
rowMask = (rowSum == size(ReducedMatrix, 2) | rowSum == 0);
ReducedMatrix(rowMask, :) = [];
Your original code seems to swap the row and column indices when removing the rows. It also did not handle the case of all zeros. If you want to remove not more than 30% of rows, you can do something like this before the removal:
rowMask = find(rowMask); % Convert to indices
rowMask = rowMask(1:min(numel(rowMask), round(0.3 * size(ReducedMatrix, 2))));

Related

permutation/combination with specific condition

Let us we have binary number to fill out 9 spots with specific condition: 0 always comes before 1. the possible conditions is 10:
1 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1 1
0 0 1 1 1 1 1 1 1
0 0 0 1 1 1 1 1 1
0 0 0 0 1 1 1 1 1
0 0 0 0 0 1 1 1 1
0 0 0 0 0 0 1 1 1
0 0 0 0 0 0 0 1 1
0 0 0 0 0 0 0 0 1
0 0 0 0 0 0 0 0 0
Now lest us extent it to 0, 1, 2 with same rule. 0 should be always before 1 and/or 2. 1 should be before 1. Again, 9 spots are available to fill out.
I know that this yields to 55 combinations.
Question:
(1) what is the mathematical formulation to generalize this?
(2) How can I store all those 55 combinations? [any matlab code?]
Thanks
As the commenter said, the answer comes down to stars and bars. You can also think of this as counting the number of non-decreasing sequences i_1 <= i_2 <= ... <= i_k, where k is the number of symbols available and each i_j is a number between 0 and 9.
That said, here's a matlab script that generates all possibilities. Each row of the output matrix is one possible string of digits.
function M = bin_combs(L,k)
% L: length
% k: number of symbols
if k == 1
M = zeros(1,L);
else
M = zeros(0,L);
N = bin_combs(L,k-1);
for i = 1:size(N,1)
row = N(i,:);
for j=find(row==k-2)
new_row = row;
new_row(j:end) = new_row(j:end) + 1;
M = [M;new_row];
end
M = [M;row];
end
end
Some sample output:
>> size(bin_combs(9,3))
ans =
55 9
>> size(bin_combs(9,4))
ans =
220 9

Matrix which is 1 when row and column are both odd or both even

I want to create a matrix which which has:
The value 1 if the row is odd and the column is odd
The value 1 if the row is even and the column is even
The value 0 Otherwise.
I want to get the same results as the code below, but in a one line (command window) expression:
N=8;
A = zeros(N);
for row = 1:1:length(A)
for column = 1:1:length(A)
if(mod(row,2) == 1 && mod(column,2) == 1)
A(row,column*(mod(column,2) == 1)) = 1;
elseif(mod(row,2)== 0 && mod(column,2) == 0 )
A(row,column*(mod(column,2) == 0)) = 1;
end
end
end
disp(A)
This is the expected result:
1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1
A simple approach is to use implicit expansion of addition, noting that
odd+odd = even+even = 0
So this is your answer:
A = 1 - mod( (1:N) + (1:N).', 2 );
You could also do this with toeplitz, as shown in this MATLAB Answers Post
For a square matrix with number of rows = number of columns = N
A = toeplitz(mod(1:N,2));
If the number of rows (M) is not equal to the number of columns (N) then
A = toeplitz(mod(1:M,2),mod(1:N,2))
FWIW, you're asking a specific case of this question:
How to generate a customized checker board matrix as fast as possible?
Can you take three lines?
N=8;
A = zeros(N);
A(1:2:end, 1:2:end) = 1;
A(2:2:end, 2:2:end) = 1;
One line solution (when N is even):
A = repmat([1, 0; 0 1], [N/2, N/2]);
You can try the function meshgrid to generate mesh grids and use mod to determine even or odd
[x,y] = meshgrid(1:N,1:N);
A = mod(x+y+1,2);
such that
>> A
A =
1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1

Iterating through a matrix using a smaller matrix

I've been struggling with this for a bit now. I have a small matrix s for example and a bigger matrix B as shown below.
B =
0 0 0 0 0 0 1 1
1 1 0 0 1 0 1 1
1 1 0 1 0 0 1 1
1 1 1 0 0 0 1 0
0 0 1 1 1 0 0 1
0 0 0 1 1 1 1 1
1 1 1 0 0 0 1 0
0 1 1 0 1 1 0 0
s =
1 1
1 1
What I want to do is iterate through B with s and compare the values. If all the values in s equal the values in B (the small section of B), then the answer is 1, if not then 0.
The 1's and 0's would be placed in a matrix as well.
This is what I've done so far but unfortunately, it doesn't iterate step by step and doesn't create a matrix either.
s = ones(2,2)
B = randi([0 1],8,8)
f = zeros(size(B))
[M,N]=size(B); % the larger array
[m,n]=size(s); % and the smaller...
for i=1:M/m-(m-1)
for j=1:N/n-(n-1)
if all(s==B(i:i+m-1,j:j+n-1))
disp("1")
else
disp("0")
end
end
end
Any help would be appreciated!
The following code works on the examples you supplied, I haven't tested it on anything else, and it will not work if the dimensions of the smaller matrix are not factors of the dimensions of the larger matrix, but you didn't indicate that it needed to do that in your description.
B =[0 0 0 0 0 0 1 1
1 1 0 0 1 0 1 1
1 1 0 1 0 0 1 1
1 1 1 0 0 0 1 0
0 0 1 1 1 0 0 1
0 0 0 1 1 1 1 1
1 1 1 0 0 0 1 0
0 1 1 0 1 1 0 0];
S =[1 1
1 1];
%check if array meets size requirements
numRowB = size(B,1);
numRowS = size(S,1);
numColB = size(B,2);
numColS = size(S,2);
%get loop multiples
incRows = numRowB/numRowS;
incCols = numColB/numColS;
%create output array
result = zeros(incRows, incCols);
%create rows and colums indices
rowsPull = 1:numRowS:numRowB;
colsPull = 1:numColS:numColB;
%iterate
for i= 1:incRows
for j= 1:incCols
result(i,j) = isequal(B(rowsPull(i):rowsPull(i)+numRowS-1, colsPull(j):colsPull(j)+numColS-1),S);
end
end
%print the resulting array
disp(result)

generating combinations in Matlab

I have a column vector x made up of 4 elements, how can i generate all the possible combinations of the values that x can take such that x*x' is less than or equal to a certain value?
note that the values of x are positive and integers.
To be more clear:
the input is the number of elements of the column vector x and the threshold, the output are the different possible combinations of the values of x respecting the fact that x*x' <=threshold
Example: threshold is 4 and x is a 4*1 column vector.....the output is x=[0 0 0 0].[0 0 0 1],[1 1 1 1]......
See if this works for you -
threshold = 4;
A = 0:threshold
A1 = allcomb(A,A,A,A)
%// Or use: A1 = combvec(A,A,A,A).' from Neural Network Toolbox
combs = A1(sum(A1.^2,2)<=threshold,:)
Please note that the code listed above uses allcomb from MATLAB File-exchange.
Output -
combs =
0 0 0 0
0 0 0 1
0 0 0 2
0 0 1 0
0 0 1 1
0 0 2 0
0 1 0 0
0 1 0 1
0 1 1 0
0 1 1 1
0 2 0 0
1 0 0 0
1 0 0 1
1 0 1 0
1 0 1 1
1 1 0 0
1 1 0 1
1 1 1 0
1 1 1 1
2 0 0 0

Assign values w/ multiple conditions

Let's have a M = [10 x 4 x 12] matrix. As example I take the M(:,:,4):
val(:,:,4) =
0 0 1 0
0 1 1 1
0 0 0 1
1 1 1 1
1 1 0 1
0 1 1 1
1 1 1 1
1 1 1 1
0 0 1 1
0 0 1 1
How can I obtain this:
val(:,:,4) =
0 0 3 0
0 2 2 2
0 0 0 4
1 1 1 1
1 1 0 1
0 2 2 2
1 1 1 1
1 1 1 1
0 0 3 3
0 0 3 3
If I have 1 in the first column then all the subsequent 1's should be 1.
If I have 0 in the first column but 1 in the second, all the subsequent 1's should be 2.
If I have 0 in the first and second column but 1 in the third then all the subsequent 1's should be 3.
If I have 0 in the first 3 columns but 1 in the forth then this one should be four.
Note: The logical matrix M is constructed:
Tab = [reshape(Avg_1step.',10,1,[]) reshape(Avg_2step.',10,1,[]) ...
reshape(Avg_4step.',10,1,[]) reshape(Avg_6step.',10,1,[])];
M = Tab>=repmat([20 40 60 80],10,1,size(Tab,3));
This is a very simple approach that works for both 2D and 3D matrices.
%// Find the column index of the first element in each "slice".
[~, idx] = max(val,[],2);
%// Multiply the column index with each row of the initial matrix
bsxfun(#times, val, idx);
This could be one approach -
%// Concatenate input array along dim3 to create a 2D array for easy work ahead
M2d = reshape(permute(M,[1 3 2]),size(M,1)*size(M,3),[]);
%// Find matches for each case, index into each matching row and
%// elementwise multiply all elements with the corresponding multiplying
%// factor of 2 or 3 or 4 and thus obtain the desired output but as 2D array
%// NOTE: Case 1 would not change any value, so it was skipped.
case2m = all(bsxfun(#eq,M2d(:,1:2),[0 1]),2);
M2d(case2m,:) = bsxfun(#times,M2d(case2m,:),2);
case3m = all(bsxfun(#eq,M2d(:,1:3),[0 0 1]),2);
M2d(case3m,:) = bsxfun(#times,M2d(case3m,:),3);
case4m = all(bsxfun(#eq,M2d(:,1:4),[0 0 0 1]),2);
M2d(case4m,:) = bsxfun(#times,M2d(case4m,:),4);
%// Cut the 2D array thus obtained at every size(a,1) to give us back a 3D
%// array version of the expected values
Mout = permute(reshape(M2d,size(M,1),size(M,3),[]),[1 3 2])
Code run with a random 6 x 4 x 2 sized input array -
M(:,:,1) =
1 1 0 1
1 0 1 1
1 0 0 1
0 0 1 1
1 0 0 0
1 0 1 1
M(:,:,2) =
0 1 0 1
1 1 0 0
1 1 0 0
0 0 1 1
0 0 0 1
0 0 1 0
Mout(:,:,1) =
1 1 0 1
1 0 1 1
1 0 0 1
0 0 3 3
1 0 0 0
1 0 1 1
Mout(:,:,2) =
0 2 0 2
1 1 0 0
1 1 0 0
0 0 3 3
0 0 0 4
0 0 3 0