Removing all zero-values except for those flanking non-zero values - arrays

Given a vector of:
a = [0;0;2;3;0;2;10;11;0;0;0;4;5;8;0;0;0]
Can anybody show or suggest a way to remove all zero-values except for those which flank non-zero values?
The desired result for the above would be:
b = [0;2;3;0;2;10;11;0;0;4;5;8;0]
Where these values have been removed:
[0;0;2;3;0;2;10;11;0;0;0;4;5;8;0;0;0]
I'm not sure where to start with this problem without having to resort to using a set of IF statements such as:
for k=1:length(a)
if a(k) == 0 && a(k+1) == 0
*delete value*
end
if a(k) == 0 && a(k+1) >0
*keep/store value*
end
if a(k) > 0
*keep/store value*
end
if a(k) == 0 && a(k-1) >0
*keep/store value*
end
end
And so forth.

I have another idea (granted, not very different from the other two), using logical indexing:
a(~(~a & ~[diff(a);0] & ~[0;diff(a)] ));
Explanation:
~ - boolean not, returns a boolean value representing the "opposite" of the input.
~a - returns the zero elements of a (it is not required in the example you gave, but is important if you have repeating nonzero values that you would like to keep).
~[diff(a);0] & ~[0;diff(a)] - return the values whose derivative on either size is zero.
a(~(...)) - return the values of a that aren't "zeros with the same values on both sides", which is b.
Another way to write the same thing (using De Morgan's laws and exploiting the "truthiness" of nonzero values):
a( a | [diff(a);0] | [0;diff(a)] );
You can think of it as finding "which values to keep" rather than "which values to remove", where the simplest way I can think of to define which values to keep is "all nonzero elements and zero elements that have a nonzero on either side".

You can use convolution:
b = a(conv(abs(sign(a)), ones(3,1), 'same')>0);
This works as follows:
Convert a to a vector of zeros or ones (abs(sign(a))), with zero if the entry of a is zero and one otherwise.
Convolve with a mask of three ones (conv(..., ones(3,1), 'same')). So a nonzero value in a produces a nonzero result at its position and at its neighbouring positions.
This is compared with zero to create a logical vector with which a is indexed (a(...>0)).
This can be easily generalized to keep more distant neighbours. Specifically, use the mask ones(2*N+1,1) to keep zero values that are up to N entries away of nonzero values.

If you create two additional vectors, one shifting to the left, one to the right from your vector a (EDIT : using circshift as suggested in the comments below) :
a = [0;0;2;3;0;2;10;11;0;0;0;4;5;8;0;0;0];
a_right = circshift(a,-1);
a_left = circshift(a,1);
Create a matrix M :
M = [a,a_right,a_left];
And sum each line :
s = sum(M,2);
Then find the components that differs from 0 :
i = find(s~=0);
This will give you the right indexes to select from your initial vector :
b=a(i)
I get :
b=[0;2;3;0;2;10;11;0;0;4;5;8;0]

Related

Find nearest smaller value in Matlab

I have two vectors of different size. Just as an example:
Triggs = [38.1680, 38.1720, 38.1760, 38.1800, 38.1840, 38.1880, 38.1920, 38.1960, 38.2000, 38.2040, 38.2080, 38.2120, 38.2160, 38.2200, 38.2240, 38.2280, 38.2320, 38.2360, 38.2400, 38.2440, 38.2480, 38.2520, 38.2560, 38.2600, 38.2640, 38.2680]
Peaks = [27.7920, 28.4600, 29.1360, 29.8280, 30.5200, 31.2000, 31.8920, 32.5640, 33.2600, 33.9480, 34.6520, 35.3680, 36.0840, 36.7680, 37.5000, 38.2440, 38.9920, 39.7120, 40.4160, 41.1480, 41.8840, 42.5960, 43.3040, 44.0240, 44.7160, 45.3840, 46.1240, 46.8720, 47.6240, 48.3720, 49.1040, 49.8080, 50.5200, 51.2600]
For each element in Triggs I need to find the nearest smaller element in Peaks.
That is, if Triggs(1) == 38.1680, I need to find the column number equal to Peaks(15) (the 15th element of Peaks).
Just to be 100% clear, the closest element of course could be the next one, that is 38.2440. That would not be ok for me. I will always need the one to the left of the array.
So far I have this:
for i = 1:length(triggersStartTime)
[~,valuePosition] = (min(abs(Peaks-Triggs(i))))
end
However, this could give me the incorrect value, that is, one bigger than Triggs(i), right?
As a solution I was thinking I could do this:
for i = 1:length(Triggs)
[~,valuePosition] = (min(abs(Peaks-Triggs(i))))
if Peaks(valuePosition) >= Triggs(i)
valuePosition = valuePosition-1
end
end
Is there a better way of doing this?
This can be done in a vectorized way as follows (note that the intermediate matrix d can be large). If there is no number satisfying the condition the output is set to NaN.
d = Triggs(:).'-Peaks(:); % matrix of pair-wise differences. Uses implicit expansion
d(d<=0) = NaN; % set negative differences to NaN, so they will be disregarded
[val, result] = min(d, [], 1); % for each column, get minimum value and its row index
result(isnan(val)) = NaN; % if minimum was NaN the index is not valid
If it is assured that there will always be a number satisfying the condition, the last line and the variable val can be removed:
d = Triggs(:).'-Peaks(:); % matrix of pair-wise differences. Uses implicit expansion
d(d<=0) = NaN; % set negative differences to NaN, so they will be disregarded
[~, result] = min(d, [], 1); % for each column, get row index of minimum value
I think this should help you:
temp=sort(abs(Peaks-Triggs));
lowest=find(abs(Peaks-Triggs)==temp(1))

Filter Multiple Conditions Array

I am trying to filter every negative number as well as every other number on an array with MATLAB. How's this possible? I thought I could have done this but it is not working:
Z = A(A<0 | 2:2:end)
The issue is that 2:2:end simply returns the following array
[2, 4, 6, .... % All the way up to numel(A)
The conditional yields a logical array the size of A that is true where an element is negative and false otherwise.
You can't combine these two because they are two different types and two different sizes.
If you want all numbers that are either a negative number or occur at an even location you could create a logical array that is true at all of the even locations (and false otherwise) and then perform logical operations using that instead. To do this, we create an array from [1....numel(A)] and perform the modulo operation (mod) with 2. Even numbers will have a remainder of 0 and odd numbers will have a remained of 1. Therefore, by comparing the result of mod(...,2) to 0 (== 0) we get a logical array that is true in all of the even locations and false otherwise.
even_locations = mod(1:numel(A), 2) == 0;
Z = A(A < 0 | even_locations);
If you simply want the even locations that are also negative
tmp = A(2:2:end);
Z = tmp(tmp < 0);
Or you can use the even_locations array above:
Z = A(A < 0 & even_locations);

Comparing two arrays of pixel values, and store any matches

I want to compare the pixel values of two images, which I have stored in arrays.
Suppose the arrays are A and B. I want to compare the elements one by one, and if A[l] == B[k], then I want to store the match as a key value-pair in a third array, C, like so: C[l] = k.
Since the arrays are naturally quite large, the solution needs to finish within a reasonable amount of time (minutes) on a Core 2 Duo system.
This seems to work in under a second for 1024*720 matrices:
A = randi(255,737280,1);
B = randi(255,737280,1);
C = zeros(size(A));
[b_vals, b_inds] = unique(B,'first');
for l = 1:numel(b_vals)
C(A == b_vals(l)) = b_inds(l);
end
First we find the unique values of B and the indices of the first occurrences of these values.
[b_vals, b_inds] = unique(B,'first');
We know that there can be no more than 256 unique values in a uint8 array, so we've reduced our loop from 1024*720 iterations to just 256 iterations.
We also know that for each occurrence of a particular value, say 209, in A, those locations in C will all have the same value: the location of the first occurrence of 209 in B, so we can set all of them at once. First we get locations of all of the occurrences of b_vals(l) in A:
A == b_vals(l)
then use that mask as a logical index into C.
C(A == b_vals(l))
All of these values will be equal to the corresponding index in B:
C(A == b_vals(l)) = b_inds(l);
Here is the updated code to consider all of the indices of a value in B (or at least as many as are necessary). If there are more occurrences of a value in A than in B, the indices wrap.
A = randi(255,737280,1);
B = randi(255,737280,1);
C = zeros(size(A));
b_vals = unique(B);
for l = 1:numel(b_vals)
b_inds = find(B==b_vals(l)); %// find the indices of each unique value in B
a_inds = find(A==b_vals(l)); %// find the indices of each unique value in A
%// in case the length of a_inds is greater than the length of b_inds
%// duplicate b_inds until it is larger (or equal)
b_inds = repmat(b_inds,[ceil(numel(a_inds)/numel(b_inds)),1]);
%// truncate b_inds to be the same length as a_inds (if necessary) and
%// put b_inds into the proper places in C
C(a_inds) = b_inds(1:numel(a_inds));
end
I haven't fully tested this code, but from my small samples it seems to work properly and on the full-size case, it only takes about twice as long as the previous code, or less than 2 seconds on my machine.
So, if I understand your question correctly, you want for each value of l=1:length(A) the (first) index k into B so that A(l) == B(k). Then:
C = arrayfun(#(val) find(B==val, 1, 'first'), A)
could give you your solution, as long as you're sure that every element will have a match. The above solution would fail otherwise, complaning that the function returned a non-scalar (because find would return [] if no match is found). You have two options:
Using a cell array to store the result instead of a numeric array. You would need to call arrayfun with 'UniformOutput', false at the end. Then, the values of A without matches in B would be those for which isempty(C{i}) is true.
Providing a default value for an index into A with no matches in B (e.g. 0 or NaN). I'm not sure about this one, but I think that you would need to add 'ErrorHandler', #(~,~) NaN to the arrayfun call. The error handler is a function that gets called when the function passed to arrayfun fails, and may either rethrow the error or compute a substitute value. Thus the #(~,~) NaN. I am not sure that it would work, however, since in this case the error is in arrayfun and not in the passed function, but you can try it.
If you have the images in arrays A & B
idx = A == B;
C = zeros(size(A));
C(idx) = A(idx);

How do I filter a list (remove unwanted elements)?

I have looked through the documentation but cannot seem to find the answer. Suppose I have a list
a=(1:1000);
How would I perform a simple filter for example all numbers divisible by 7 and greater than 250?
isDivisable = mod(a,7)==0;
isGreater = a>250;
out = a(isDivisable & isGreater);
It's not a list. It is an array. And you can use boolean indexing (A vector of 1 and 0's) to collect the entries you are interested in.
So the following returns a boolean vector with '1'-s where numbers in a are multiples of 7:
div7 = mod(a, 7) == 0
And you can use that to get the elements you want from the vector a:
b = a(div7)
Returns a new vector with the elements where div7 is equal to 1.
Of course, you can combine boolean vectors to get more complicated 'selectors'. So, like Andrey had shown
bigger250 = (a>250)
is a vector of the same length as a, but with '1'-s where a(i)>250. And you can use that together with div7_vec:
b = a(div7 & bigger250)
The & sign produces a new vector that is the element-wise "and" of both boolean vectors. So that would get you a vector that has '1'-s where both conditions are true.

Indicies of zero ranges in a zero-one matrix

I am using Matlab for one of my projects. I am actually stuck at a point since some time now. Tried searching on google, but, not much success.
I have an array of 0s and 1s. Something like:
A = [0,0,0,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,0,0,0,0];
I want to extract an array of indicies: [x_1, x_2, x_3, x_4, x_5, ..]
Such that x_1 is the index of start of first range of zeros. x_2 is the index of end of first range of zeros.
x_3 is the index of start of second range of zeros. x_4 is the index of end of second range of zeros.
For the above example:
x_1 = 1, x_2 = 3
x_3 = 9, x_4 = 10
and so on.
Of course, I can do it by writing a simple loop. I am wondering if there is a more elegant (vectorized) way to solve this problem. I was thinking about something like prefix some, but, no luck as of now.
Thanks,
Anil.
The diff function is great for this sort of stuff and pretty quick.
temp = diff(A);
Starts = find([A(1) == 0, temp==-1]);
Ends = find([temp == 1,A(end)==0])
Edit: Fixed the error in the Ends calculation caught by gnovice.
Zeros not preceded by other zeros: A==0 & [true A(1:(end-1))~=0]
Zeros not followed by other zeros: A==0 & [A(2:end)~=0 true]
Use each of these plus find to get starts and ends of runs of zeros. Then, if you really want them in a single vector as you described, interleave them.
If you want to get your results in a single vector like you described above (i.e. x = [x_1 x_2 x_3 x_4 x_5 ...]), then you can perform a second-order difference using the function DIFF and find the points greater than 0:
x = find(diff([1 A 1],2) > 0);
EDIT:
The above will work for the case when there are at least 2 zeroes in every string of zeroes. If you will have single zeroes appearing in A, the above can be modified to handle them like so:
diffA = diff([1 A 1],2);
[~,x] = find([diffA > 0; diffA == 2]);
In this case, a single zero value will create repeated indices in x (i.e. if A starts with a single zero, then x(1) and x(2) will both be 1).

Resources