Finding repeating pairs of specific numbers within column (Matlab) - matlab

I'm stuck on this probably very trivial problem.
I have a matrix like this (just much larger):
A = [1 3 4 8 10 12 14 17 19; 5 30 90 30 50 70 5 30 5]'
I'm now looking for pairs of specific numbers in A(:, 2). For example, I'd like to find all 30s that are preceded by 5s in A(:, 2) and extract the corresponding row in A(:, 1).
So I'd like to end up, in this example, with B = [3, 17];
How can I do that in Matlab? Any help much appreciated.

You can run:
num_wanted=30;
num_previous=5;
is_match=[ false; A(1:end-1,2)==num_previous & A(2:end,2)==num_wanted ]; %the first element cannot be a match
A(is_match,1).',

Related

index of first greater than during two array comparison

I have two arrays threshold and values.
threshold=[10 22 97]
values=[99 23 77 11 8 10]
I want to output idx such that threshold(idx-1)<values(i)<=threshold(idx). That is for the above example output will be
output=[4 3 3 2 1 1]
The naive code that can produce above output will be
output=ones(1,length(values))*(length(values)+1);
for i=1:length(values)
for j=1:length(threshold)
if(values(i)>threshold(j))
output(i)=j;
end
end
end
Is there a simple way of doing it. I want to avoid loops.
You can use histc command, with a slight adjustment of threshold array
>> threshold=[-inf 10 22 97 inf];
>> values=[99 23 77 11 8 10];
>> [~, output] = histc( values, threshold+.1 )
output =
4 3 3 2 1 1
The modification of threshold is due to "less-than"/"less-than-equal" type of comparison for bin boundary decisions.
No loops often means you'll gain speed by increasing peak memory. Try this:
threshold = [10 22 97];
values = [99 23 77 11 8 10];
%// Do ALL comparisons
A = sum(bsxfun(#gt, values.', threshold));
%// Find the indices and the ones before
R = max(1, [A; A-1]);
%// The array you want
R(:).'
If you run out of memory, just use the loop, but then with a find replacing the inner loop.
Loops aren't all that bad, you know (if you have MATLAB > R2008). In theory, the solution above shouldn't even be faster than a loop with find, but oh well...profiling is key :)

Removing a row in a matrix, by removing an entry from a possibly different row for each column

I have a vector of values which represent an index of a row to be removed in some matrix M (an image). There's only one row value per column in this vector (i.e. if the image is 128 x 500, my vector contains 500 values).
I'm pretty new to MATLAB so I'm unsure if there's a more efficient way of removing a single pixel (row,col value) from a matrix so I've come here to ask that.
I was thinking of making a new matrix with one less row, looping through each column up until I find the row whose value I wish to remove, and "shift" the column up by one and then move onto the next column to do the same.
Is there a better way?
Thanks
Yes, there is a solution which avoids loops and is thus faster to write and to execute. It makes use of linear indexing, and exploits the fact that you can remove a matrix entry by assigning it an empty value ([]):
% Example data matrix:
M = [1 5 9 13 17
2 6 10 14 18
3 7 11 15 19
4 8 12 16 20];
% Example vector of rows to be removed for each column:
vector = [2 3 4 1 3];
[r c] = size(M);
ind = sub2ind([r c],vector,1:c);
M(ind) = [];
M = reshape(M,r-1,c);
This gives the result:
>> M =
1 5 9 14 17
3 6 10 15 18
4 8 11 16 20

MATLAB: Starting with a vector of starting points and a vector of end points, make runs of consecutive numbers between these points [duplicate]

This question already has answers here:
Vectorizing the Notion of Colon (:) - values between two vectors in MATLAB
(4 answers)
Closed 9 years ago.
This is probably simple but here is my problem.
I have two vectors, starts and ends. Starts are the starting points of sequences of consecutive numbers and ends are the end points of sequences of consecutive numbers. I would like to create a vector which contains these runs.
So for example, say
starts = [2 7 10 18 24]
ends = [5 8 15 20 30]
I would like to create the following vector
ans = [2 3 4 5 7 8 10 11 12 13 14 15 18 19 20 24 25 26 27 28 29 30]
Using starts:end only uses the first element of each vector
I would also like to do this without using a (for) loop in order to keep it as fast as possible!
Thanks for reading
Chris
Assuming there's always the same number of start and end points, and they always match (e.g. the nth start corresponds to the nth end), then you can do
cell2mat(arrayfun(#(s,e) (s:e), starts, ends, 'UniformOutput', false))
For a bit more detailed explanation, the arrayfun(#(s,e) (s:e), starts, ends, 'UniformOutput', false) part will generate a sequence of n cell arrays, where n is the length of the starts and ends vectors, such that each cell array has the sequence starts(i):ends(i) corresponding to the ith elements of the two vectors. Then the cell2mat function will fuse each of the individual cell arrays into 1 larger matrix.
When you're worried about making it fast, preallocate:
starts = [2 7 10 18 24]
ends = [5 8 15 20 30]
a = zeros(1,sum(ends)+numel(ends)-sum(starts));
% or a = zeros(1,sum(ends+1-starts))
j = 1;
for i = 1:numel(ends)
j2 = j+ends(i)-starts(i);
a(j:j2) = (starts(i):ends(i));
j = j2+1;
end

Find the increasing and decreasing trend in a curve MATLAB

a=[2 3 6 7 2 1 0.01 6 8 10 12 15 18 9 6 5 4 2].
Here is an array i need to extract the exact values where the increasing and decreasing trend starts.
the output for the array a will be [2(first element) 2 6 9]
a=[2 3 6 7 2 1 0.01 6 8 10 12 15 18 9 6 5 4 2].
^ ^ ^ ^
| | | |
Kindly help me to get the result in MATLAB for any similar type of array..
You just have to find where the sign of the difference between consecutive numbers changes.
With some common sense and the functions diff, sign and find, you get this solution:
a = [2 3 6 7 2 1 0.01 6 8 10 12 15 18 9 6 5 4 2];
sda = sign(diff(a));
idx = [1 find(sda(1:end-1)~=sda(2:end))+2 ];
result = a(idx);
EDIT:
The sign function messes things up when there are two consecutive numbers which are the same, because sign(0) = 0, which is falsely identified as a trend change. You'd have to filter these out. You can do this by first removing the consecutive duplicates from the original data. Since you only want the values where the trend change starts, and not the position where it actually starts, this is easiest:
a(diff(a)==0) = [];
This is a great place to use the diff function.
Your first step will be to do the following:
B = [0 diff(a)]
The reason we add the 0 there is to keep the matrix the same length because of the way the diff function works. It will start with the first element in the matrix and then report the difference between that and the next element. There's no leading element before the first one so is just truncates the matrix by one element. We add a zero because there is no change there as it's the starting element.
If you look at the results in B now it is quite obvious where the inflection points are (where you go from positive to negative numbers).
To pull this out programatically there are a number of things you can do. I tend to use a little multiplication and the find command.
Result = find(B(1:end-1).*B(2:end)<0)
This will return the index where you are on the cusp of the inflection. In this case it will be:
ans =
4 7 13

Matlab: Dividing chunks of data randomly into equal sized sets

I have a large dataset that I need to divide randomly into 5 almost equal sized sets for cross validation. I have happily used _crossvalind_ to divide into sets before, however this time I need to divide chunks of data into these groups at a time.
Let's say my data looks like this:
data = [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18];
Then I want to divide them randomly into 5 groups in chunks of 2, e.g. like this
g1 = [3 4], [11 12]
g2 = [9 10]
g3 = [1 2], [15 16]
g4 = [7 8], [17 18]
g5 = [5 6], [13 14]
I think I can do this with some for-loops, but I'm guessing there must be a much more cost-efficient way to do it in matlab :-)
Any suggestions?
I'm interpreting your needs to be random ordering of sets, but within each set, the ordering of elements is unchanged from the parent set. You can use randperm to randomly order the number of sets and use linear indexing for the elements.
dataElements=numel(data);%# get number of elements
totalGroups=5;
groupSize=dataElements/totalGroups;%# I'm assuming here that it's neatly divisible as in your example
randOrder=randperm(totalGroups);%# randomly order of numbers from 1 till totalGroups
g=reshape(data,groupSize,totalGroups)'; %'# SO formatting
g=g(randOrder,:);
The different rows of g give you the different groupings.
You can shuffle the array (randperm) and then divide it into consequentive equal parts.
data = [10 20 30 40 50 60 70 80 90 100 110 120 130 140 150];
permuted = data(randperm(length(data)));
% padding may be required if the length of data is not divisible by the size of chunks
k = 5;
g = reshape(permuted, k, length(data)/k);