I've got logical array(zeros and ones) 1500x700
I want to find "1" in every column and when there are more than one "1" in column i should choose the middle one.
Is that possible to do it? I know how to find "1", but don't know how to extract the middle "1" if there's couple of "1" in one column.
The find function returns the indices of your ones.
>> example=[1,0,0,1,0,1,1];
>> indices=find(example)
indices =
1 4 6 7
>> indices(floor(numel(indices)/2))
ans =
4
Do this for each column and you have a solution.
You can
Get the row and column indices of ones with find;
Apply accumarray with a custom function to get the middle row index for each column.
x = [1 0 0 0 0; 0 0 1 0 0; 1 0 1 0 0; 1 0 0 1 0]; % example
[ii, jj] = find(x); % step 1
result = accumarray(jj, ii, [size(x,2) 1], #(x) x(ceil(end/2)), NaN); % step 2
Note that:
For an even number of ones this gives the first of the two middle indices. If you prefer the average of the two middle indices replace #(x) x(ceil(end/2)) by #median.
For a column without ones this gives NaN as result. If you prefer a different value, replace the input fifth argument of accumarray by that.
Example:
x =
1 0 0 0 0
0 0 1 0 0
1 0 1 0 0
1 0 0 1 0
result =
3
NaN
2
4
NaN
Related
I have an array A of 1s and 0s and want to see if the larger array of bits B contains those bits in that exact order?
Example: A= [0 1 1 0 0 0 0 1]
B= [0 1 0 0 1 1 0 0 0 0 1 0 1 0 1]
would be true as A is contained in B
Most solutions I have found only determine if a value IS contained in another matrix, this is no good here as it is already certain that both matrices will be 1s and 0s
Thanks
One (albeit unusual) option, since you're dealing with integer values, is to convert A and B to character arrays and use the contains function:
isWithin = contains(char(B), char(A));
There are some obtuse vectorized ways to to do this, but by far the easiest, and likely just as efficient, is to use a loop with a sliding window,
A = [0 1 1 0 0 0 0 1];
B = [0 1 0 0 1 1 0 0 0 0 1 0 1 0 1];
vec = 0:(numel(A)-1);
for idx = 1:(numel(B)-numel(A)-1)
if all(A==B(idx+vec))
fprintf('A is contained in B\n');
break; % exit the loop as soon as 1 match is found
end
end
Or if you want to know the location(s) in B (of potentially multiple matches) then,
A = [0 1 1 0 0 0 0 1];
B = [0 1 0 0 1 1 0 0 0 0 1 0 1 0 1];
C = false(1,numel(B)-numel(A)-1);
vec = 0:(numel(A)-1);
for idx = 1:numel(C)
C(idx) = all(A==B(idx+vec));
end
if any(C)
fprintf('A is contained in B\n');
end
In this case
>> C
C =
1×6 logical array
0 0 0 1 0 0
You can use the cross-correlation between two signals for this, as a measure of local similarity.
For achieving good results, you need to shift A and B so that you don't have the value 0 any more. Then compute the correlation between the two of them with conv (keeping in mind that the convolution is the cross-correlation with one signal flipped), and normalize with the energy of A so that you get a perfect match whenever you get the value 1:
conv(B-0.5, flip(A)-0.5, 'valid')/sum((A-0.5).^2)
In the normalization term, flipping is removed as it does not change the value.
It gives:
[0 -0.5 0.25 1 0 0 -0.25 0]
4th element is 1, so starting from index equal to 4 you get a perfect match.
I have an array (say of 1s and 0s) and I want to find the index, i, for the first location where 1 appears n times in a row.
For example,
x = [0 0 1 0 1 1 1 0 0 0] ;
i = 5, for n = 3, as this is the first time '1' appears three times in a row.
Note: I want to find where 1 appears n times in a row so
i = find(x,n,'first');
is incorrect as this would give me the index of the first n 1s.
It is essentially a string search? eg findstr but with a vector.
You can do it with convolution as follows:
x = [0 0 1 0 1 1 1 0 0 0];
N = 3;
result = find(conv(x, ones(1,N), 'valid')==N, 1)
How it works
Convolve x with a vector of N ones and find the first time the result equals N. Convolution is computed with the 'valid' flag to avoid edge effects and thus obtain the correct value for the index.
Another answer that I have is to generate a buffer matrix where each row of this matrix is a neighbourhood of overlapping n elements of the array. Once you create this, index into your array and find the first row that has all 1s:
x = [0 0 1 0 1 1 1 0 0 0]; %// Example data
n = 3; %// How many times we look for duplication
%// Solution
ind = bsxfun(#plus, (1:numel(x)-n+1).', 0:n-1); %'
out = find(all(x(ind),2), 1);
The first line is a bit tricky. We use bsxfun to generate a matrix of size m x n where m is the total number of overlapping neighbourhoods while n is the size of the window you are searching for. This generates a matrix where the first row is enumerated from 1 to n, the second row is enumerated from 2 to n+1, up until the very end which is from numel(x)-n+1 to numel(x). Given n = 3, we have:
>> ind
ind =
1 2 3
2 3 4
3 4 5
4 5 6
5 6 7
6 7 8
7 8 9
8 9 10
These are indices which we will use to index into our array x, and for your example it generates the following buffer matrix when we directly index into x:
>> x = [0 0 1 0 1 1 1 0 0 0];
>> x(ind)
ans =
0 0 1
0 1 0
1 0 1
0 1 1
1 1 1
1 1 0
1 0 0
0 0 0
Each row is an overlapping neighbourhood of n elements. We finally end by searching for the first row that gives us all 1s. This is done by using all and searching over every row independently with the 2 as the second parameter. all produces true if every element in a row is non-zero, or 1 in our case. We then combine with find to determine the first non-zero location that satisfies this constraint... and so:
>> out = find(all(x(ind), 2), 1)
out =
5
This tells us that the fifth location of x is where the beginning of this duplication occurs n times.
Based on Rayryeng's approach you can loop this as well. This will definitely be slower for short array sizes, but for very large array sizes this doesn't calculate every possibility, but stops as soon as the first match is found and thus will be faster. You could even use an if statement based on the initial array length to choose whether to use the bsxfun or the for loop. Note also that for loops are rather fast since the latest MATLAB engine update.
x = [0 0 1 0 1 1 1 0 0 0]; %// Example data
n = 3; %// How many times we look for duplication
for idx = 1:numel(x)-n
if all(x(idx:idx+n-1))
break
end
end
Additionally, this can be used to find the a first occurrences:
x = [0 0 1 0 1 1 1 0 0 0 0 0 1 0 1 1 1 0 0 0 0 0 1 0 1 1 1 0 0 0]; %// Example data
n = 3; %// How many times we look for duplication
a = 2; %// number of desired matches
collect(1,a)=0; %// initialise output
kk = 1; %// initialise counter
for idx = 1:numel(x)-n
if all(x(idx:idx+n-1))
collect(kk) = idx;
if kk == a
break
end
kk = kk+1;
end
end
Which does the same but shuts down after a matches have been found. Again, this approach is only useful if your array is large.
Seeing you commented whether you can find the last occurrence: yes. Same trick as before, just run the loop backwards:
for idx = numel(x)-n:-1:1
if all(x(idx:idx+n-1))
break
end
end
One possibility with looping:
i = 0;
n = 3;
for idx = n : length(x)
idx_true = 1;
for sub_idx = (idx - n + 1) : idx
idx_true = idx_true & (x(sub_idx));
end
if(idx_true)
i = idx - n + 1;
break
end
end
if (i == 0)
disp('No index found.')
else
disp(i)
end
I'm trying to remove the rows which has duplicates in sequence. I have only 2 possible values which are 0 and 1. I have nXm which n shows possible number of bits and m is not important for my question. My goal is to find an matrix which is nX(m-a). The rows a which has the property which includes duplicates in sequence. For example:
My matrix is :
A=[0 1 0 1 0 1;
0 0 0 1 1 1;
0 0 1 0 0 1;
0 1 0 0 1 0;
1 0 0 0 1 0]
I want to remove the rows has t duplicates in sequence for 0. In this question let's assume t is 3. So I want the matrix which:
B=[0 1 0 1 0 1;
0 0 1 0 0 1;
0 1 0 0 1 0]
2nd and 5th rows are removed.
I probably need to use diff.
So you want to remove rows of A that contain at least t zeros in sequence.
How about a single line?
B = A(~any(conv2(1,ones(1,t),2*A-1,'valid')==-t, 2),:);
How this works:
Transform A to bipolar form (2*A-1)
Convolve each row with a sequence of t ones (conv2(...))
Keep only rows for which the convolution does not contain -t (~any(...)). The presence of -t indicates a sequence of t zeros in the corresponding row of A.
To remove rows that contain at least t ones, just change -t to t:
B = A(~any(conv2(1,ones(1,t),2*A-1,'valid')==t, 2),:);
Here is a generalized approach which removes any rows which has given number of consecutive duplicates (not just zero. could be any number).
t = 3;
row_mask = ~any(all(~diff(reshape(im2col(A,[1 t],'sliding'),t,size(A,1),[]))),3);
out = A(row_mask,:)
Sample Run:
>> A
A =
0 1 0 1 0 1
0 0 1 5 5 5 %// consecutive 3 5's
0 0 1 0 0 1
0 1 0 0 1 0
1 1 1 0 0 1 %// consecutive 3 1's
>> out
out =
0 1 0 1 0 1
0 0 1 0 0 1
0 1 0 0 1 0
How about an approach using strings? This is certainly not as fast as Luis Mendo's method where you work directly with the numerical array, but it's thinking a bit outside of the box. The basis of this approach is that I consider each row of A to be a unique string, and I can search each string for occurrences of a string of 0s by regular expressions.
A=[0 1 0 1 0 1;
0 0 0 1 1 1;
0 0 1 0 0 1;
0 1 0 0 1 0;
1 0 0 0 1 0];
t = 3;
B = sprintfc('%s', char('0' + A));
ind = cellfun('isempty', regexp(B, repmat('0', [1 t])));
B(~ind) = [];
B = double(char(B) - '0');
We get:
B =
0 1 0 1 0 1
0 0 1 0 0 1
0 1 0 0 1 0
Explanation
Line 1: Convert each line of the matrix A into a string consisting of 0s and 1s. Each line becomes a cell in a cell array. This uses the undocumented function sprintfc to facilitate this cell array conversion.
Line 2: I use regular expressions to find any occurrences of a string of 0s that is t long. I first use repmat to create a search string that is full of 0s and is t long. After, I determine if each line in this cell array contains this sequence of characters (i.e. 000....). The function regexp helps us perform regular expressions and returns the locations of any matches for each cell in the cell array. Alternatively, you can use the function strfind for more recent versions of MATLAB to speed up the computation, but I chose regexp so that the solution is compatible with most MATLAB distributions out there.
Continuing on, the output of regexp/strfind is a cell array of elements where each cell reports the locations of where we found the particular string. If we have a match, there should be at least one location that is reported at the output, so I check to see if any matches are empty, meaning that these are the rows we don't want to remove. I want to turn this into a logical array for the purposes of removing rows from A, and so this is wrapped with a cellfun call to determine the cells that are empty. Therefore, this line returns a logical array where a 0 means that remove this row and a 1 means that we don't.
Line 3: I take the logical array from Line 2 and invert it because that's what we really want. We use this inverted array to index into the cell array and remove those strings.
Line 4: The output is still a cell array, so I convert it back into a character array, and finally back into a numerical array.
I have a Vector 1xm, ShortMemory (SM), and a Matrix nxm, Agenda (AG).
SM only have positive integers and zeros.
Each column of AG only has one element equal to 1 and all the other elements of the same column equal to 0.
My objective is changing the position of the number 1 from a randomly choosen column from AG. The problem is that only columns that have a corresponding 0 in SM can be changed.
Example:
SM = [1 0 2];
AG = [1 1 0 ; 0 0 1 ; 0 0 0];
Randomly Generated number here
RandomColumn = 2;
The possible outcomes would be
AG = [1 0 0 ; 0 1 1 ; 0 0 0]; or AG = [1 0 0; 0 0 1 ; 0 1 0]; or AG = [1 1 0 ; 0 0 1 ; 0 0 0];
The Line that gets the 1 is also random but that's easy to do
I could do it by just getting random numbers between 1 and m but m can be very big in my problem and the number of zeros can be very small too, so it could potentially take alot of time. I could also do it with a cycle but it's Matlab and this is embeded on double cycle already.
Thanks
edit: Added commentary to the code for clarity.
edit: Corrected an error on possible outcome
My solution is based on following assumptions:
Objective is changing the position of the number 1 from a randomly choosen column from AG.
Only columns that have a corresponding 0 in SM can be changed.
Solution:
% input
SM = [1 0 2]
AG = [1 1 0 ; 0 0 1 ; 0 0 0]
% generating random column according to assumptions 1 and 2
RandomColumn1 = 1:size(AG,2);
RandomColumn1(SM~=0)=[];
RandomColumn1=RandomColumn1(randperm(length(RandomColumn1)));
RandomColumn=RandomColumn1(1);
% storing the current randomly chosen column before changing
tempColumn=AG(:,RandomColumn);
% shuffling the position of 1
AG(:,RandomColumn)=AG(randperm(size(AG,1)),RandomColumn);
% following checks if the column has remained same after shuffling. This while loop should execute (extremely) rarely.
while tempColumn==AG(:,RandomColumn)
tempColumn=AG(:,RandomColumn);
AG(:,RandomColumn)=AG(randperm(size(AG,1)),RandomColumn);
end
AG
If I have sequence 1 0 0 0 1 0 1 0 1 1 1
how to effectively locate zero which has from both sides 1.
In this sequence it means zero on position 6 and 8. The ones in bold.
1 0 0 0 1 0 1 0 1 1 1
I can imagine algorithm that would loop through the array and look one in back and one in front I guess that means O(n) so probably there is not any more smooth one.
If you can find another way, I am interested.
Use strfind:
pos = strfind(X(:)', [1 0 1]) + 1
Note that this will work only when X is a vector.
Example
X = [1 0 0 0 1 0 1 0 1 1 1 ];
pos = strfind(X(:)', [1 0 1]) + 1
The result:
pos =
6 8
The strfind method that #EitanT suggested is quite nice. Another way to do this is to use find and element-wise bit operations:
% let A be a logical ROW array
B = ~A & [A(2:end),false] & [false,A(1:end-1)];
elements = find(B);
This assumes, based on your example, that you want to exclude boundary elements. The concatenations [A(2:end),false] and [false,A(1:end-1)] are required to keep the array length the same. If memory is a concern, these can be eliminated:
% NB: this will work for both ROW and COLUMN vectors
B = ~A(2:end-1) & A(3:end) & A(1:end-2);
elements = 1 + find(B); % need the 1+ because we cut off the first element above
...and to elaborate on #Eitan T 's answer, you can use strfind for an array if you loop by row
% let x = some matrix of 1's and 0's (any size)
[m n] = size(x);
for r = 1:m;
pos(r,:) = strfind(x(r,:)',[1 0 1]) + 1;
end
pos would be a m x ? matrix with m rows and any returned positions. If there were no zeros in the proper positions though, you might get a NaN ... or an error. Didn't get a chance to test.