I need to calculate the following in matlab.
EDIT EDIT: I alway have a 16 x 3 matrix. 16 rows and 3 columns.
The 3 columns represent R,G,B and the 16 rows represent points. From 1-16.
An example matrix looks like this:
1 1 1
-1 0 0
0 0 1
1 0 0
-1 0 0
1 0 -1
1 1 1
1 1 1
0 0 0
-1 0 1
1 0 0
0 0 1
1 0 1
0 0 0
0 0 0
1 0 1
Now I need to know are there 11 coherently rows which have min. 1 value ~= 0 in each column?
In the above example the first 8 rows and the last row have in each column min 1 value and are coherently. So this 9 rows are the max coherently rows without a complete zero row between.
Sry that my first post wasn't correct.
I've do that with a really poor for-solution. Is there a faster way (vectorized) to do that?
for i=1:16
for j=0:16
if i+j > 16
value = (i+j)-16;
else
value = i+j;
end
if table(value,1) ~= 0 || table(value,2) ~= 0 || table(value,3) ~= 0
equal = equal + 1;
if equal >= 11
copy(y,x) = 1;
equal = 0;
break;
end
else
equal = 0;
end
end
end
end
And the 16 points are circular. This min the first point and the last point connect.
Thanks for help and sry for the confusing.
This counts the number of coherent rows with at least one none-zero entry without circularity:
B = ~(A==0);
idx = find(sum(B,2) == 0);
result = max([idx;size(A,1)+1] - [0;idx]) - 1;
Now you can check whether result is bigger than 11.
Another way would be:
B = ~(A==0);
C = bwconncomp(sum(B,2)>0);
num = cellfun(#numel,C.PixelIdxList);
result = max(num);
EDIT 2: To account for circularity, i.e. rows at the beginning and the end should be counted as coherent, you could do
B = ~(A==0);
idx = find(sum(B,2) == 0);
result = max([idx;size(A,1)+idx;size(A,1)+1] - [0;idx;size(A,1)+idx]) - 1;
EDIT: I edited the result-line in the first solution according to Knedlsepp's comments.
This is a somewhat clunky solution, but it should give a solution at least if the matrix isn't too big. If you call your matrix m try the following line of code:
log2(max([cumprod(2*logical(m),2),ones(size(m,1),1)],[],2))
I hope this helps!
EDIT:
Now that it is clear what is ment by the question, here's an answer that should work:
find(~[m(:,1)|m(:,2)|m(:,3);0],1)-1 >= 11
Related
I have a loop that iterates over a matrix and sets all rows and columns with only one non-zero element to all zeroes.
so for example, it will transform this matrix:
A = [ 1 0 1 1
0 0 1 0
1 1 1 1
1 0 1 1 ]
to the matrix:
A' = [ 1 0 1 1
0 0 0 0
1 0 1 1
1 0 1 1 ]
row/column 2 of A only has 1 non zero element in it, so every element in row/column 2 is set to 0 in A'
(it is assumed that the matrices will always be diagonally symmetrical)
here is my non-vectorised code:
for ii = 1:length(A)
if nnz(A(ii,:)) == 1
A(ii,:) = 0;
A(:,ii) = 0;
end
end
Is there a more efficient way of writing this code in MATLAB?
EDIT:
I have been asked in the comments for some clarification, so I will oblige.
The purpose of this code is to remove edges from a graph that lead to a vertex of degree 1.
if A is the adjacency matrix representing a undirected graph G, then a row or column of that matrix which only has one non-zero element indicates that row/column represents a vertex of degree one, as it only has one edge incident to it.
My objective is to remove such edges from the graph, as these vertices will never be visited in a solution to the problem I am trying to solve, and reducing the graph will also reduce the size of the input to my search algorithm.
#TimeString, i understand that in the example you gave, recursively applying the algorithm to your matrix will result in a zero matrix, however the matrices that I am applying it to represent large, connected graphs, so there will never be a case like that. In response to your question as to why I only check for how many elements in a row, but the clear both columns and rows; this is because the matrix is always diagonally symmetrical, so i know that if something is true for a row, so it will be for the corresponding column..
so, just to clarify using another example:
I want to turn this graph G:
represented by matrix:
A = [ 0 1 1 0
1 0 1 0
1 1 0 1
0 0 1 0 ]
to this graph G':
represented by this matrix:
A' = [ 0 1 1 0
1 0 1 0
1 1 0 0
0 0 0 0 ]
(i realise that this matrix should actually be a 3x3 matrix because point D has been removed, but i already know how to shrink the matrix in this instance, my question is about efficiently setting columns/rows with only 1 non-zero element all to 0)
i hope that is a good enough clarification..
Not sure if it's really faster (depends on Matlab's JIT) but you can try the following:
To find out which columns (equivalently, rows, since the matrix is symmetric) have more than one non zero element use:
sum(A ~= 0) > 1
The ~= 0 is probably not needed in your case since the matrix consists of 1/0 elements only (graph edges if I understand correctly).
Transform the above into a diagonal matrix in order to eliminate unwanted columns:
D = diag(sum(A~=0) > 1)
And multiply with A from left to zero rows and from right to zero columns:
res = D * A * D
Thanks to nimrodm's suggestion of using sum(A ~= 0) instead of nnz, i managed to find a better solution than my original one
to clear the rows with one element i use:
A(sum(A ~= 0) == 1,:) = 0;
and then to clear columns with one element:
A(:,sum(A ~= 0) == 1) = 0;
for those of you who are interested, i did a 'tic-toc' comparison on a 1000 x 1000 matrix:
% establish matrix
A = magic(1000);
rem_rows = [200,555,950];
A(rem_rows,:) = 0;
A(:,rem_rows) = 0;
% insert single element into empty rows/columns
A(rem_rows,500) = 5;
A(500,rem_rows) = 5;
% testing original version
A_temp = A;
for test = 1
tic
for ii = 1:length(A_temp)
if nnz(A_temp(ii,:)) == 1
A_temp(ii,:) = 0;
A_temp(:,ii) = 0;
end
end
toc
end
Elapsed time is 0.041104 seconds.
% testing new version
A_temp = A;
for test = 1
tic
A_temp(sum(A_temp ~= 0) == 1,:) = 0;
A_temp(:,sum(A_temp ~= 0) == 1) = 0;
toc
end
Elapsed time is 0.010378 seconds
% testing matrix operations based solution suggested by nimrodm
A_temp = A;
for test = 1
tic
B = diag(sum(A_temp ~= 0) > 1);
res = B * A_temp * B;
toc
end
Elapsed time is 0.258799 seconds
so it appears that the single line version that I came up with, inspired by nimrodm's suggestion, is the fastest
thanks for all your help!
Bsxfuning it -
A(bsxfun(#or,(sum(A~=0,2)==1),(sum(A~=0,1)==1))) = 0
Sample run -
>> A
A =
1 0 1 1
0 0 1 0
1 1 1 1
1 0 1 1
>> A(bsxfun(#or,(sum(A~=0,2)==1),(sum(A~=0,1)==1))) = 0
A =
1 0 1 1
0 0 0 0
1 0 1 1
1 0 1 1
I have a matrix filled with zeros and ones and I need to count the number of ones in each row. Then I need to know which row's count exceeds or equal a specific limit (any number, for example 3). After that foreach row in these rows I need to create a vector that holds the index of all the columns which has non-zero values in that row and in all the rows above it and below it till it reach a row with zero count.
Example:
data contains the data below:
0 0 0 0 0 0 0
0 0 0 1 1 0 0
0 1 0 0 1 0 1
0 0 0 0 0 0 0
0 1 0 0 0 0 0
0 1 1 1 0 0 0
0 0 1 0 0 0 0
0 0 0 0 0 0 0
The output should be if the limit is 3:
Row 3: col 4 5 2 5 7
Row 6: col 2 2 3 4 3
I already read the data and I counted the ones in the code below:
load('data');
mat(isnan(mat)) = 0;
[rows,cols,vals] = find(mat~= 0);
unqRows=unique(rows);
countElinRows=histc(rows,unqRows);
Edit for clarification as requested by commentators:
If the third row of the given sample input array becomes [0 1 0 0 0 0 1], then we must only have this output -
Row 6: col 2 2 3 4 3
Assuming A as the input array, see if this works for you -
[sc1,sr1] = find(A') %//'# row and col indices for sorted rows
s_a1 = sum(A,2) %// sum input array along cols
bounds = find(s_a1==0) %// find bounds/gropus delimited by all zero rows
bounds = unique([1 ; bounds ; size(A,1)]) %// account for non all zero
%// starting and ending rows
cumsum1 = cumsum(s_a1==0) + double(sum(A(1,:))~=0) %// label groups
valid_groups = accumarray(cumsum1, s_a1, [], #max)>=3 %// valid groups
out = arrayfun(#(k1) sc1(sr1>=bounds(k1) & sr1<=bounds(k1+1)),...
1:numel(bounds)-1,'un',0) %// find all indices within each group
out = out(valid_groups) %// select only the valid groups for the final output
Visualized output with celldisp(out).
Apologies for strange code, but it's the best I could come up with
[I1,~]=find(sum(mat,2)>=3)
[I2,~]=find(sum(mat,2)==0)
[~,CM]=find(diff(mod(sum(bsxfun(#le,I1,I2.')),2))~=0)
[I,J]=arrayfun(#(t)find(mat(I2(CM(t)):I2(CM(t)+1),:)>0),1:length(CM),'UniformOutput',false)
[~,w]=cellfun(#sort,I,'UniformOutput',false);
J=arrayfun(#(t) J{t}(w{t}).',1:length(J),'UniformOutput',false)
celldisp(J)
This code does feel pretty overcomplicated.
I have tested it on a few cases and it seems to be fine, but it's hard to know for certain.
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
This is a very specific question. I have an M*3 matrix. The first column contains M set of elements. It may follow this.
0
0
0
0
1
1
1
1
1
1
1
1
1
0
0
0
0
0
My interest is only 1s and corresponding other column values. I can remove zeros get a new set of matrix with only 1s, but sometimes it may follow this:
1
1
1
0
0
0
0
0
1
1
1
1
1
1
1
1
1
1
1
1
1
0
0
1
1
When the situation is like above I want to disregard 1s in the beginning and remove all the elements in M*3 matrix up to the first 1, then when it reaches second start of zeros in the column it can remove all the values to the end of the column. (so it will be 13*3 matrix).
I'm doing this in matlab.
Thank you :)
Let's call your matrix A:
firstCol = A(:, 1);
indices = find(firstCol);
check = find(diff(indices) ~= 1);
if (isempty(check) )
Afinal = A(indices, :);
else
indices2 = indices(check(1)+1:1:check(2));
Afinal = A(indices2, :);
end
Afinal should be the output you're looking for.
Say I have a vector containing only logical values, such as
V = [1 0 1 0 1 1 1 1 0 0]
I would like to write a function in MATLAB which returns a 'streak' vector S for V, where S(i) represents the number of consecutive 1s in V up to but not including V(i). For the example above, the streak vector would be
S = [0 1 0 1 0 1 2 3 4 0]
Given that I have to do this for a very large matrix, I would very much appreciate any solution that is vectorized / efficient.
This should do the trick:
S = zeros(size(V));
for i=2:length(V)
if(V(i-1)==1)
S(i) = 1 + S(i-1);
end
end
The complexity is only O(n), which I guess should be good enough.
For your sample input:
V = [1 0 1 0 1 1 1 1 0 0];
S = zeros(size(V));
for i=2:length(V)
if(V(i-1)==1)
S(i) = 1 + S(i-1);
end
end
display(V);
display(S);
The result would be:
V =
1 0 1 0 1 1 1 1 0 0
S =
0 1 0 1 0 1 2 3 4 0
You could also do it completely vectorized with a couple intermediate steps:
V = [1 0 1 0 1 1 1 1 0 0];
Sall = cumsum(V);
stopidx = find(diff(V)==-1)+1;
V2=V;
V2(stopidx) = -Sall(stopidx)+[0 Sall(stopidx(1:end-1))];
S2 = cumsum(V2);
S = [0 S2(1:end-1)];
Afaik the only thing that can take a while is the find call; you can't use logical indexing everywhere and bypass the find call, because you need the absolute indices.
It's outside the box - but have you considered using text functions? Since strings are just vectors for Matlab it should be easy to use them.
Regexp contains some nice functions for finding repeated values.