Matlab find Series of First Negative Number - matlab

With a matrix of numbers in Matlab, how would you find the first negative number after a series of positive numbers?
So far, the only answer I could come up with was to write a loop to check for the first negative number, then record it, then look for the first positive number, and truncate the array there, then start over. Is there a vectorized way to do this?
e.g., I have x = [ -1 -5 -2 3 4 8 -2 -3 1 9], and I want this function or script to give me an array of y = [1 7].

or
find(diff(sign([1 x]))<0)
that is to say: find the locations in x where the difference in sign between successive elements is negative, oh and pushing a 1 onto the front of x to take care of the case where the 1st element is already negative

This is a quite home made solution, check it out
x = [ -1 -5 -2 3 4 8 -2 -3 1 9]
neg_idx = find(x < 0) % // negative values
z = [0 diff(neg_idx)] % // increments among indices of successive values;
% // consecutive indices return a difference of 1 whereas
% // non consecutive return a value greater than 1.
% // because the first element must be distinguished
% // we put the zero in front
id = find(z ~= 1) % // Every time there is a jump, a non consecutive neg.
% // value appears
% // thus the solution is in
y = neg_idx(id)
ans =
1 7
If neg_idx is empty (i.e. no negative value is involved) you will get an Index exceeds matrix dimensions, although the condition is immediate to check.

Related

Algorithm to divide a vector R = 0:upper into a given number of subintervals?

I have been trying to write a MATLAB function that does the following:
Takes inputs "upper" and "number" which are both positive integers. The program should take the interval [0,"upper"] and cut it into "number" subintervals which have integer endpoints and are the same length, except for the last interval (if the interval isn't cleanly divisible by number, then the last interval can contain less integer entries).
Then the program should make a vector x with entries that are the beginnings/ends of those subintervals.
For example, if I have upper = 11 and number = 3, then the program should take the vector
R = [0 1 2 3 4 5 6 7 8 9 10 11]
and generate
x = [0 4 8 11].
It should first create subinterval vectors
r1 = [0 1 2 3 4]
r2 = [4 5 6 7 8]
r3 = [8 9 10 11]
and then loop through x and assign 0 to the first entry and the endpoints of the rs to the other entries of x but I am at a loss as to how to tell MATLAB to do this.
My other problem is that I don't know how to tell MATLAB how to figure out if the interval [0, "upper"] is cleanly divisible by "number" and if not, how to then do to make the last subinterval shorter than the others.
I've sat at this for several days, but I'm not getting anywhere.
Any ideas or pointers in the right direction would be greatly appreciated,
thank you.
I've figured out how to handle cases for when mod(upper, number) = 0, i.e., when upper is cleanly divisible by number. For those cases, I simply make
delta = upper/number;
and then
for i=1:(number+1)
x(i) = (i-1)*delta;
end
Where the difficulties arise is when upper and number aren't cleanly divisible.
Given
upper = 12;
number = 3;
You can generate R using
R = 0:upper; % [0, 1, 2, ..., upper]
If number was a divisor of upper then the output interval would be upper/number. If not then this quotient should be rounded up, so there's a smaller "chunk" left in the final interval. So
interval = ceil(upper/number);
Now we just need to build the output array x. If number was a divisor of upper then the colon notation would give what you want
x = 0:interval:upper; % only useful if number is a divisor of upper
Otherwise, this will not actually include upper; we can handle the two possible cases in an if:
if mod(upper,number) == 0
x = 0:interval:upper; % When number divides upper, this includes upper
else
x = [0:interval:upper, upper]; % Otherwise we need to append upper
end
You could instead handle this by subtracting 0.5 (or any positive value <1) from upper during the creation of x which would ensure it isn't included, and then always append it. Essentially forcing the else case above.
Then the overall code becomes this, note we don't even need to generate R unless you want it as an output:
upper = 11; % Upper limit
number = 3; % Number of intervals
interval = ceil(upper/number); % Interval size
x = [0:interval:(upper-0.5), upper]; % Create output

sum matrix using logical matrix - index exceeds matrix dimensions

I have two matrices.
mcaps which is a double 1698 x 2
index_g which is a logical 1698 x 2
When using the line of code below I get the error message that Index exceeds matrix dimensions. I don't see how this is the case though?
tsp = nansum(mcaps(index_g==1, :));
Update
Sorry I should have mentioned that I need the sum of each column in the mcaps vector
** Example of data **
mcaps index_g
5 6 0 0
4 3 0 0
6 5 1 1
4 6 0 1
8 7 0 0
There are two problems here. I missed one. Original answer is below.
What I missed is that when you use the logical index in this way, you are picking out elements of the matrix that may have different numbers of elements in each column, so MATLAB can't return a well formed matrix back to nansum, and so returns a vector. To get around this, use the fact that 0 + anything = 0
% create a mask of values you don't want to sum. Note that since
% index_g is already logical, you don't have to test equal to 1.
mask = ~index_g & isnan(mcaps)
% create a temporary variable
mcaps_to_sum = mcaps;
% change all of the values that you don't want to sum to zero
mcaps_to_sum(mask) = 0;
% do the sum
sum(mcaps_to_sum,1);
This is basically all that the nansum function does internally, is to set all of the NaN values to zero and then call the sum function.
index_g == 1 returns a 1698 x 2 logical matrix, but then you add in an extra dimension with the colon. To sum the columns, use the optional dim input. You want:
tsp = nansum(mcaps(index_g == 1),1);

Indices of constant consecutive values in a matrix, and number of constant values

I have a matrix with constant consecutive values randomly distributed throughout the matrix. I want the indices of the consecutive values, and further, I want a matrix of the same size as the original matrix, where the number of consecutive values are stored in the indices of the consecutive values. For Example
original_matrix = [1 1 1;2 2 3; 1 2 3];
output_matrix = [3 3 3;2 2 0;0 0 0];
I have struggled mightily to find a solution to this problem. It has relevance for meteorological data quality control. For example, if I have a matrix of temperature data from a number of sensors, and I want to know what days had constant consecutive values, and how many days were constant, so I can then flag the data as possibly faulty.
temperature matrix is number of days x number of stations and I want an output matrix that is also number of days x number of stations, where the consecutive values are flagged as described above.
If you have a solution to that, please provide! Thank you.
For this kind of problems, I made my own utility function runlength:
function RL = runlength(M)
% calculates length of runs of consecutive equal items along columns of M
% work along columns, so that you can use linear indexing
% find locations where items change along column
jumps = diff(M) ~= 0;
% add implicit jumps at start and end
ncol = size(jumps, 2);
jumps = [true(1, ncol); jumps; true(1, ncol)];
% find linear indices of starts and stops of runs
ijump = find(jumps);
nrow = size(jumps, 1);
istart = ijump(rem(ijump, nrow) ~= 0); % remove fake starts in last row
istop = ijump(rem(ijump, nrow) ~= 1); % remove fake stops in first row
rl = istop - istart;
assert(sum(rl) == numel(M))
% make matrix of 'derivative' of runlength
% don't need last row, but needs same size as jumps for indices to be valid
dRL = zeros(size(jumps));
dRL(istart) = rl;
dRL(istop) = dRL(istop) - rl;
% remove last row and 'integrate' to get runlength
RL = cumsum(dRL(1:end-1,:));
It only works along columns since it uses linear indexing. Since you want do something similar along rows, you need to transpose back and forth, so you could use it for your case like so:
>> original = [1 1 1;2 2 3; 1 2 3];
>> original = original.'; % transpose, since runlength works along columns
>> output = runlength(original);
>> output = output.'; % transpose back
>> output(output == 1) = 0; % see hitzg's comment
>> output
output =
3 3 3
2 2 0
0 0 0

find elements occurring more than once and delete matlab

I have troubles in solving this problem and I ask for your help..
suppose that I have a vector
vector1 = [ 1 1 2];
with an associated error vector (the first element of vector1 is linked with the first element of error1)
error1= [0.08 0.9 0.3];
if vector1 contains 2 equal elements (first and second element in this case) I should replace the element with the highest error with NaN
Any help is appreciated.
I assume the following things:
vector1 can have more than 2 equal elements.
error1 can contain negative values.
This may not be the fastest solution, but it works, according to me.
% Generating random inputs
vector1=[1 2 2 1 1 1 3 3 4 4 5 5 6];
error=3*randn(1,13);
% Sorting the vector
[vector2,I]=sort(vector1);
error2=error(I);
% finding unique elements and the range of repeated elements is stored in b2
[~,b,~]=unique(vector2)
b2=[b [b(2:end)-1;length(vector2)]]
b2((b2(:,1)-b2(:,2))==0,:)=[]
% Calculating maximum error for those repeated indices.
for i=1:size(b2,1)
[~,ind]=max(error2(b2(i,1):b2(i,2)));
maxErrorInd(i,1)=ind+b2(i,1)-1;
end
% Replacing with NaN
vector2(maxErrorInd)=NaN
% Mapping back since we had sorted
vector3(I)=vector2
error3(I)=error2
See if this works for you -
presence_vec_ele = double(bsxfun(#eq,vector1,unique(vector1)'))
presence_vec_ele(presence_vec_ele==0)=nan
[~,min_ind] = nanmin(bsxfun(#times,presence_vec_ele,error1),[],2)
error1(setdiff(1:numel(error1),min_ind))=NaN

Finding contiguous points in an increasing range

I have a set of data points in a vector. For example,
[NaN, NaN, NaN, -1.5363, NaN -1.7664, -1.7475];
These data result from a code which selects 3 points within a specified range (specifically. -0.6 an 0.6). If three points from the column do not exist in this range, the range is incrementally expanded until three points are found. In the above example, the range was increased to -1.8 to 1.8. However, the data we are analyzing is erratic, and has random peaks and troughs, leading to points which are non-contiguous being accepted into the range (element 3 is chosen to be valid, but not element 4).
What would be the best way to go about this? I already have a code to incrementally increase the range to find three points, I just need to modify it to not stop at any three points, but to increase the range until it finds three CONTIGUOUS points. If that were done for the above example, I would just evaluate slopes to remove the 3rd element (since between 3 and 4, the slope is negative).
Thanks.
Assuming your data as provided in the example is in the variable x, you can use isnan and findstr like so:
x = [NaN, NaN, NaN, -1.5363, NaN -1.7664, -1.7475, 123];
~isnan(x)
ans =
0 0 0 1 0 1 1 1
pos = findstr(~isnan(x), [1 1 1]);
The reason for using findstr like this is that we would like to find the sequence [1 1 1] within the logical array returned by isnan, and findstr will return the index of the positions in the input array where this sequence appears.
For your example data, this will return [], but if you change it to the data in the example I have given, it will return 6, and you can extract the contiguous region with x(pos:pos+2). You will have to be a bit careful about cases where there are more than 3 contiguous values (if there were 4, it would return [6 7]) and the cases where there is more than one contiguous region. If you don't need to do anything meaningful with these cases then just use pos(1).
If you want to extract the entirety of the first contiguous region whose length is greater than or equal to 3, you could do something like:
x = [NaN, NaN, NaN, -1.5363, NaN -1.7664, -1.7475, 123, 456, 789];
startPos = [];
stopPos = [];
pos = findstr(~isnan(x), [1 1 1]);
if ~isempty(pos)
startPos = pos(1);
stopPos = startPos + 2;
% Find any cases where we have consecutive numbers in pos
if length(pos) > 1 && any(diff(pos) == 1)
% We have a contiguous section longer than 3 elements
% Find the NaNs
nans = find(isnan(x));
% Find the first NaN after pos(1), or the index of the last element
stopPos = nans(nans > startPos);
if ~isempty(stopPos)
stopPos = stopPos(1) - 1; % Don't want the NaN
else
stopPos = length(x);
end
end
end
x(startPos:stopPos)