Assign values w/ multiple conditions - matlab

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

Related

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)

Matrix row/column manipulation in 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))));

Linear span of a vector in MATLAB

I'm looking for a way to generate the spans of a given vector in MATLAB.
For example:
if a = [ 0 1 0 1] I need all vectors of the form [0 x 0 y], 1 <= x <= max1, 1 <= y <= max2,.
or if
a = [ 0 1 0 1 1 0] I need all vectors of the form [0 x 0 y z 0], 1 <= x <= max1, 1 <= y <= max2, 1<= z <= max3.
Note that the vector can have a variable number of 1's.
My first impression is that I would need a variable number of for loops, though I don't know if that is doable in MATLAB. Also any other ideas are welcome!
You don't need multiple for loops for this. The code below generates all required vectors as rows of a tall matrix. It actually creates the columns of the matrix one at a time. Each column will have numbers 1:m(i) arranged in the pattern where
each term repeats the number of times equal to the product of all m-numbers after m(i)
the whole pattern repeats the number of times equal to the product of all m-numbers before m(i)
This is what repmat(kron(1:m(i),ones(1,after)),1,before)' does. (Starting with R2015a you can use repelem to simplify this by replacing the kron command, but I don't have that release yet.)
a = [0 1 0 1 1 0];
m = [2 4 3]; // the numbers max1, max2, max3
A = zeros(prod(m), length(a));
i = 1; // runs through elements of m
for j=1:length(a) // runs through elements of a
if (a(j)>0)
before = prod(m(1:i-1));
after = prod(m(i+1:end));
A(:,j) = repmat(kron(1:m(i),ones(1,after)),1,before)';
i = i+1;
end
end
Output:
0 1 0 1 1 0
0 1 0 1 2 0
0 1 0 1 3 0
0 1 0 2 1 0
0 1 0 2 2 0
0 1 0 2 3 0
0 1 0 3 1 0
0 1 0 3 2 0
0 1 0 3 3 0
0 1 0 4 1 0
0 1 0 4 2 0
0 1 0 4 3 0
0 2 0 1 1 0
0 2 0 1 2 0
0 2 0 1 3 0
0 2 0 2 1 0
0 2 0 2 2 0
0 2 0 2 3 0
0 2 0 3 1 0
0 2 0 3 2 0
0 2 0 3 3 0
0 2 0 4 1 0
0 2 0 4 2 0
0 2 0 4 3 0

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

count node degree in matlab from a adjacency matrix

this is my matrix that displays a sample network graph
matrix =
0 1 1 1
1 0 1 0
0 0 0 1
1 1 1 0
where its a 4x4 matrix
1) 2) 3) 4)
1) 0 1 1 1
2) 1 0 1 0
3) 0 0 0 1
4) 1 1 1 0
i want to count this 4x4 matrix like
row 1 counts how many 1s i have and adds column 1 number of 1's to it and returns 1)=5 as total 1's in row 1 and col 1 = 5
i want my output to be like
1=5
2=4
3=4
4=5
This must be it -
out = sum([matrix matrix'],2)
Example run -
matrix =
1 1 1 1
1 0 0 0
0 1 0 1
0 0 1 1
out =
6
3
4
5
The above code would count 1s twice when they appear on the diagonal, which if you don't want, use this -
out1 = sum([matrix matrix'],2) - diag(matrix)
Example run -
matrix =
1 1 1 1
1 0 0 0
0 1 0 1
0 0 1 1
out1 =
5
3
4
4
I agree with the answer of Divakar, but once your graph gets larger and larger, you might not want to transpose the entire matrix. I suggest doing the sum first and then transposing afterwards:
sum(matrix,1)'+sum(matrix,2)-diag(matrix);
matrix =
0 1 1 1
1 0 1 0
0 0 0 1
1 1 1 0
degree=sum(matrix,1)'+sum(matrix,2)-diag(matrix)
degree =
5
4
4
5