How to find exact number of minimum values in matrix? [duplicate] - matlab

This question already has answers here:
Get the indices of the n largest elements in a matrix
(4 answers)
Closed 8 years ago.
I want to find 3 minimum values in my matrix.
For example:
7 7 11 5 6
8 9 6 3 2
10 15 8 3 4
12 9 6 8 11
3 minimum values: 2,3,4.
Any suggestions?

If the vector is large, sorting a matrix (O(n*log n)) might take more time than doing three linear searches (O(n)). So something like this might actually be faster than sorting the array and selecting the first three values.
On my computer, this approach is faster on vectors larger than 1000-3000 elements.
num_vals = 3
vals = zeros(num_vals,1);
for k = 1:3
[min_val, idx] = min(A);
vals(ii) = min_val;
A(idx) = NaN;
end
To illustrate:
A = rand(1e6,1);
S = A;
%% Linear search:
tic
for ii = 1:10
A = S;
num_vals = 3;
vals = zeros(num_vals,1);
for ii = 1:3
[min_val, idx] = min(A);
vals(ii) = min_val;
A(idx) = NaN;
end
end
t1 = toc
A = S;
%% Sorting first:
tic
for ii = 1:10
As = sort(A(:),'ascend');
vals2 = As(1:3);
end
t2 = toc
isequal(vals, vals2)
t1 =
0.0661
t2 =
0.4781
ans =
1

Something like this:
nrItems = 3;
yourAr = [ 7 7 11 5 6 8 9 6 3 2 10 15 8 3 4 12 9 6 8 11 ];
sortAr = sort(yourAr, 'ascend');
vals = sort(1:nrItems)

Related

How to save indices and values from Matrix in Matlab?

I have a 3x3 Matrix and want to save the indices and values into a new 9x3 matrix. For example A = [1 2 3 ; 4 5 6 ; 7 8 9] so that I will get a matrix x = [1 1 1; 1 2 2; 1 3 3; 2 1 4; 2 2 5; ...] With my code I only be able to store the last values x = [3 3 9].
A = [1 2 3 ; 4 5 6 ; 7 8 9];
x=[];
for i = 1:size(A)
for j = 1:size(A)
x =[i j A(i,j)]
end
end
Thanks for your help
Vectorized approach
Here's one way to do it that avoids loops:
A = [1 2 3 ; 4 5 6 ; 7 8 9];
[ii, jj] = ndgrid(1:size(A,1), 1:size(A,2)); % row and column indices
vv = A.'; % values. Transpose because column changes first in the result, then row
x = [jj(:) ii(:) vv(:)]; % result
Using your code
You're only missing concatenation with previous x:
A = [1 2 3 ; 4 5 6 ; 7 8 9];
x = [];
for i = 1:size(A)
for j = 1:size(A)
x = [x; i j A(i,j)]; % concatenate new row to previous x
end
end
Two additional suggestions:
Don't use i and j as variable names, because that shadows the imaginary unit.
Preallocate x instead of having it grow in each iteration, to increase speed.
The modified code is:
A = [1 2 3 ; 4 5 6 ; 7 8 9];
x = NaN(numel(A),3); % preallocate
n = 0;
for ii = 1:size(A)
for jj = 1:size(A)
n = n + 1; % update row counter
x(n,:) = [ii jj A(ii,jj)]; % fill row n
end
end
I developed a solution that works much faster. Here is the code:
% Generate subscripts from linear index
[i, j] = ind2sub(size(A),1:numel(A));
% Just concatenate subscripts and values
x = [i' j' A(:)];
Try it out and let me know ;)

How to resize MATLAB matrix

If I have matrix size(mat)= X*Y*6
let call mat(:,:,1)=A
and mat(:,:,2)=B and so on
how do i rezise mat to X*Y*12
where
mat(:,:,1)=mat(:,:,2)= A
mat(:,:,3)=mat(:,:,4)=B
and so on
You can use the following syntax:
%defines input matrix (in your case it is already defined)
m = 500;
n = 400;
z = 6;
mat = rand(m,n,z);
%initialize output matrix
newMat = zeros(m,n,z*2);
%assign old matrix values into the new matrix
newMat(:,:,1:2:end) = mat;
newMat(:,:,2:2:end) = mat;
If you have Matlab 2015a or newer you can use repelem:
N = 2; %// number of times to repeat
result = repelem(mat, 1, 1, N); %// repeat N times along 3rd dim
For older Matlab versions you can do it manually as follows:
N = 2; %// number of times to repeat
ind = ceil(1/N:1/N:size(mat,3)); %// build index with repetitions
result = mat(:,:,ind); %// apply index along desired dim
Example:
>> %// Data
>> mat = randi(9,2,4,2)
mat(:,:,1) =
5 8 9 2
7 3 1 5
mat(:,:,2) =
5 7 1 1
1 8 8 2
>> %// First approach
>> N = 2;
>> result = repelem(mat, 1, 1, N)
result(:,:,1) =
5 8 9 2
7 3 1 5
result(:,:,2) =
5 8 9 2
7 3 1 5
result(:,:,3) =
5 7 1 1
1 8 8 2
result(:,:,4) =
5 7 1 1
1 8 8 2
>> %// Second approach
>> N = 2;
>> ind = ceil(1/N:1/N:size(mat,3));
>> result = mat(:,:,ind)
result(:,:,1) =
5 8 9 2
7 3 1 5
result(:,:,2) =
5 8 9 2
7 3 1 5
result(:,:,3) =
5 7 1 1
1 8 8 2
result(:,:,4) =
5 7 1 1
1 8 8 2

Sort the row of one matrix with respect to another matrix

I have two different matrices A and B:
A =
[7 8 9;
4 5 6]
B =
[22 32 12;
9 8 10]
sortB =
[12 22 32;
8 9 10]
sortindex_B=[3 1 2;2 1 3];, i.e., 12 is in the third position of matrix B, 22 in first, and 32 in second position; similarly for the second row.
Now I want to sort A depending on Sortindex_B (i.e., in matrix A I want 7 as the third element, 8 as the first, and 9 as the second element of the first row; and similarly for the second row: 4 at the second, 5 at the first, and 6 as the third element). Hence the result should look like:
A_final =
[8 9 7;
5 4 6]
How can I achieve this?
You can ask for the sorting index matrix from sort command, when sorting B, and thereafter operate on A as a cell, in so making cellfun available:
A = [7 8 9; 4 5 6];
B = [22 32 12; 9 8 10];
[sortB, I] = sort(B,2);
Icell = mat2cell(I,ones(1, size(I,1)),size(I,2));
Acell = mat2cell(A,ones(1, size(I,1)),size(I,2));
sortA = cell2mat(...
cellfun(#(x,y) y(x), Icell, Acell, 'UniformOutput', false))
Output (you state first row output as 8 9 7, but did you really mean 9 7 8?)
sortA =
9 7 8
5 4 6
For sorting as specifically specified in your question; re-map index matrix I:
A = [7 8 9; 4 5 6];
B = [22 32 12; 9 8 10];
[sortB, I] = sort(B,2);
%// re-map I
for i = 1:size(I,1)
Itmp = I(i,:);
for j = 1:size(I,2)
I(i,Itmp(j)) = j;
end
end
Icell = mat2cell(I,ones(1, size(I,1)),size(I,2));
ImapCell = mat2cell(Imap,ones(1, size(I,1)),size(I,2));
Acell = mat2cell(A,ones(1, size(I,1)),size(I,2));
sortA = cell2mat(...
cellfun(#(x,y) y(x), Icell, Acell, 'UniformOutput', false))
Output
sortA =
8 9 7
5 4 6
Use a combination of sort as in dfri's answer and sub2ind:
A = [7 8 9;
4 5 6];
B = [22 32 12;
9 8 10];
[sortB, sortindex_B] = sort(B,2);
[~, colIdx] = sort(sortindex_B,2);
rowIdx = ndgrid(1:size(B,1),1:size(B,2));
idx = sub2ind(size(B),rowIdx,colIdx);
sortA = A(idx)
ans =
8 9 7
5 4 6
you will have to do this row by row using the index values that the sort function returns.
Something like this should do the trick and is expandable to any number of rows that your matrix A and B may have. This does also validate that A and B are the same size before it continues.
B= [22 32 12; 9 8 10]
A = [7 8 9; 4 5 6];
assert(all(size(A) == size(B)));
sortB = zeros(size(B));
finalA= zeros(size(A));
for i = 1:size(B,1)
[sorted,idx] = sort(B(i,:));
sortB(i,:) = sorted;
tempA = A(i,:);
tempA = tempA(idx);
finalA(i,:) = tempA;
end
There are many clever ways to do this, including this for loop. I hope the comments will explain the logic.
clear; %// input the sample data
A = [7 8 9; 4 5 6];
B = [22 32 12; 9 8 10];
sortB = [12 22 32; 8 9 10];
%// loop through every element in B
[R C]=size(B);
for i=1:C
for j=1:R
%// Where does A(j,i) need to go in Afinal?
%// It needs to go in the j-th row, and in
%// whatever column of B(j,:) equals sortB(j,i).
Afinal( j , find( B(j,:) ==sortB(j,i)) ) = A(j,i);
end
end
And the result:
>> Afinal
Afinal =
8 9 7
5 4 6

Find top n elements in matrix

I have a matrix which contains values and I wish to find the index of the top n minimum values.
I use the following code for finding the minimum most value:
[r,c]=find(Result==min(min(Result)));
I cant find any other questions on stack overflow which answer the question, please help
Maybe you could do something like this:
sorted = sort(Result(:));
topten = sorted(1:10);
[~,ia,~] = intersect(Result(:),topten(:)); % // Get the indices of the top ten values
[r,c]=ind2sub(size(Result),ia); % // Convert the indices to rows and columns
Or without Intersect in the other answer
[sorted,I] = sort(Result(:));
[r,c] = ind2sub(size(Result),I(1:10)); %//Change 10 to any other required value
Use prctile (Statistics Toolbox) to find the appropriate threshold, and then use indexing to select the elements above that threshold:
x = magic(4); %// example
n = 5; %// we want the top n elements
M = numel(x);
p = prctile(x(:), (M-n)/M*100);
indices = find(x>p); %// result in the form linear indices
[row, col] = find(x>p); %// result in the form of row and column indices
In this example:
>> x
x =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
>> indices.'
ans =
1 8 12 13 15
>> row.'
ans =
1 4 4 1 3
>> col.'
ans =
1 2 3 4 4
>> x(indices).'
ans =
16 14 15 13 12
Example with repeated elements:
>> x = [1 1 2 5; 3 4 3 5];
>> n = 5;
gives
>> indices.'
ans =
2 4 6 7 8
>> row.'
ans =
2 2 2 1 2
>> col.'
ans =
1 2 3 4 4
>> x(indices).'
ans =
3 4 3 5 5

What is the simplest way to create a weight matrix bases on how frequent each element appear in the matrix?

This is the input matrix
7 9 6
8 7 9
7 6 7
Based on the frequency their appearance in the matrix (Note. these values are for explanation purpose. I didn't pre-calculate them in advance. That why I ask this question)
number frequency
6 2
7 4
8 1
9 2
and the output I expect is
4 2 2
1 4 2
4 2 4
Is there a simple way to do this?
Here's a three-line solution. First prepare the input:
X = [7 9 6;8 7 9;7 6 7];
Now do:
[a m n] = unique(X);
b = hist(X(:),a);
c = reshape(b(n),size(X));
Which gives this value for c:
4 2 2
1 4 2
4 2 4
If you also wanted the frequency matrix, you can get it with this code:
[a b']
Here is a code with for-loop (a is input matrix, freq - frequency matrix with 2 columns):
weight = zeros(size(a));
for k = 1:size(freq,1)
weight(a==freq(k,1)) = freq(k,2);
end
Maybe it can be solved without loops, but my code looks like:
M = [7 9 6 ;
8 7 9 ;
7 6 7 ;];
number = unique(M(:));
frequency = hist(M(:), number)';
map = containers.Map(number, frequency);
[height width] = size(M);
result = zeros(height, width); %allocate place
for i=1:height
for j=1:width
result(i,j) = map(M(i,j));
end
end