Smallest N elements of an array with their location - matlab

I have an array called Acc_Std of size 1 Row and 222 Columns.
I need to have the smallest 100 values in each that array but with their original location.
I have written this code but, actually, doesn't work:
for Col = 1:222
[Std_Cont, Std_Loc] = min(Acc_Std(:));
Sort_Std_Cont(Col,1) = Std_Cont;
Sort_Std_Loc(Col,1) = Std_Loc;
Acc_Std(Std_Loc) = []; % Here is the problem in my code
end

Use both outputs of sort:
% Example data
Acc_Std = randi(10, 1,10);
% Extract the smallest N elements
N = 3;
% Sort, while saving the original indices
[B, indices] = sort(Acc_Std);
% Now extract the N smallest elements
smallest_N = B(1:N);
% Check that they are indeed located at the
% indices returned by sort()
isequal(smallest_N, Acc_Std(indices(1:N)))
Result of executing this little script:
ans =
1

Related

Merging and adding two column vectors in a specific way in MATLAB

I have two column vectors "b" and "c"
b = [0;1;0;1;1;3;1;2;0;1]
c = [0.25; 0.21; 0.33; 0.22;0.24]
I need the output vector
output = [0; 0.25; 0.25; 0.46; 0.79;0.79; 1.01; 1.01; 1.01; 1.25]
Whenever there is 0 or any number (except 1) in index position 1 of b, it will assign 0 to output vector.
Whenever it finds the number 1 for the first time in b (in our case at 2nd index of b), it will take 0.25 from c and assign to the output vector.
It will retain the same number in other index positions of output vector until there is zero or any number (except 1) in b, and whenever it finds the 1 in b, it will take second index of c and will add like (0.25+0.21 = 0.46).
Again it found the 1 at index position 5 of b, so it will take 3rd index element of c and add like (0.46+0.33). The process continues.
The size of the b and output vector are same...
Also, the number of 1s in b are equal to the number of elements in c
Also, this is just an example, the actual size of the column vector "b'' will be like 400x1. So the suggested answer using loop indexing will be good enough.
You can write this logic as follows in a loop, see the comments for details:
% Define inputs
b = [0;1;0;1;1;3;1;2;0;1];
c = [0.25; 0.21; 0.33; 0.22;0.24];
% Create helper arrays
output = zeros(size(b)); % Output defaults to [0, 0, ...]
cSum = cumsum(c); % Cumulative sum of "c"
cIdx = 0; % Current index in "cSum" for output
% Loop over the elements in "b"
for ii = 1:numel(b)
if b(ii) == 1
% If b(ii)=1 then we want to move to the next element in "cSum"
cIdx = cIdx + 1;
end
if cIdx > 0
% If there has been at least one "1" in b so far then the output
% should be taken from "cSum"
output(ii) = cSum(cIdx);
end
end
Alternatively, you could do this without a loop, but with the same underlying logic. Using cumsum(b==1) gives you the index for which element of c you want to use for the output. This gives the same result:
bIdx = cumsum(b==1)+1;
cSum = [0; cumsum(c)];
output = cSum(bIdx);
In your example, these both gives this as desired:
output = [0; 0.25; 0.25; 0.46; 0.79;0.79; 1.01; 1.01; 1.01; 1.25]
Both of these assume the number of 1s in b is less than or equal to the number of elements in c, or they will error.

Fast way to detect one of the value pairs in matlab array

My script generate some arrays. I have a list of value pairs which should not be in array. Pairs are symmetric, if i don't like pair [1 2], then pair [2 1] is also bad. To detect "bad" arrays I use the following approach:
%% SAMPLE DATA
Pair2Find=[1,2;4,6;7,10]; % value pairs to detect
Seq=randi(10,1,10000); % array where detect pairs
%% DETECTION
for iPair=1:size(Pair2Find)
idx=find(or(Seq(1:end-1)==Pair2Find(iPair,1)&Seq(2:end)==Pair2Find(iPair,2),...
Seq(1:end-1)==Pair2Find(iPair,2)&Seq(2:end)==Pair2Find(iPair,1)));
if (~isempty(idx))
display('Bad array')
break
end
end
Everything works fine, but it is the bottleneck of my program.
Could you help me to improve the quality and speed of this code
pairs = [1 2; 4 6; 7 10];
seq = randi(10,1,10000);
for i = 1:size(pairs,1)
pair = pairs(i,:);
res = strfind(seq,pair);
if (~isempty(res))
disp('Bad array!');
break;
end
pair = fliplr(pair);
res = strfind(seq,pair);
if (~isempty(res))
disp('Bad array!');
break;
end
end
If your pairs matrix is very big, you could also increase your loop time (a little bit) as follows:
pairs = [1 2; 4 6; 7 10];
pairs_flip = fliplr(pairs);
seq = randi(10,1,10000);
for i = 1:size(pairs,1)
res = strfind(seq,pairs(i,:));
if (~isempty(res))
disp('Bad array!');
break;
end
res = strfind(seq,pairs_flip(i,:));
if (~isempty(res))
disp('Bad array!');
break;
end
end
If you're able to reshape your vector into an n x 2 array you can do this pretty simply with set operations like intersect. You can create a helper function to detect whether or not the bad pairs are present and return a boolean that you can use for further logic.
For example:
Pair2Find = [1,2;4,6;7,10]; % value pairs to detect
Seq = randi(10,1,1000000); % array where detect pairs
Seq = reshape(Seq, 2, []).'; % Reshape to a 2 column array
test = isbad(Seq, Pair2Find);
function [outbool] = isbad(Seq, Pair2Find)
sortSeq = sort(Seq, 2); % Sort the pairs
uniquepairs = unique(sortSeq, 'rows'); % Find unique pairs
test = intersect(Pair2Find, uniquepairs, 'rows'); % Find pair intersection
outbool = ~isempty(test);
end

How to find maximum value within multiple intervals?

I have two arrays, der_pos and der_neg, which contain indices of an array interpolated. I want to get all the indices for the maximum values of interpolated in the intervals:
der_pos(1):der_neg(1)
der_pos(2):der_neg(2)
etc...
E.g.:
interpolated = [1,5,3,2,7,10,8,14,4]
der_pos = [1,6]
der_neg = [4,9]
So, I would like to obtain the indices:
[2,8]
Because:
in the interval der_pos(1):der_neg(1) → 1:4 → interpolated(1:4) = [1,5,3,2] the max is 5 which is at index 2.
in the interval der_pos(2):der_neg(2) → 6:9 → interpolated(6:9) = [10,8,14,4] the max is 14 which is at index 8.
I managed to do it using a for loop:
interpolated = [1,5,3,2,7,10,8,14,4];
der_pos = [1,6];
der_neg = [4,9];
indices = zeros(1,length(der_pos));
for i = [1:length(der_pos)]
[peak, index] = max(interpolated(der_pos(i):der_neg(i)));
indices(i) = index + der_pos(i) - 1;
endfor
indices % gives [2,8]
But is there a more concise way of doing this?
Here's a sample code. The function findpeaks returns all the peak. The loop then keeps indices of peaks that are in the wanted range.
I added a test to avoid errors in case of no found peak (index will be -1), and to keep the first peak if two peaks are found. You can use a cell if you want to keep all peaks in an interval.
interpolated = [1,5,3,2,7,10,8,14,4];
der_pos = [1 6 7 ];
der_neg = [4 9 8];
[~,i]=findpeaks(interpolated);
indices= -1+zeros(size(der_pos,2),1);
for loopi = 1:length(i)
val = i(i>=der_pos(loopi)&i<=der_neg(loopi));
if ~isempty(val)
indices(loopi) = val(1);
end
end
Here's a way:
interpolated = [1,5,3,2,7,10,8,14,4]; % data
der_pos = [1,6]; % data
der_neg = [4,9]; % data
m = bsxfun(#ge, 1:numel(interpolated), der_pos(:)) .* ...
bsxfun(#le, 1:numel(interpolated), der_neg(:)); % each row is a mask that contains
% 1 for values in the interval and 0 for values outside the interval
m(~m) = NaN; % replace 0 by NaN
[val, result] = max(bsxfun(#times, m, interpolated(:).'), [], 2); % val is the maximum
% of each row, and result is its column index. The maximum is 1 for rows that
% contain at least a 1, and NaN for rows that only contain NaN
result(isnan(val)) = 0; % If the maximum value was NaN the result is set to 0
% (or maybe use NaN), to indicate that the interval was empty
This gives 0 for empty intervals. For example, der_pos = [1,6,8]; der_neg = [4,9,6]; produce result = [2;8;0].
The intervals may overlap. For example, der_pos = [1,6,3]; der_neg = [4,9,7]; produce result = [2;8;6].

How can we use nchoosek() to get all the combinations of the rows of a matrix?

If we have a vector v of 1- 5 numbers we can use nchoosek(v,2) to get all the combinations having two elements. But this function does now allow us to get all the combinations of a matrix. I want to use it to get all the combinations of rows of a matrix.
Here's one way to do it:
function p = q47204269(inMat)
% Input handling:
if nargin == 0 || isempty(inMat)
inMat = magic(5);
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
rowsCell = num2cell(inMat,2);
nRows = size(inMat,1);
p = cell(nRows,1);
for indR = 1:nRows
r = nchoosek(1:nRows,indR);
p{indR} = cell2mat(reshape(rowsCell(r.',:).',indR,1,[]));
end
See also:
The perms function, as it might come in handy in what you're doing.
This question.
with square matrix A
v = 1:size(A,1);
a = nchoosek(v,2);
B = zeros(2,size(A,1),length(a));
for i = 1:length(a)
B(:,:,i) = A(a(i,:)',:);
end
Each layer of array B is a 2 row matrix with the row combos from A
Not the most readable answer, but just for the sake of a one-liner :-)
A = randn(5,3); % example matrix
N = 2; % number of rows to pick each time
result = permute(reshape(A(nchoosek(1:size(A,1), N).', :), N, [], size(A,2)), [1 3 2]);
The result is a 3D array, such that each third-dim slice gives one of the a submatrices of A.

using Matlab to reshape a 4d matrix into a cell array of vectors

I have a 4D matrix (dims - x,y,z,t). I want to reshape it to a 1D cell array of length x*y*z where each element is a long vector of size t which captures all elements at each volume location (x,y,z). After that I need to reshape it back.
I thought of looping over the array to do it since I can't really find a built in function to do it.
Any insights will be super helpful! Thanks!
See if this is that you want:
x = randn(2,3,4,5); % example data
x = reshape(x, [], size(x,4)); % collapse first three dimensions
x = mat2cell(x, ones(1,size(x,1)), size(x,2)); % split first dimension into cells
Luis's answer is great for being semi-vectorized (mat2cell uses a loop). If what you desire is a cell array of size x*y*z where each element is t long, it's possible to use a loop over each volume location and extract the t elements that "temporally" occupy this spot in 4D. Make sure you squeeze out any singleton dimensions to get the resulting vector. This is something to consider if you wanted to go with the loop approach. Assuming your matrix is called A, try the following:
B = cell(size(A,1)*size(A,2)*size(A,3), 1);
count = 1;
for ii = 1 : size(A,1)
for jj = 1 : size(A,2)
for kk = 1 : size(A,3)
B{count} = squeeze(A(ii,jj,kk,:));
count = count + 1;
end
end
end
To get this back into a 4D matrix form, you'd just apply the same logic but in reverse:
Ar = zeros(size(A));
count = 1;
for ii = 1 : size(A,1)
for jj = 1 : size(A,2)
for kk = 1 : size(A,3)
Ar(ii,jj,kk,:) = B{count};
count = count + 1;
end
end
end
Like Luis' solution, but simpler and more complete:
% Transform to cell
x = randn(2,3,4,5); % example data
y = reshape(x, [], size(x,4));
z = num2cell(y,2);
% transform back
x = reshape(cat(1,z{:}), size(x));