finding the first value of one matrix after satisfying condition of another matrix - arrays

Please help me solve this problem...
a = [1 2 3 4 5 6 7 8 9 10]
b = [12 4 13 7 5 7 8 10 3 12]
c = [4 5 3 2 6 7 5 3 4 5]
I have to find the first value on a, if the value on b is less than 10 for more than 3 consecutive places and index for the starting of satisfying the condition. Also the value of c after finding the value of b for same index.
Ans should be index for b=4, index for a=4 and value for a =4 and c=2
Thank you in advance

You may use strfind as one approach -
str1 = num2str(b <10,'%1d') %%// String of binary numbers
indx = strfind(['0' str1],'0111') %%// Indices where the condition is met
ind = indx(1) %%// Choose the first occurance
a_out = a(ind) %%// Index into a
c_out = c(ind) %%// Index into c
Output -
ind =
4
a_out =
4
c_out =
2

To find a given number of consecutive values lower than a threshold, you can apply conv to a vector of 0-1 values resulting from the comparison:
threshold = 10; %// values "should" be smaller than this
number = 4; %// at least 4 consecutive occurrences
ind = find(conv(double(b<threshold), ones(1,number), 'valid')==number, 1);
%// double(b<threshold) gives 0-1.
%// conv(...)==... gives 1 when the sought number of consecutive 1's is reached
%// find(... ,1) gives the first index where that happens
a_out = a(ind);
c_out = c(ind);

Related

Sum up vector values till threshold, then start again

I have a vector a = [1 3 4 2 1 5 6 3 2]. Now I want to create a new vector 'b' with the cumsum of a, but after reaching a threshold, let's say 5, cumsum should reset and start again till it reaches the threshold again, so the new vector should look like this:
b = [1 4 4 2 3 5 6 3 5]
Any ideas?
You could build a sparse matrix that, when multiplied by the original vector, returns the cumulative sums. I haven't timed this solution versus others, but I strongly suspect this will be the fastest for large arrays of a.
% Original data
a = [1 3 4 2 1 5 6 3 2];
% Threshold
th = 5;
% Cumulative sum corrected by threshold
b = cumsum(a)/th;
% Group indices to be summed by checking for equality,
% rounded down, between each cumsum value and its next value. We add one to
% prevent NaNs from occuring in the next step.
c = cumsum(floor(b) ~= floor([0,b(1:end-1)]))+1;
% Build the sparse matrix, remove all values that are in the upper
% triangle.
S = tril(sparse(c.'./c == 1));
% In case you use matlab 2016a or older:
% S = tril(sparse(bsxfun(#rdivide,c.',c) == 1));
% Matrix multiplication to create o.
o = S*a.';
By normalizing the arguments of cumsum with the threshold and flooring you can get grouping indizes for accumarray, which then can do the cumsumming groupwise:
t = 5;
a = [1 3 4 2 1 5 6 3 2];
%// cumulative sum of normalized vector a
n = cumsum(a/t);
%// subs for accumarray
subs = floor( n ) + 1;
%// cumsum of every group
aout = accumarray( subs(:), (1:numel(subs)).', [], #(x) {cumsum(a(x))});
%// gather results;
b = [aout{:}]
One way is to use a loop. You create the first cumulative sum cs, and then as long as elements in cs are larger than your threshold th, you replace them with elements from the cumulative sum on the rest of the elements in a.
Because some elements in a might be larger than th, this loop will be infinite unless we also eliminate these elements too.
Here is a simple solution with a while loop:
a = [1 3 4 2 1 5 6 3 2];
th = 5;
cs = cumsum(a);
while any(cs>th & cs~=a) % if 'cs' has values larger that 'th',
% and there are any values smaller than th left in 'a'
% sum all the values in 'a' that are after 'cs' reached 'th',
% excluding values that are larger then 'th'
cs(cs>th & cs~=a) = cumsum(a(cs>th & cs~=a));
end
Calculate the cumulative sum and replace the indices value obeying your condition.
a = [1 3 4 2 1 5 6 3 2] ;
b = [1 4 4 2 3 5 6 3 5] ;
iwant = a ;
a_sum = cumsum(a) ;
iwant(a_sum<5) = a_sum(a_sum<5) ;

Vectorization- Matlab

Given a vector
X = [1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3]
I would like to generate a vector such
Y = [1 2 3 4 5 1 2 3 4 5 6 1 2 3 4 5]
So far what I have got is
idx = find(diff(X))
Y = [1:idx(1) 1:idx(2)-idx(1) 1:length(X)-idx(2)]
But I was wondering if there is a more elegant(robust) solution?
One approach with diff, find & cumsum for a generic case -
%// Initialize array of 1s with the same size as input array and an
%// intention of using cumsum on it after placing "appropriate" values
%// at "strategic" places for getting the final output.
out = ones(size(X))
%// Find starting indices of each "group", except the first group, and
%// by group here we mean run of identical numbers.
idx = find(diff(X))+1
%// Place differentiated and subtracted values of indices at starting locations
out(idx) = 1-diff([1 idx])
%// Perform cumulative summation for the final output
Y = cumsum(out)
Sample run -
X =
1 1 1 1 2 2 3 3 3 3 3 4 4 5
Y =
1 2 3 4 1 2 1 2 3 4 5 1 2 1
Just for fun, but customary bsxfun based alternative solution -
%// Logical mask with each column of ones for presence of each group elements
mask = bsxfun(#eq,X(:),unique(X(:).')) %//'
%// Cumulative summation along columns and use masked values for final output
vals = cumsum(mask,1)
Y = vals(mask)
Here's another approach:
Y = sum(triu(bsxfun(#eq, X, X.')), 1);
This works as follows:
Compare each element with all others (bsxfun(...)).
Keep only comparisons with current or previous elements (triu(...)).
Count, for each element, how many comparisons are true (sum(..., 1)); that is, how many elements, up to and including the current one, are equal to the current one.
Another method is using the function unique
like this:
[unqX ind Xout] = unique(X)
Y = [ind(1):ind(2) 1:ind(3)-ind(2) 1:length(X)-ind(3)]
Whether this is more elegant is up to you.
A more robust method will be:
[unqX ind Xout] = unique(X)
for ii = 1:length(unqX)-1
Y(ind(ii):ind(ii+1)-1) = 1:(ind(ii+1)-ind(ii));
end

pick up the most value element from matix matlab

I have matrix nx3 like this
A = [ 1 3 50;
1 4 80;
1 6 75;
2 3 20;
3 6 10;
6 8 20;
6 9 99;
. . .
. . .
]
I want to check the first index that have same
=> check the third element and pick the maximum value and re arrange matrix
it should be like
Ans = [1 4 80;
2 3 20;
6 9 99;
. . .
]
I was thinking use max() check to on the third element but how can I detect the first element on matrix that are repeated
To produce the same results as Luis Mendo
Ans = sortrows(A, 3);
[~, J] = unique(Ans(:,1));
Ans = Ans(J,:);
%// Obtain unique values of col 1. Each value will determine a group of rows:
ii = unique(A(:,1));
%// For each group of rows, compute maximum of column 3. This is done efficiently
%// with accumarray. Use its sparse option to avoid memory problems , in case
%// values of column 1 are very disperse:
kk = nonzeros(accumarray(A(:,1),A(:,3),[],#max,[],true));
%// Select indices of rows whose column 3 contains the maximum of the group
%// determined by column 1. This is done efficiently using bsxfun twice:
m = any(bsxfun(#eq, A(:,1).', ii) & bsxfun(#eq, A(:,3).', kk));
%// Build result:
result = A(m,:);
In your example:
result =
1 4 80
2 3 20
3 6 10
6 9 99

MATLAB: locate the first position of each unique number from a vector

I wish to locate the first position of each unique number from a vector but without a for loop:
e.g
a=[1 1 2 2 3 4 2 1 3 4];
and I can obtain the unique number by having:
uniq=unique(a);
where uniq = [1 2 3 4]
What I want is to obtain each number's first appearance location, any ideas????
first_pos = [1 3 5 6]
where 1 is firstly appear in position 1, 4 is firstly appear in the sixth position from the vector
ALSO, what about the position of the second appearance??
second_pos = [2 4 9 10]
Thank you very much
Use the second output of unique, and use the 'first' option:
>> A = [1 1 2 2 3 4 2 1 3 4];
>> [a,b] = unique(A, 'first')
a =
1 2 3 4 %// the unique values
b =
1 3 5 6 %// the first indices where these values occur
To find the locations of the second occurrences,
%// replace first occurrences with some random number
R = rand;
%// and do the same as before
A(b) = R;
[a2,b2] = unique(A, 'first');
%// Our random number is NOT part of original vector
b2(a2==R)=[];
a2(a2==R)=[];
with this:
b2 =
2 4 9 10
Note that there will have to be at least 2 occurrences of each number in the vector A if the sizes of b and b2 are to agree (this was not the case before your edit).

matlab: eliminate elements from array

I have quite big array. To make things simple lets simplify it to:
A = [1 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 5 5 5];
So, there is a group of 1's (4 elements), 2's (2 elements), 3's (4 elements), 4's (2 elements) and 5's (8 elements). Now, I want to keep only columns, which belong to group of 3 or more elements. So it will be like:
B = [1 1 1 1 3 3 3 3 5 5 5 5 5 5 5 5];
I was doing it using for loop, scanning separately 1's, 2's, 3's and so on, but its extremely slow with big arrays...
Thanks for any suggestions how to do it in more efficient way :)
Art.
A general approach
If your vector is not necessarily sorted, then you need to run to count the number of occurrences of each element in the vector. You have histc just for that:
elem = unique(A);
counts = histc(A, elem);
B = A;
B(ismember(A, elem(counts < 3))) = []
The last line picks the elements that have less than 3 occurrences and deletes them.
An approach for a grouped vector
If your vector is "semi-sorted", that is if similar elements in the vector are grouped together (as in your example), you can speed things up a little by doing the following:
start_idx = find(diff([0, A]))
counts = diff([start_idx, numel(A) + 1]);
B = A;
B(ismember(A, A(start_idx(counts < 3)))) = []
Again, note that the vector need not to be entirely sorted, just that similar elements are adjacent to each other.
Here is my two-liner
counts = accumarray(A', 1);
B = A(ismember(A, find(counts>=3)));
accumarray is used to count the individual members of A. find extracts the ones that meet your '3 or more elements' criterion. Finally, ismember tells you where they are in A. Note that A needs not be sorted. Of course, accumarray only works for integer values in A.
What you are describing is called run-length encoding.
There is software for this in Matlab on the FileExchange. Or you can do it directly as follows:
len = diff([ 0 find(A(1:end-1) ~= A(2:end)) length(A) ]);
val = A(logical([ A(1:end-1) ~= A(2:end) 1 ]));
Once you have your run-length encoding you can remove elements based on the length. i.e.
idx = (len>=3)
len = len(idx);
val = val(idx);
And then decode to get the array you want:
i = cumsum(len);
j = zeros(1, i(end));
j(i(1:end-1)+1) = 1;
j(1) = 1;
B = val(cumsum(j));
Here's another way to do it using matlab built-ins.
% Set up
A=[1 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5];
threshold=2;
% Get the unique elements of the array
uniqueElements=unique(A);
% Count haw many times each unique element occurs
counts=histc(A,uniqueElements);
% Write which elements should be kept
toKeep=uniqueElements(counts>threshold);
% Make a logical index
indexer=false(size(A));
for i=1:length(toKeep)
% For every unique element we want to keep select the indices in A that
% are equal
indexer=indexer|(toKeep(i)==A);
end
% Apply index
B=A(indexer);

Resources