Matlab find indexes of x min values in column - arrays

I there any build function in Matlab which would find in column indexes of x(parameter) min values?
For example:
a = [1; 2; 3; 4; 0; 5]
someFindFunction(a, 2)
ans = [5, 1]
someFindFunction(a, 1)
ans = [5]
someFindFunction(a, 3)
ans = [5, 1, 2]

If you don't mind doing them all, then [~, ans] = sort(a) will do the trick. You can then take the first few elements of ans that you actually need. The build in sort is extremely fast and this should be performant enough despite finding all the mins and not just the ones you need.

#NirFriedman actually did answer your question, but this is an answer that is more self-contained. sort with the second output parameter tells you where each of the values appear in the original matrix after you sort them. As such, if you wanted to make what you asked into a function, you would use the second parameter to index into the second output and only produce those values from the first element up to as many as you desire. Let's also call this something else instead of someFindFunction, like findSmallestLocations. As such:
function [out] = findSmallestLocations(a, ind)
%// Sort the values and get where they're located
[~,b] = sort(a);
%// Retrieve the locations that you want from 1 up to ind
out = b(1:ind);
end
This should now produce what you want. If you want to run this on your own, copy and paste the above code into a M-file called findSmallestLocations.m, then set your current working directory to where this file is located so you can call this function.
Going with your example input and expected outputs, this is what we get:
>> a = [1; 2; 3; 4; 0; 5]
>> findSmallestLocations(a, 2)
ans =
5
1
>> findSmallestLocations(a, 1)
ans =
5
>> findSmallestLocations(a, 3)
ans =
5
1
2

Related

In MATLAB how can I write out a multidimensional array as a string that looks like a raw numpy array?

The Goal
(Forgive me for length of this, it's mostly background and detail.)
I'm contributing to a TOML encoder/decoder for MATLAB and I'm working with numerical arrays right now. I want to input (and then be able to write out) the numerical array in the same format. This format is the nested square-bracket format that is used by numpy.array. For example, to make multi-dimensional arrays in numpy:
The following is in python, just to be clear. It is a useful example though my work is in MATLAB.
2D arrays
>> x = np.array([1,2])
>> x
array([1, 2])
>> x = np.array([[1],[2]])
>> x
array([[1],
[2]])
3D array
>> x = np.array([[[1,2],[3,4]],[[5,6],[7,8]]])
>> x
array([[[1, 2],
[3, 4]],
[[5, 6],
[7, 8]]])
4D array
>> x = np.array([[[[1,2],[3,4]],[[5,6],[7,8]]],[[[9,10],[11,12]],[[13,14],[15,16]]]])
>> x
array([[[[ 1, 2],
[ 3, 4]],
[[ 5, 6],
[ 7, 8]]],
[[[ 9, 10],
[11, 12]],
[[13, 14],
[15, 16]]]])
The input is a logical construction of the dimensions by nested brackets. Turns out this works pretty well with the TOML array structure. I can already successfully parse and decode any size/any dimension numeric array with this format from TOML to MATLAB numerical array data type.
Now, I want to encode that MATLAB numerical array back into this char/string structure to write back out to TOML (or whatever string).
So I have the following 4D array in MATLAB (same 4D array as with numpy):
>> x = permute(reshape([1:16],2,2,2,2),[2,1,3,4])
x(:,:,1,1) =
1 2
3 4
x(:,:,2,1) =
5 6
7 8
x(:,:,1,2) =
9 10
11 12
x(:,:,2,2) =
13 14
15 16
And I want to turn that into a string that has the same format as the 4D numpy input (with some function named bracketarray or something):
>> str = bracketarray(x)
str =
'[[[[1,2],[3,4]],[[5,6],[7,8]]],[[[9,10],[11,12]],[[13,14],[15,16]]]]'
I can then write out the string to a file.
EDIT: I should add, that the function numpy.array2string() basically does exactly what I want, though it adds some other whitespace characters. But I can't use that as part of the solution, though it is basically the functionality I'm looking for.
The Problem
Here's my problem. I have successfully solved this problem for up to 3 dimensions using the following function, but I cannot for the life of me figure out how to extend it to N-dimensions. I feel like it's an issue of the right kind of counting for each dimension, making sure to not skip any and to nest the brackets correctly.
Current bracketarray.m that works up to 3D
function out = bracketarray(in, internal)
in_size = size(in);
in_dims = ndims(in);
% if array has only 2 dimensions, create the string
if in_dims == 2
storage = cell(in_size(1), 1);
for jj = 1:in_size(1)
storage{jj} = strcat('[', strjoin(split(num2str(in(jj, :)))', ','), ']');
end
if exist('internal', 'var') || in_size(1) > 1 || (in_size(1) == 1 && in_dims >= 3)
out = {strcat('[', strjoin(storage, ','), ']')};
else
out = storage;
end
return
% if array has more than 2 dimensions, recursively send planes of 2 dimensions for encoding
else
out = cell(in_size(end), 1);
for ii = 1:in_size(end) %<--- this doesn't track dimensions or counts of them
out(ii) = bracketarray(in(:,:,ii), 'internal'); %<--- this is limited to 3 dimensions atm. and out(indexing) need help
end
end
% bracket the final bit together
if in_size(1) > 1 || (in_size(1) == 1 && in_dims >= 3)
out = {strcat('[', strjoin(out, ','), ']')};
end
end
Help me Obi-wan Kenobis, y'all are my only hope!
EDIT 2: Added test suite below and modified current code a bit.
Test Suite
Here is a test suite to use to see if the output is what it should be. Basically just copy and paste it into the MATLAB command window. For my current posted code, they all return true except the ones more than 3D. My current code outputs as a cell. If your solution output differently (like a string), then you'll have to remove the curly brackets from the test suite.
isequal(bracketarray(ones(1,1)), {'[1]'})
isequal(bracketarray(ones(2,1)), {'[[1],[1]]'})
isequal(bracketarray(ones(1,2)), {'[1,1]'})
isequal(bracketarray(ones(2,2)), {'[[1,1],[1,1]]'})
isequal(bracketarray(ones(3,2)), {'[[1,1],[1,1],[1,1]]'})
isequal(bracketarray(ones(2,3)), {'[[1,1,1],[1,1,1]]'})
isequal(bracketarray(ones(1,1,2)), {'[[[1]],[[1]]]'})
isequal(bracketarray(ones(2,1,2)), {'[[[1],[1]],[[1],[1]]]'})
isequal(bracketarray(ones(1,2,2)), {'[[[1,1]],[[1,1]]]'})
isequal(bracketarray(ones(2,2,2)), {'[[[1,1],[1,1]],[[1,1],[1,1]]]'})
isequal(bracketarray(ones(1,1,1,2)), {'[[[[1]]],[[[1]]]]'})
isequal(bracketarray(ones(2,1,1,2)), {'[[[[1],[1]]],[[[1],[1]]]]'})
isequal(bracketarray(ones(1,2,1,2)), {'[[[[1,1]]],[[[1,1]]]]'})
isequal(bracketarray(ones(1,1,2,2)), {'[[[[1]],[[1]]],[[[1]],[[1]]]]'})
isequal(bracketarray(ones(2,1,2,2)), {'[[[[1],[1]],[[1],[1]]],[[[1],[1]],[[1],[1]]]]'})
isequal(bracketarray(ones(1,2,2,2)), {'[[[[1,1]],[[1,1]]],[[[1,1]],[[1,1]]]]'})
isequal(bracketarray(ones(2,2,2,2)), {'[[[[1,1],[1,1]],[[1,1],[1,1]]],[[[1,1],[1,1]],[[1,1],[1,1]]]]'})
isequal(bracketarray(permute(reshape([1:16],2,2,2,2),[2,1,3,4])), {'[[[[1,2],[3,4]],[[5,6],[7,8]]],[[[9,10],[11,12]],[[13,14],[15,16]]]]'})
isequal(bracketarray(ones(1,1,1,1,2)), {'[[[[[1]]]],[[[[1]]]]]'})
I think it would be easier to just loop and use join. Your test cases pass.
function out = bracketarray_matlabbit(in)
out = permute(in, [2 1 3:ndims(in)]);
out = string(out);
dimsToCat = ndims(out);
if iscolumn(out)
dimsToCat = dimsToCat-1;
end
for i = 1:dimsToCat
out = "[" + join(out, ",", i) + "]";
end
end
This also seems to be faster than the route you were pursing:
>> x = permute(reshape([1:16],2,2,2,2),[2,1,3,4]);
>> tic; for i = 1:1e4; bracketarray_matlabbit(x); end; toc
Elapsed time is 0.187955 seconds.
>> tic; for i = 1:1e4; bracketarray_cris_luengo(x); end; toc
Elapsed time is 5.859952 seconds.
The recursive function is almost complete. What is missing is a way to index the last dimension. There are several ways to do this, the neatest, I find, is as follows:
n = ndims(x);
index = cell(n-1, 1);
index(:) = {':'};
y = x(index{:}, ii);
It's a little tricky at first, but this is what happens: index is a set of n-1 strings ':'. index{:} is a comma-separated list of these strings. When we index x(index{:},ii) we actually do x(:,:,:,ii) (if n is 4).
The completed recursive function is:
function out = bracketarray(in)
n = ndims(in);
if n == 2
% Fill in your n==2 code here
else
% if array has more than 2 dimensions, recursively send planes of 2 dimensions for encoding
index = cell(n-1, 1);
index(:) = {':'};
storage = cell(size(in, n), 1);
for ii = 1:size(in, n)
storage(ii) = bracketarray(in(index{:}, ii)); % last dimension automatically removed
end
end
out = { strcat('[', strjoin(storage, ','), ']') };
Note that I have preallocated the storage cell array, to prevent it from being resized in every loop iteration. You should do the same in your 2D case code. Preallocating is important in MATLAB for performance reasons, and the MATLAB Editor should warm you about this too.

Change diagonals of an array of matrices

I have an application with an array of matrices. I have to manipulate the diagonals several times. The other elements are unchanged. I want to do things like:
for j=1:nj
for i=1:n
g(i,i,j) = gd(i,j)
end
end
I have seen how to do this with a single matrix using logical(eye(n)) as a single index, but this does not work with an array of matrices. Surely there is a way around this problem. Thanks
Use a linear index as follows:
g = rand(3,3,2); % example data
gd = [1 4; 2 5; 3 6]; % example data. Each column will go to a diagonal
s = size(g); % size of g
ind = bsxfun(#plus, 1:s(1)+1:s(1)*s(2), (0:s(3)-1).'*s(1)*s(2)); % linear index
g(ind) = gd.'; % write values
Result:
>> g
g(:,:,1) =
1.000000000000000 0.483437118939645 0.814179952862505
0.154841697368116 2.000000000000000 0.989922194103104
0.195709075365218 0.356349047562417 3.000000000000000
g(:,:,2) =
4.000000000000000 0.585604389346560 0.279862618046844
0.802492555607293 5.000000000000000 0.610960767605581
0.272602365429990 0.551583664885735 6.000000000000000
Based on Luis Mendo's answer, a version that may perhaps be more easy to modify depending on one's specific purposes. No doubt his version will be more computationally efficient though.
g = rand(3,3,2); % example data
gd = [1 4; 2 5; 3 6]; % example data. Each column will go to a diagonal
sz = size(g); % Get size of data
sub = find(eye(sz(1))); % Find indices for 2d matrix
% Add number depending on location in third dimension.
sub = repmat(sub,sz(3),1); %
dim3 = repmat(0:sz(1)^2:prod(sz)-1, sz(1),1);
idx = sub + dim3(:);
% Replace elements.
g(idx) = gd;
Are we already playing code golf yet? Another slightly smaller and more readable solution
g = rand(3,3,2);
gd = [1 4; 2 5; 3 6];
s = size(g);
g(find(repmat(eye(s(1)),1,1,s(3))))=gd(:)
g =
ans(:,:,1) =
1.00000 0.35565 0.69742
0.85690 2.00000 0.71275
0.87536 0.13130 3.00000
ans(:,:,2) =
4.00000 0.63031 0.32666
0.33063 5.00000 0.28597
0.80829 0.52401 6.00000

Given two arrays A and B, how to get B values which are the closest to A

Suppose I have two arrays ordered in an ascending order, i.e.:
A = [1 5 7], B = [1 2 3 6 9 10]
I would like to create from B a new vector B', which contains only the closest values to A values (one for each).
I also need the indexes. So, in my example I would like to get:
B' = [1 6 9], Idx = [1 4 5]
Note that the third value is 9. Indeed 6 is closer to 7 but it is already 'taken' since it is close to 4.
Any idea for a suitable code?
Note: my true arrays are much larger and contain real (not int) values
Also, it is given that B is longer then A
Thanks!
Assuming you want to minimize the overall discrepancies between elements of A and matched elements in B, the problem can be written as an assignment problem of assigning to every row (element of A) a column (element of B) given a cost matrix C. The Hungarian (or Munkres') algorithm solves the assignment problem.
I assume that you want to minimize cumulative squared distance between A and matched elements in B, and use the function [assignment,cost] = munkres(costMat) by Yi Cao from https://www.mathworks.com/matlabcentral/fileexchange/20652-hungarian-algorithm-for-linear-assignment-problems--v2-3-:
A = [1 5 7];
B = [1 2 3 6 9 10];
[Bprime,matches] = matching(A,B)
function [Bprime,matches] = matching(A,B)
C = (repmat(A',1,length(B)) - repmat(B,length(A),1)).^2;
[matches,~] = munkres(C);
Bprime = B(matches);
end
Assuming instead you want to find matches recursively, as suggested by your question, you could either walk through A, for each element in A find the closest remaining element in B and discard it (sortedmatching below); or you could iteratively form and discard the distance-minimizing match between remaining elements in A and B until all elements in A are matched (greedymatching):
A = [1 5 7];
B = [1 2 3 6 9 10];
[~,~,Bprime,matches] = sortedmatching(A,B,[],[])
[~,~,Bprime,matches] = greedymatching(A,B,[],[])
function [A,B,Bprime,matches] = sortedmatching(A,B,Bprime,matches)
[~,ix] = min((A(1) - B).^2);
matches = [matches ix];
Bprime = [Bprime B(ix)];
A = A(2:end);
B(ix) = Inf;
if(not(isempty(A)))
[A,B,Bprime,matches] = sortedmatching(A,B,Bprime,matches);
end
end
function [A,B,Bprime,matches] = greedymatching(A,B,Bprime,matches)
C = (repmat(A',1,length(B)) - repmat(B,length(A),1)).^2;
[minrows,ixrows] = min(C);
[~,ixcol] = min(minrows);
ixrow = ixrows(ixcol);
matches(ixrow) = ixcol;
Bprime(ixrow) = B(ixcol);
A(ixrow) = -Inf;
B(ixcol) = Inf;
if(max(A) > -Inf)
[A,B,Bprime,matches] = greedymatching(A,B,Bprime,matches);
end
end
While producing the same results in your example, all three methods potentially give different answers on the same data.
Normally I would run screaming from for and while loops in Matlab, but in this case I cannot see how the solution could be vectorized. At least it is O(N) (or near enough, depending on how many equally-close matches to each A(i) there are in B). It would be pretty simple to code the following in C and compile it into a mex file, to make it run at optimal speed, but here's a pure-Matlab solution:
function [out, ind] = greedy_nearest(A, B)
if nargin < 1, A = [1 5 7]; end
if nargin < 2, B = [1 2 3 6 9 10]; end
ind = A * 0;
walk = 1;
for i = 1:numel(A)
match = 0;
lastDelta = inf;
while walk < numel(B)
delta = abs(B(walk) - A(i));
if delta < lastDelta, match = walk; end
if delta > lastDelta, break, end
lastDelta = delta;
walk = walk + 1;
end
ind(i) = match;
walk = match + 1;
end
out = B(ind);
You could first get the absolute distance from each value in A to each value in B, sort them and then get the first unique value to a sequence when looking down in each column.
% Get distance from each value in A to each value in B
[~, minIdx] = sort(abs(bsxfun(#minus, A,B.')));
% Get first unique sequence looking down each column
idx = zeros(size(A));
for iCol = 1:numel(A)
for iRow = 1:iCol
if ~ismember(idx, minIdx(iRow,iCol))
idx(iCol) = minIdx(iRow,iCol);
break
end
end
end
The result when applying idx to B
>> idx
1 4 5
>> B(idx)
1 6 9

How to transform 'double' into 'cell array'?

My code:
B = zeros(height(A),1);
col_names = A.Properties.VariableNames; % Replicate header names
for k = 1:height(A)
% the following 'cellfun' compares each column to the values in A.L{k},
% and returns a cell array of the result for each of them, then
% 'cell2mat' converts it to logical array, and 'any' combines the
% results for all elements in A.L{k} to one logical vector:
C = any(cell2mat(...
cellfun(#(x) strcmp(col_names,x),A.L{k},...
'UniformOutput', false).'),1);
% then a logical indexing is used to define the columns for summation:
B(k) = sum(A{k,C});
end
generates the following error message.
Error using cellfun
Input #2 expected to be a cell array, was double instead.
How do I solve this error?
This is how table 'A' looks like:
A.L{1,1} contains:
C = any(cell2mat(...
cellfun(#(x) strcmp(col_names,x),A.L{k},...
'UniformOutput', false).'),1);
here A.L{k} gets the contents of the cell located at the kth position of A.L. Using A.L(k) you get the cell itself which is located at A.L:
tmp = A.L(k);
C = any(cell2mat(...
cellfun(#(x) strcmp(col_names,x),tmp{1},...
'UniformOutput', false).'),1);
Bit of a hacky way, as you first need to get the cell at A.L(k) and then need the contents of that cell, so you need a temporary variable.
I'm not entirely sure quite what's going on here, but here's a fabricated example that I think is similar to what you're trying to achieve.
%% Setup - fabricate some data
colNames = {'xx', 'yy', 'zz', 'qq'};
h = 20;
% It looks like 'L' contains something related to the column names
% so I'm going to build something like that.
L = repmat(colNames, h, 1);
% Empty some rows out completely
L(rand(h,1) > 0.7, :) = {''};
% Empty some other cells out at random
L(rand(numel(L), 1) > 0.8) = {''};
A = table(L, rand(h,1), rand(h, 1), rand(h, 1), rand(h, 1), ...
'VariableNames', ['L', colNames]);
%% Attempt to process each row
varNames = A.Properties.VariableNames;
B = zeros(height(A), 1);
for k = 1:height(A)
% I think this is what's required - work out which columns are
% named in "A.L(k,:)". This can be done simply by using ISMEMBER
% on the row of A.L.
C = ismember(varNames, A.L(k,:));
B(k) = sum(A{k, C});
end
If I'm completely off-course here, then perhaps you could give us an executable example.

indexing into an octave array using another array

Hi I have an three dimensional octave array A of size [x y z]
Now I have another array B of dimensions n * 3
say B(0) gives [3 3 1]
I need to access that location in A ie A(3, 3, 1) = say 15
something like A(B(0))
How do I go about it?
See the help for sub2ind (and ind2sub).
However, nowadays people recommend to use loops.
Well, first, B(0) is invalid index, as addressing in MATLAB and Octave begins from 1. Other issue is that you want that B(0) would contain a vector [3 3 1 ]. Matrices in MATLAB can not contain other matrices, only scalars. So you need to use a 3x3 cell array, a 3x3 struct or a 4-dimensional array. I'll choose here the cell array option, because I find it easiest and most convenient.
% Set random seed (used only for example data generation).
rng(123456789);
% Let's generate some pseudo-random example data.
A = rand(3,3,3);
A(:,:,1) =
0.5328 0.7136 0.8839
0.5341 0.2570 0.1549
0.5096 0.7527 0.6705
A(:,:,2) =
0.6434 0.8185 0.2308
0.7236 0.0979 0.0123
0.7487 0.0036 0.3535
A(:,:,3) =
0.1853 0.8994 0.9803
0.7928 0.3154 0.5421
0.6122 0.4067 0.2423
% Generate an example 3x3x3 cell array of indices, filled with pseudo-random 1x3 index vectors.
CellArrayOfIndicesB = cellfun(#(x) randi(3,1,3), num2cell(zeros(3,3,3)), 'UniformOutput', false);
% Example #1. Coordinates (1,2,3).
Dim1 = 1;
Dim2 = 2;
Dim3 = 3;
% The code to get the corresponding value of A directly.
ValueOfA = A(CellArrayOfIndicesB{Dim1,Dim2,Dim3}(1), CellArrayOfIndicesB{Dim1,Dim2,Dim3}(2), CellArrayOfIndicesB{Dim1,Dim2,Dim3}(3));
ValueOfA =
0.8839
% Let's confirm that by first checking where CellArrayOfIndicesB{1,2,3} points to.
CellArrayOfIndicesB{1,2,3}
ans =
[ 1 3 1 ]
% CellArrayOfIndicesB{1,2,3} points to A(1,3,1).
% So let's see what is the value of A(1,3,1).
A(1,3,1)
ans =
0.8839
% Example #2. Coordinates (3,1,2).
Dim1 = 3;
Dim2 = 1;
Dim3 = 2;
ValueOfA = A(CellArrayOfIndicesB{Dim1,Dim2,Dim3}(1), CellArrayOfIndicesB{Dim1,Dim2,Dim3}(2), CellArrayOfIndicesB{Dim1,Dim2,Dim3}(3));
ValueOfA =
0.4067
CellArrayOfIndicesB{3,1,2}
ans =
[ 3 2 3 ]
A(3,2,3)
ans =
0.4067

Resources