MATLAB: Find function within range - matlab

How can I use the find function within specific ranges.
Say, I have an array arr1 with random values. I have the start & end indices of the portions I'd like to analyze (in this example, I want to find the first occurrence for when the value is larger than 0.8)
How could the find function be used here with start and end indices and the condition as well?
For example:
arr1 = rand(1000,1);
start_ind = [100;500;850];
end_ind = [160;620;925];
for i = 1:length(start_ind)
output = find(arr1(start_ind(i):end_ind(i)) >=0.8); % ????
end
Much appreciated,

Use the second argument of find to only get the first match. You can then shift indices by adding start_ind - 1:
arr1 = rand(1000,1);
start_ind = [100; 500; 850];
end_ind = [160; 620; 925];
output = zeros(length(start_ind), 1);
for i = 1:length(start_ind)
output(i) = find(arr1(start_ind(i):end_ind(i)) >=0.8, 1) + start_ind(i) - 1;
end

Related

Vectorizing logic to check if index matches

I have the following function that works perfectly, but I would like to apply vectorization to it...
for i = 1:size(centroids,1)
centroids(i, :) = mean(X(idx == i, :));
end
It checks if idx matches the current index and if it does, it calculates the mean value for all the X values that correspond to that index.
This is my attempt at vectorization, my solution does not work and I know why...
centroids = mean(X(idx == [1:size(centroids,1)], :));
The following idx == [1:size(centroids,1)] breaks the code. I have no idea how to check if idx equals to either of the numbers from 1 to size(centroids,1).
tl:dr
Get rid of the for loop through vectorization
One option is to use arrayfun;
nIdx = size(centroids,1);
centroids = arrayfun(#(ii) mean(X(idx==ii,:)),1:nIdx, 'UniformOutput', false);
centroids = vertcat(centroids{:})
Since the output of a single function call is not necessarily a scalar, the UniformOutput option has to be set to false. Thus, arrayfun returns a cell array and you need to vertcat it to get the desired double array.
you can split the matrix into cells and take the mean from each cell using cellfun (which applies a loop in its inner operation):
generate data:
dim = 10;
N = 400;
nc = 20;
idx = randi(nc,[N 1]);
X = rand(N,dim);
centroids = zeros(nc,dim);
mean using loop (the question's method)
for i = 1:size(centroids,1)
centroids(i, :) = mean(X(idx == i, :));
end
vectorizing:
% split X into cells by idx
A = accumarray(idx, (1:N)', [nc,1], #(i) {X(i,:)});
% mean of each cell
C = cell2mat(cellfun(#(x) mean(x,1),A,'UniformOutput',0));
maximum absolute error between the methods:
max(abs(C(:) - centroids(:))) % about 1e-16

matlab vectorization if statement

Can someone please tell me the vectorized implementation of following matlab code. Predicted is an array containing either of the two values "pos" or "neg". I have to copy the values when condition comes true.
p = 1;
box = zeros(size(bbox));
for k = 1: size(predicted)
if predicted(k) == 'pos'
box(p,:) = bbox(k,:);
p = p + 1;
end
end
bbox=rand(100); %demo data
predicted = rand(1,100)>0.5; %logical values
%You want to convert your array of strings into an array of logical values
%predicted=strcmp(predicted,'pos');
box=bbox(predicted,:);

Removing repeated elements in a list of words and counting the number of repetitions

This is my code, I am trying to sort an array of words, and calling the sorted array 'a'.
I am trying to use a while loop to compare adjacent elements of a, and as it is sorted any repetitions should already be next to each other. If there is a repetition I remove the word and and it to the count. I am unsure how to get my output to show each sorted word and its associated count together. Thank you for any help.
(myAsort is a function I have already made that puts words into alphabetical order)
For example if I input myACsort({'cat','dog','cat'), I want the output to be:
answer =
'cat' 'dog'
count:2 count:1
function [ answer ]= myACsort( input )
%UNTITLED2 Summary of this function goes here
% Detailed explanation goes here
a = myAsort(input);
n = length(a);
i = 1;
count = 1;
while (i<=n)
if isequal(a{i},a{i+1})
a(i+1) = [];
count = count+1;
else
count = 1;
i=i+1;
end
end
end
The usual combination of unique and accumarray would be my suggestion:
>> strs = {'cat','dog','cat'};
>> [uStr,ia,ic] = unique(strs);
>> countCell = [uStr(:).'; num2cell(accumarray(ic,1)).']
countCell =
'cat' 'dog'
[ 2] [ 1]
FYI, you can later extract the counts via counts = [countCell{2,:}];.
If you wan to do it without the help of these functions, you can fix up your myACsort function as follows:
function answer = myACsort(input)
a = sort(input); % sort operates on cell arrays of strings
i = 1; count = 1;
uwords = a(1);
while (i<numel(a))
if i<numel(a) && isequal(a{i},a{i+1})
a(i+1) = [];
count(i) = count(i)+1;
else
i=i+1;
count(i) = 1;
uwords(i) = a(i);
end
end
answer = [uwords(:).'; num2cell(count(:)).'];
Although array growing is not very efficient.
Another approach: sort the strings (variable sortedStrs), detect the end of each run of equal strings in the sorted sequence (variable ind), and the result is easily obtained from that.
strs = {'cat','dog','cat'}; %// data
n = numel(strs);
sortedStrs = sort(strs);
dif = arrayfun(#(n) ~strcmp(sortedStrs{n},sortedStrs{n-1}), 2:n);
ind = [ find(dif) n ];
result(1,:) = sortedStrs(ind);
result(2,:) = mat2cell([ind(1) diff(ind)],1,ones(1,numel(ind)));

Changing values in one MatLab matrix based on ranges stored in a second matrix

Elements of a column matrix of non-sequential numbers (sourceData) should have their values incremented if their index positions lie between certain values as defined in a second column matrix (triggerIndices) which lists the indices sequentially.
This can be easily done with a for-loop but can it be done in a vectorized way?
%// Generation of example data follows
sourceData = randi(1e3,100,1);
%// sourceData = 1:1:1000; %// Would show more clearly what is happening
triggerIndices = randperm(length(sourceData),15);
triggerIndices = sort(triggerIndices);
%// End of example data generation
%// Code to be vectorized follows
increment = 75;
addOn = 100;
for index = 1:1:length(triggerIndices)-1
sourceData(triggerIndices(index):1:triggerIndices(index+1)-1) = ...
sourceData(triggerIndices(index):1:triggerIndices(index+1)-1) + addOn;
addOn = addOn + increment;
end
sourceData(triggerIndices(end):1:end) = ....
sourceData(triggerIndices(end):1:end) + addOn;
%// End of code to be vectorized
How about replacing everything with:
vals = sparse(triggerIndices, 1, increment, numel(sourceData), 1);
vals(triggerIndices(1)) = addOn;
sourceData(:) = sourceData(:) + cumsum(vals);
This is basically a variant of run-length decoding shown here.

Saving different sized vectors from a nested for loop in MATLAB

I have a 3 nested for loop that produces a vector, but each time it runs through the loops the vector that is produced changes size. I need to save each of these vectors at the ended of the for loops, so I was thinking of using mat2cell and store them in a cell. But I don't know the code get out a cell vector that will contain each of these different sized vectors.
I will give an example code
for ip = n_low:n_up
for jx = x_low:x_up
for jphi = phi_lowx:phi_upx
lx = find_path(ip,jx,jphi,0,1);
.
.
.
A_r = volumeintegrate(integr_final_r , r , z , phi);
end
end
end
Obviously, you don't know what these variables are or the numbers but I think its not needed to solve the problem. Anyways A_r is what is spit out at the end of the loops, but A_r varies in size as the loop repeats itself. I need to save every A_r vector.
Add a counter and save to a cell element: for example:
counter=0
for ...
for ...
for ...
counter=counter+1;
A_r{counter} = volumeintegrate(integr_final_r , r , z , phi);
then to extract the n-th vector just write A_r{n}
Just create a cell array:
A_r = cell(Ni, Nj, Nk)
Then create loops - note I am indexing over well behaved integers which I will use as index into my cell array, then computing the actual value for the variables you need by looking at the array iVec etc:
iVec = n_low:n_up;
Ni = numel(iVec);
jVec = x_low:x_up;
Nj = numel(jVec);
kVec = phi_lowx:phi_upx;
Nk = numel(kVec);
A_r = cell(Ni, Nj, Nk);
for ii = 1:Ni
ip = iVec(ii);
for jj = 1:Nj
jx = jVec(jj);
for kk = 1:Nk
jphi = kVec(kk);
lx = find_path(ip,jx,jphi,0,1);
....
A_r{ii,jj,kk} = volumeintegrate(integr_final_r , r , z , phi);;
end
end
end
You can now access each array in the same way that it was assigned:
A_r{ii,jj,kk}