How to find indices of the largest N elements in a cell? - matlab

I Matlab, I know that I can use this to get the largest number of a cell.
cell_max = cellfun(#(x) max(x(:)), the_cell);
However, there are two problems with this. First, I need the index of the maximum values as well. Second, I need not the single largest value of each cell, but its N largest values.
Is this possible with cells in Matlab?
Update: I have a pixel matrix that I get by running a filter on some input image file. From that matrix, I then split this matrix into tiles and want to keep only the N largest values per tile, while all other entries should be set to zero. (So I don't need the indices in the end, but they would allow me to create a new empty cell and copy over the large values.)
Tiles = mat2tiles(FilterResult, tileSize, tileSize);
If there is an easier way for my use case then using the mat2tiles script, I'd be grateful to know.

The routine cellfun can return the multiple arguments of the function you're passing in (see the documentation). So, assuming each cell contains a numeric vector of values, you can obtain the N largest elements of each cell like this:
% Using random data for the_cell
the_cell{1, 1} = rand(1, 12);
the_cell{1, 2} = rand(1, 42);
the_cell{2, 1} = rand(1, 18);
the_cell{2, 2} = rand(1, 67);
% First sort elements in each cell in descending order and keep indices
[s, i] = cellfun(#(x)sort(x(:), 'descend'), the_cell, 'UniformOutput', false);
% Then, in each resulting `s` and `i` cell arrays,
% take only the `N` first elements and indices
N = 4;
NLargestValues = cellfun(#(x)x(1:N), s, 'UniformOutput', false);
NLargestIndices = cellfun(#(x)x(1:N), i, 'UniformOutput', false);
NB: UniformOutput is set to false because outputs are not scalar.
Update
From your updates and comments we had, you can put all operations in some tileOperations function:
% Operation to perform for each tile in the_cell array
function [tile] = tileOperations(tile, N)
%[
% Return unique sorted values of the tile
v = unique(tile(:));
% Find threshold index
i = length(v) - N;
if (i <= 1), return; end % Quick exit if not enough elements
% Set elements below threshold to zero
threshold = v(i);
tile(tile < threshold) = 0.0;
%]
end
You can then call cellfun only once to repetitively apply operations on all tiles in the_cell:
filteredTiles = cellfun(#(x)tileOperations(x), the_cell, 'UniformOutput', false);

Related

Accelerating the index in huge sparse matrix during loop in MATLAB

I need to construct a huge sparse matrix in iterations. The code is as follow:
function Huge_Matrix = Create_Huge_Matrix(len, Weight, Index)
k = size(Weight,1);
Huge_Matrix = spalloc(len, len,floor(len*k));
parfor i = 1:len
temp = sparse(1,len);
ind = Index(:,i);
temp(ind) = Weight(:,i);
Huge_Matrix(i,:) = temp;
end
Huge_Matrix = Huge_Matrix + spdiags(-k*ones(len,1),0,len,len);
end
As is shown, len is size of the height * weight of the input image, for 200*200 image, the len is 40000! And I am assigning the Weight into this huge matrix according the position stored in Index. Even though I use parfor to accerlate the loop, the speed is very slow.
I also try to create full matrix at first, it seems that the code can becomes faster, but memory is limited. Is there any other way to speed up the code? Thanks in advance!
As #CrisLuengo says in the comments, there is probably a better way to do what you're trying to do than to create a 40kx40k matrix, but if you have to create a large sparse matrix, it's better to let MATLAB do it for you.
The sparse function has a signature that takes lists of rows, columns and the corresponding values for the nonzero elements of the matrix:
S = sparse(i,j,v) generates a sparse matrix S from the triplets i, j, and v such that S(i(k),j(k)) = v(k). The max(i)-by-max(j) output matrix has space allotted for length(v) nonzero elements. sparse adds together elements in v that have duplicate subscripts in i and j.
If the inputs i, j, and v are vectors or matrices, they must have the same number of elements. Alternatively, the argument v and/or one of the arguments i or j can be scalars.
So, we can simply pass Index as the row indices and Weight as the values, so all we need is an array of column indices the same size as Index:
col_idx = repmat(1:len, k, 1);
Huge_Matrix = sparse(Index, col_idx, Weight, len, len);
(The last two parameters specify the size of the sparse matrix.)
The next step is to create another large sparse matrix and add it to the first. That seems kind of wasteful, so why not just add those entries to the existing arrays before creating the matrix?
Here's the final function:
function Huge_Matrix = Create_Huge_Matrix(len, Weight, Index)
k = size(Weight,1);
% add diagonal indices/weights to arrays
% this avoids creating second huge sparse array
Index(end+1, :) = [1:len];
Weight(end+1, :) = -k*ones(1,len);
% create array of column numbers corresponding to each Index
% make k+1 rows because we've added the diagonal
col_idx = repmat(1:len, k+1, 1);
% let sparse do the work
Huge_Matrix = sparse(Index, col_idx, Weight, len, len);
end

How to sort all elements of a 2d matrix in MATLAB?

The sort() function sorts the elements row/column wise but how to sort the elements absolutely? The result should be another matrix with smallest element in (1,1) , second smallest in (1,2) and so on.
Take some random input
input = rand(5,10);
If you want the output to be a row vector, simply use
sortedRow = sort(input(:)).';
If you want the result to be the same shape as the input, then use
sortedOriginalShape = reshape(sort(input(:)), size(input,2), size(input,1)).';
Note that when maintaining the shape, we must use the reversed size dimensions and then transpose. This is because otherwise the result is column-wise i.e. smallest element in (1,1), next in (2,1) etc, which is the opposite of what you requested.
You can use the column operator (:) to vectorize all elements of 'nxm' matrix as a vector of 'nxm' elements and sort this vector. Then you can use direct assignement or 'reshape' function to store elements as matricial form.
All you need to know is that matlab use column-major-ordering to vectorize/iterate elements:
A = rand(3, 5);
A(:) = sort(A(:);
Will preserve colum-major-ordering, or as you said you prefer row-major ordering:
A = rand(3, 5);
A = reshape(sort(A(:)), fliplr(size(A)).';
Note the fliplr to store columnwise with reversed dimension and then the .' operator to transpose again the result.
EDIT
Even if matlab uses column-major-ordering for storing elements in memory, here below are two generic routines to work with row-major-order whatever the number of dimension of your array (i.e. no limited to 2D):
function [vector] = VectorizeWithRowMajorOrdering(array)
%[
axisCount = length(size(array)); % Squeezed size of original array
permutation = fliplr(1:(axisCount + 2)); % +2 ==> Trick to vectorize data in correct order
vector = permute(array, permutation);
vector = vector(:);
%]
end
function [array] = ReshapeFromRowMajorOrdering(vector, siz)
%[
siz = [siz( : ).' 1]; % Fix size if only one dim
array = NaN(siz); % Init
axisCount = length(size(array)); % Squeezed size!
permutation = fliplr(1:(axisCount + 2)); % +2 ==> Trick to vectorize data in correct order
array = reshape(vector, [1 1 fliplr(size(array))]);
array = ipermute(array, permutation);
%]
end
This can be useful when working with data coming from C/C++ (these languages use row-major-ordering). In your case this can be used this way:
A = rand(3, 5);
A = ReshapeFromRowMajorOrdering(sort(A(:)), size(A));

How to shuffle the rows in a cell array

I have a cell array with x columns, each with a yx1 cell. I would like to randomize the "rows" within the columns. That is, for each yx1 cell with elements a_1, a_2, ... a_y, I would like to apply the same permutation to the indices of a_i.
I've got a function that does this,
function[Oarray] = shuffleCellArray(Iarray);
len = length(Iarray{1});
width = length(Iarray);
perm = randperm(len);
Oarray=cell(width, 0);
for i=1:width;
for j=1:len;
Oarray{i}{j}=Iarray{i}{perm(j)};
end;
end;
but as you can see it's a bit ugly. Is there a more natural way to do this?
I realize that I'm probably using the wrong data type, but for legacy reasons I'd like to avoid switching. But, if the answer is "switch" then I guess that's the answer.
I'm assuming you have a cell array of column vectors, such as
Iarray = {(1:5).' (10:10:50).' (100:100:500).'};
In that case, you could do it this way:
ind = randperm(numel(Iarray{1})); %// random permutation
Oarray = cellfun(#(x) x(ind), Iarray, 'UniformOutput', 0); %// apply that permutation
%// to each "column"
Or converting to an intermediate matrix and then back to a cell array:
ind = randperm(numel(Iarray{1})); %// random permutation
x = cat(2,Iarray{:}); %// convert to matrix
Oarray = mat2cell(x(ind,:), size(x,1), ones(1,size(x,2))); %// apply permutation to rows
%// and convert back

Accessing Ranges of Data in Vectorized Way MATLAB

I have a column vector of data in variable vdata and a list of indeces idx. I want to access vdata at the indeces x before and x after each index in idx. One way I would do it in a for loop is:
x = 10;
accessed_data = [];
for (ii = 1:length(idx))
accessed_data = vdata(idx-x:idx+x);
end
Is there a way to do this in a vectorized function? I found a solution to a very similar question here: Addressing multiple ranges via indices in a vector but I don't understand the code :(.
Assuming min(idx)-x>0 and max(idx)+x<=numel(vdata) then you can simply do
iidx = bsxfun(#plus, idx(:), -x:x); % create all indices
accessed_data = vdata( iidx );
One scheme that uses direct indexing instead of a for loop:
xx = (-x:x).'; % Range of indices
idxx = bsxfun(#plus,xx(:,ones(1,numel(idx))),idx(:).'); % Build array
idxx = idxx(:); % Columnize to interleave columns
idxx = idxx(idxx>=1&idxx<=length(vdata)); % Make sure the idx+/-x is valid index
accessed_data = vdata(idxx); % Indices of data
The second line can be replaced with a form of the first line from #Shai's answer. This scheme checks that all of the resultant indices are valid. Because some might have to be removed, you could end up with a ragged array. One way to solve this is to use cell arrays, but here I just make idxx a vector, and thus accessed_data is as well.
This gives the solution in a matrix, with one row for each value in idx. It assumes that all values in idx are greater than or equal to x, and less than or equal to length(vdata)-x.
% Data
x = 10;
idx = [12 20 15];
vdata = 1:100;
ind = repmat(-x:x,length(idx),1) + repmat(idx(:),1,2*x+1);
vdata(ind)

Building a map from a matrix in Matlab

I have a matrix A which holds integers in a bounded range (0..255) and I need to build a table mapping a value (0..255) to all the coordinates in the matrix which hold this value.
What is the best way to achieve this? - I thought about using containers.Map for the task but Map doesn't support multiple values per key. I could have used lists but that would seem inefficient as I would have to create a new list on each iteration.
A vectorized solution, which gives the same output as the solution from Mikhail, is to sort all the pixel values in your image using the SORT function, convert the linear indices returned from SORT into subscripted indices using the function IND2SUB, and collect them together into a single cell array using the functions ACCUMARRAY and MAT2CELL:
A = randi([0 255],[5 5],'uint8'); %# A sample matrix
[values,indices] = sort(double(A(:))); %# Sort all the pixel values
[y,x] = ind2sub(size(A),indices); %# Convert linear index to subscript
counts = accumarray(values+1,1,[256 1]); %# Count number of each value
map = mat2cell([y x],counts); %# Create a 256-by-1 cell array
Now, for a given integer value iValue you can get the N-by-2 matrix containing the y (first column) and x (second column) coordinates for the N pixels in the image with that value by doing the following:
key = double(iValue)+1; %# Need to use double to avoid integer saturation
points = map{key}; %# An N-by-2 coordinate matrix
In addition, just in case you're interested, you could also make map a structure array with fields x and y using the function STRUCT:
map = struct('x',mat2cell(x,counts),'y',mat2cell(y,counts));
And you can then access the x and y coordinates for pixels with a value iValue as follows:
key = double(iValue)+1;
x = map(key).x;
y = map(key).y
What about using a cell array? You can index it with integers. For example:
map = {[1,1;13,56], [], [4,5]};
In this example index 0 is in the matrix in 1,1 and 13,56, index 1 in none and index 2 in 4,5
Your cell would have 256 elements (mine has 3) and to acces you would simply add 1 to the index.
You could also store indices linearly so the code to fill the table would be:
for ii = 0:255
map{ii+1} = find( mat(:)==ii )
end
Well, I wrote the following and it seems to work in reasonable time. I think the thing that does the trick is preallocating the cell arrays based on the histogram for each value:
[H, W] = size(A);
histogram = hist(A, 256);
AGT = arrayfun(#(avg) {0 cell(1, histogram(avg))}, 1:256, 'UniformOutput', false);
for y = 1:H
for x = 1:W
idx = A(y, x) + 1;
count = AGT{idx}{1};
AGT{idx}{2}{count + 1} = [y x];
AGT{idx}{1} = count + 1;
end
end
Accessing the table is a bit annoyting though :
AGT{200}{2}{:}
to access all coordinates with value 200.