Moving the places of the 1's - matlab

I have 2 matrices. Matrix A is already defined.
Matrix A = [10 7 8 4 1 6;
2 6 4 3 5 1;
7 3 2 2 8 7;
6 2 3 10 11 4;
1 5 1 2 4 5]
Matrix B = [1 1 1 0 0 0;
0 0 0 0 0 0;
0 0 0 0 0 1;
0 0 0 1 1 0;
0 0 0 0 0 0]
Here each row has to have at least one '1'. This is our main target, so if there is a row with no 1's in it, we will have to move a '1' from another row.
How will we move the 1?
1) Check which rows that are all zeros. In this example, row 2 and 5.
2) We will subtract these 2 rows with all other rows that contain 1's in Matrix A. Which means that row 2 and row 5 in matrix A will be subtracted by all the other rows.
3) After the subtraction, we will check the change between each 2 subtracted rows, in the places of the 1's.
For example:
subtracting row 2 (all zeros) from row 1 will give us this [8 1 4] and
subtracting row 5 (all zeros) from row 1 will give us this [9 2 7].
subtracting row 2 (all zeros) from row 3 will give us this [6] and
subtracting row 5 (all zeros) from row 3 will give us this [2]
subtracting row 2 (all zeros) from row 4 will give us this [7 6] and
subtracting row 5 (all zeros) from row 4 will give us this [8 7]..
4) In the places of the 1's we will check the change between the rows and see the minimum change. The column that satisfied the minimum change, we will put the 1 in its place and remove it from the old place.
For example here:
For row 2, we will see where was the minimum change. Here the minimum change for row 2 was 1, which is in row 1. So we will remove the 1 of this column, and move it to the same column in row 2.
[1 0 1 0 0 0;
0 1 0 0 0 0;
0 0 0 0 0 1;
0 0 0 1 1 0;
0 0 0 0 0 0]
subtracting row 5 (all zeros) from row 1 will give us this [8 4].
subtracting row 5 (all zeros) from row 3 will give us this [2]
subtracting row 5 (all zeros) from row 4 will give us this [8 7]..
The same for row 5, check the minimum change. Here it is 2 but it is the only one in the array which is in row 3. So for this case we want to add a condition, that we don't do the subtraction method unless there is two 1's or more in the row. so we will move to another minimum change which is here the 4 which is the change from row 1 so will remove the 1 in row 1 and put it in row 5
so here the output will be =
[1 0 0 0 0 0;
0 1 0 0 0 0;
0 0 0 0 0 1;
0 0 0 1 1 0;
0 0 1 0 0 0]
and now the condition is satisfied, each row has at least one 1.
This is what i wrote in the code
%search for zero-rows in matrix B
minim = max(A) % Set the minimum value as an initial solution
zeroRows = find(sum(B,2)==0);
nonZeroRows = find(sum(B,2)~=0);
x = [];
y = [];
for zi = zeroRows'
for nZi = nonZeroRows'
%gives row vector of A with elements, where corresponding B elements are 1
%this row nZi in B is not zero
nonZeroRow = A(nZi,B(nZi,:) ==1);
nonZeroFull = A(nZi,:)
%gives row vector of A with elements, where corresponding B elements are 1
%this row zi in B is zero
zeroRow =A(zi, B(nZi,:) == 1);
zeroFull = A(zi,:)
%calculate the distance
disp(strcat('row ',num2str(nZi), ' - row ', num2str(zi)))
change = abs(nonZeroRow - zeroRow)
changeFull = nonZeroFull - zeroFull
x = [x change]
y = [y;changeFull]
Minimumchange = min(x)
if(Minimumchange < minim)
minim = Minimumchange
intersection = intersect(y,minim)
for i = 1 : length(intersection)
[w Index_intersection] = find(y == intersection(i))
B(zi,Index_intersection) = 0
B(nZi,Index_intersection) = 1
end
end
end
end
This is the code so far but its not giving the right output
Ask me if the question is still not clear.!

Since I can't comment due to the lack of my reputation I'll try it this way...
this would be your steps 1,2 and 3:
A = [10 7 8 4 1 6;
2 6 4 3 5 1;
7 3 2 2 8 7;
6 2 3 10 11 4;
1 5 1 2 4 5];
B = [1 1 1 0 0 0;
0 0 0 0 0 0;
0 0 0 0 0 1;
0 0 0 1 1 0;
0 0 0 0 0 0];
%search for zero-rows in matrix B
zeroRows = find(sum(B,2)==0);
nonZeroRows = find(sum(B,2)~=0);
for nZi = nonZeroRows'
for zi = zeroRows'
%gives row vector of A with elements, where corresponding B elements are 1
%this row nZi in B is not zero
nonZeroRow = A(nZi,B(nZi,:) ==1);
%gives row vector of A with elements, where corresponding B elements are 1
%this row zi in B is zero
zeroRow =A(zi, B(nZi,:) == 1);
%calculate the distance
disp(strcat('row ',num2str(nZi), ' - row ', num2str(zi)))
abs(nonZeroRow - zeroRow)
end
end
However I don't understand what you mean in step 4. Could you formulated your step 4 shorter and more precisely? Or give some examples with intermediate steps?

Related

Transform a matrix to a stacked vector where all zeroes after the last non-zero value per row are removed

I have a matrix with some zero values I want to erase.
a=[ 1 2 3 0 0; 1 0 1 3 2; 0 1 2 5 0]
>>a =
1 2 3 0 0
1 0 1 3 2
0 1 2 5 0
However, I want to erase only the ones after the last non-zero value of each line.
This means that I want to retain 1 2 3 from the first line, 1 0 1 3 2 from the second and 0 1 2 5 from the third.
I want to then store the remaining values in a vector. In the case of the example this would result in the vector
b=[1 2 3 1 0 1 3 2 0 1 2 5]
The only way I figured out involves a for loop that I would like to avoid:
b=[];
for ii=1:size(a,1)
l=max(find(a(ii,:)));
b=[b a(ii,1:l)];
end
Is there a way to vectorize this code?
There are many possible ways to do this, here is my approach:
arotate = a' %//rotate the matrix a by 90 degrees
b=flipud(arotate) %//flips the matrix up and down
c= flipud(cumsum(b,1)) %//cumulative sum the matrix rows -and then flip it back.
arotate(c==0)=[]
arotate =
1 2 3 1 0 1 3 2 0 1 2 5
=========================EDIT=====================
just realized cumsum can have direction parameter so this should do:
arotate = a'
b = cumsum(arotate,1,'reverse')
arotate(b==0)=[]
This direction parameter was not available on my 2010b version, but should be there for you if you are using 2013a or above.
Here's an approach using bsxfun's masking capability -
M = size(a,2); %// Save size parameter
at = a.'; %// Transpose input array, to be used for masked extraction
%// Index IDs of last non-zero for each row when looking from right side
[~,idx] = max(fliplr(a~=0),[],2);
%// Create a mask of elements that are to be picked up in a
%// transposed version of the input array using BSXFUN's broadcasting
out = at(bsxfun(#le,(1:M)',M+1-idx'))
Sample run (to showcase mask usage) -
>> a
a =
1 2 3 0 0
1 0 1 3 2
0 1 2 5 0
>> M = size(a,2);
>> at = a.';
>> [~,idx] = max(fliplr(a~=0),[],2);
>> bsxfun(#le,(1:M)',M+1-idx') %// mask to be used on transposed version
ans =
1 1 1
1 1 1
1 1 1
0 1 1
0 1 0
>> at(bsxfun(#le,(1:M)',M+1-idx')).'
ans =
1 2 3 1 0 1 3 2 0 1 2 5

Finding a critical point in matrix

I'm attempting to find a critical point in a matrix. The value at index (i,j) should be greater than or equal to all elements in its row, and less than or equal to all elements in its column.
Here is what I have (it's off but I'm close):
function C = critical(A)
[nrow ncol] = size(A);
C = [];
for i = 1:nrow
for j = 1:ncol
if (A(i,j) >= A(i,1:end)) && (A(i,j) <= A(1:end,j))
C = [C ; A(i,j)]
end
end
end
You can use logical indexing.
minI = min(A,[],1);
maxI = max(A,[],2);
[row,col] = find(((A.'==maxI.').' & A==minI) ==1)
Details
Remember that Matlab is column major. We therefore transpose A and maxI.
A = [
3 4 1 1 2
2 4 2 1 4
4 3 2 1 2
3 3 1 1 1
2 3 0 2 1];
A.'==maxI.'
ans =
0 0 1 1 0
1 1 0 1 1
0 0 0 0 0
0 0 0 0 0
0 1 0 0 0
Then do the minimum
A==minI
ans =
0 0 0 1 0
1 0 0 1 0
0 1 0 1 0
0 1 0 1 1
1 1 1 0 1
And then multiply the two
((A.'==maxI.').' & A==minI)
ans =
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 1 0 0 0
0 1 0 0 0
Then find the rows and cols
[row,col] = find(((A.'==maxI.').' & A==minI) ==1)
row =
4
5
col =
2
2
Try this vectorised solution using bsxfun
function [ r,c,criP ] = critical( A )
%// finding the min and max values of each col & row resptly
minI = min(A,[],1);
maxI = max(A,[],2);
%// matching all the values of min & max for each col and row resptly
%// getting the indexes of the elements satisfying both the conditions
idx = find(bsxfun(#eq,A,maxI) & bsxfun(#eq,A,minI));
%// getting the corresponding values from the indexes
criP = A(idx);
%// Also getting corresponding row and col sub
[r,c] = ind2sub(size(A),idx);
end
Sample Run:
r,c should be a vector of equal length which represents the row and column subs of each Critical point. While val is a vector of same length giving the value of the critical point itself
>> A
A =
3 4 1 1 2
2 4 2 1 4
4 3 2 1 2
3 3 1 1 1
2 3 0 2 1
>> [r,c,val] = critical(A)
r =
4
5
c =
2
2
val =
3
3
I think there is a simpler way with intersect:
>> [~, row, col] = intersect(max(A,[],2), min(A));
row =
4
col =
2
UPDATE:
With intersect, in case you have multiple critical points, it will only give you the first one. To have all the indicies, there is also another simple way:
>> B
B =
3 4 1 4 2 5
2 5 2 4 4 4
4 4 2 4 2 4
3 4 1 4 1 4
2 5 4 4 4 5
>> row = find(ismember(max(B,[],2),min(B)))
row =
3
4
>> col = find(ismember(min(B),max(B,[],2)))
col =
2 4 6
Note that the set of critical points now should be the combination of row and col, means you have total 6 critical points in this example: (3,2),(4,2),(3,4),(4,4),(3,6),(4,6).
Here you can find how to export such combination.

Find position of vector in matrix [duplicate]

This question already has answers here:
General method to find submatrix in matlab matrix
(3 answers)
Closed 8 years ago.
I have the vector:
1 2 3
and the matrix:
4 1 2 3 5 5
9 8 7 6 3 1
1 4 7 8 2 3
I am trying to find a simple way of locating the vector [1 2 3] in my matrix.
A function returning either coordinates (Ie: (1,2:4)) or a matrix of 1s where there is a match a 0s where there isn't, Ie:
0 1 1 1 0 0
0 0 0 0 0 0
0 0 0 0 0 0
So far, the only function I've found is is 'ismember', which however only tells me if the individual components of the vector appear in the matrix. Suggestions?
Use strfind with a linearized version of the matrix, and then convert linear indices to subindices. Care should be taken to remove matches of the vector spanning different rows.
mat = [1 2 3 1 2 3 1 2;
3 0 1 2 3 5 4 4]; %// data
vec = [1 2 3]; %// data
ind = strfind(reshape(mat.',[],1).', vec);
[col row] = ind2sub(fliplr(size(mat)), ind);
keep = col<=size(mat,2)-length(vec)+1; %// remove result split across rows
row = row(keep);
col = col(keep);
Result for this example:
>> row, col
row =
1 1 2
col =
1 4 3
meaning the vector appears three times: row 1, col 1; row 1, col 4; row 2, col 3.
The result can be expressed in zero-one form as follows:
result = zeros(fliplr(size(mat)));
ind_ones = bsxfun(#plus, ind(keep).', 0:numel(vec)-1);
result(ind_ones) = 1;
result = result.';
which gives
>> result
result =
1 1 1 1 1 1 0 0
0 0 1 1 1 0 0 0
One way to get the starting location of the vector in the matrix is using colfilt:
>> A = [1 2 3 1 2 3 1 2; ...
3 0 1 2 3 5 4 4]; % matrix from Luis Mendo
>> T = [1 2 3];
>> colFun = #(x,t) all(x==repmat(t,1,size(x,2)),1);
>> B = colfilt(A,size(T),'sliding',colFun,T(:))
B =
0 1 0 0 1 0 0 0
0 0 0 1 0 0 0 0
That gives a mask of the center points, which translate to (row,col) coordinates:
>> [ii,jj]=find(B);
>> locs = bsxfun(#minus,[ii jj],floor((size(T)-1)/2))
locs =
1 1
2 3
1 4

Midpoints of matrix rows depending on certain conditions Matlab

I have a matrix A with size 10x100 as shown below. What I want to do is:
I'll work row by row in which for each row I'll check the data of
each coloumn in this row
Let's say I'm now in the first col cell in the first row. I'll check if the value is zero I'll move to the next col, and so on till I found a col having a non-zero value and save its col number e.g. col 3 "this means that col 1&2 were zeros"
Now I'm in the first non zero col in row1, I'll move to the next col till I find a col with zero value. I'll fetch the col just before this zero one which must be a non-zero one and save it. e.g col 7 "this means that col4&5&6 are non-zeros and col8 is zero"
Now I want to save the median middle col between this two columns e.g col3 and col7 then the middle col is col5 so I'll save the index row1_col5. if there are two middle values then any of them is fine.
I'll then move to the next col till I find a non-zero col "do the
same steps from 2-->5" till the first row is finished.
Move to the next row and start over again from step 2-->5.
There are two rules: -The first one is that I'll get the middle index of non-zero consecutive values only if there is a minimum of 3 non-zero consecutive values, if there are two non-zero consecutive value then the middle will not be calculated -The second one is that if the number of zero consecutive values are less than 3 then they will be ignored and will be considered as non-zero values. e.g in the below example the first row middle values are col5 and col11. In row2 col5 is counted, while no cols in row3 satisfy this conditions , and in row4 col6 or col7 will be counted.
After finishing all the rows want to have a vector or array holding the positions of all the middle indexes e.g row1_col5 row1_col17 row2_col_10 and so on.
example:
A = [ 0 0 0 2 4 1 0 0 0 1 3 2;
0 0 0 5 1 1 1 1 0 0 0 1;
0 3 4 1 0 3 1 2 0 0 1 3;
0 0 0 0 1 3 4 5 0 0 0 0];
for the first row the middle value will be 5 and 11 and so on
So if anyone could please advise how can I do this with least processing as this can be done using loops but if there is more efficient way of doing it? Please let me know if any clarification is needed.
Now you have clarified your question (again...) here is a solution (still using a for loop...). It includes "rule 7" - excluding runs of fewer than three elements; it also includes the second part of that rule - runs of fewer than three zeros don't count as zero. The new code looks like this:
A = [ 0 0 0 2 4 1 0 0 0 1 3 2;
0 0 0 5 1 1 1 1 0 0 0 1;
0 3 4 1 0 3 1 2 0 0 1 3;
0 0 0 0 1 3 4 5 0 0 0 0];
retVal = cell(1, size(A, 1));
for ri = 1:size(A,1)
temp = [1 0 0 0 A(ri,:) 0 0 0 1]; % pad ends with 3 zeros + 1
% so that is always a "good run"
isz = (temp == 0); % find zeros - pad "short runs of 0" with ones
diffIsZ = diff(isz);
f = find(diffIsZ == 1);
l = find(diffIsZ == -1);
shortRun = find((l-f)<3); % these are the zeros that need eliminating
for ii = 1:numel(shortRun)
temp(f(shortRun(ii))+1:l(shortRun(ii))) = 1;
end
% now take the modified row:
nz = (temp(4:end-3)~=0);
dnz = diff(nz); % find first and last nonzero elements
f = find(dnz==1);
l = find(dnz==-1);
middleValue = floor((f + l)/2);
rule7 = find((l - f) > 2);
retVal{ri} = middleValue(rule7);
end
You have to use a cell array for the return value since you don't know how many elements will be returned per row (per your updated requirement).
The code above returns the following cell array:
{[5 11], [6], [7], [7]}
I appear still not to understand your "rule 7", because you say that "no columns in row 3 satisfy this condition". But it seems to me that once we eliminate the short runs of zeros, it does. Unless I misinterpret how you want to treat a run of non-zero numbers that goes right to the edge (I assume that's OK - which is why you return 11 as a valid column in row 1; so why wouldn't you return 7 for row 3??)
Try this:
sizeA = size(A);
N = sizeA(1);
D = diff([zeros(1, N); (A.' ~= 0); zeros(1,N)]) ~= 0;
[a b] = find(D ~= 0);
c = reshape(a, 2, []);
midRow = floor(sum(c)/2);
midCol = b(1:2:length(b))
After this, midRow and midCol contain the indices of your centroids (e.g. midRow(1) = 1, midCol(1) = 4 for the example matrix you gave above.
If you don't mind using a for loop:
A = [ 0 0 1 1 1 0 1;
0 0 0 0 0 0 0;
0 1 1 1 1 0 0;
0 1 1 1 0 1 1;
0 0 0 0 1 0 0]; % data
sol = repmat(NaN,size(A,1),1);
for row = 1:size(A,1)
[aux_row aux_col aux_val] = find(A(row,:));
if ~isempty(aux_col)
sol(row) = aux_col(1) + floor((find(diff([aux_col 0])~=1,1)-1)/2);
% the final 0 is necessary in case the row of A ends with ones
% you can use either "floor" or "ceil"
end
end
disp(sol)
Try it and see if it does what you want. I hope the code is clear; if not, tell me

Matlab: Search rows in matrix with fixed first and last element with vectorization

I have a matrix like the following (arbitrary cols/rows):
1 0 0 0 0
1 2 0 0 0
1 2 3 0 0
1 2 3 4 0
1 2 3 4 5
1 2 5 0 0
1 2 5 3 0
1 2 5 3 4
1 4 0 0 0
1 4 2 0 0
1 4 2 3 0
1 4 2 5 0
1 4 2 5 3
1 4 5 0 0
1 4 5 3 0
2 0 0 0 0
2 3 0 0 0
2 3 4 0 0
2 3 4 5 0
2 5 0 0 0
2 5 3 0 0
2 5 3 4 0
3 0 0 0 0
3 4 0 0 0
3 4 2 0 0
3 4 2 5 0
3 4 5 0 0
and now I want to get all rows where the first element is a certain value X and the last element (that is the last element != 0) is a certain value Y, OR turned around: the first is Y and the last is X.
Can't see any speedful code which does NOT use a for-loop :(
Thanks!
EDIT: To filter all rows with a certain first element is really easy, you don't need to help me here. So let's assume I only want to do the following: Filter all rows where the last element (i.e. the last element != 0 in each row) is either X or Y.
EDIT
Thanks a lot for your posts. I benchmarked the three possible solutions with a matrix of 473408*10 elements. Here's the benchmarkscript:
http://pastebin.com/9hEAWw9a
The results were:
t1 = 2.9425 Jonas
t2 = 0.0999 Brendan
t3 = 0.0951 Oli
So thanks a lot you guys, I'm sticking with Oli's solution and thus accept it. Thanks though for all the other solutions!
All you need to do is to find the linear indices of the last non-zero element of every row. The rest is easy:
[nRows,nCols] = size(A);
[u,v] = find(A); %# find all non-zero elements in A
%# for each row, find the highest column index with accumarray
%# and convert to linear index with sub2ind
lastIdx = sub2ind([nRows,nCols],(1:nRows)',accumarray(u,v,[nRows,1],#max,NaN));
To filter rows, you can then write
goodRows = A(:,1) == X & A(lastIdx) == Y
Here is a trick: Look for numbers with a 0 on the right, and sum them all:
H=[1 2 0 0 0;
2 3 1 0 0;
4 5 8 0 0;
8 5 4 2 2];
lastNumber=sum(H.*[H(:,2:end)==0 true(size(H,1),1)],2)
ans =
2
1
8
2
The rest is easy:
firstNumber=H(:,1);
find( (firstNumber==f) & (lastNumber==l) )
This works only if the numbers in each row are x number of non-zeros followed by a series of zeros. i.e. it will not work if the following is possible 1 0 3 4 0 0, I assume that isn't possible based on the sample input you gave ...
% 'a' is your array
[nx, ny] = size(a);
inds = [0:ny:ny*(nx-1)]' + sum(a ~= 0, 2);
% Needs to transpose so that the indexing reads left-to-right
aT = a';
valid1 = aT(inds) == Y;
valid2 = a(:,1) == X;
valid = valid1 & valid2;
valid_rows = a(valid,:);
This is messy I know ...