2
votes

I have a cell array in MATLAB which contains some matrices in a descending size order (e.g. [512x512 double] [256x256 double] [128x128 double] etc).

Now I want to find and wipe (turn into zeros) the 100 smallest elements in the matrices in the cell array altogether (not in each matrix individually). For example, the smallest element might be in the 128x128 matrix, the next in the 512x512 matrix and so on.

How can I do it most efficiently?

The next is my super-slow code for the case of three such cell arrays - H,D,V (percent2zero is a parameter for the percent of elements to wipe from the whole cell array):

details=[H;D;V];
for k=1:3
    numOfLevels=length(details(k,:));
    TotalCoeffs=0;
    minimalInEachLevel=zeros(1,numOfLevels);
    for i=1:numOfLevels
        temp=cell2mat(details(k,i));
        TotalCoeffs=TotalCoeffs+numel(temp);
        minimalInEachLevel(i) = min(min(abs(temp)));
    end
    CoeffsToBeRemoved=percent2zero*TotalCoeffs/100;
    for i=1:CoeffsToBeRemoved
        for j=1:numOfLevels
            temp=cell2mat(details(k,j));
            minimalInEachLevel(j) = min(min(abs(temp)));
        end
        [val,ind]=min(minimalInEachLevel);
        temp=cell2mat(details(k,ind));
        temp(find(abs(temp==val),1))=0;
        details(k,ind)=mat2cell(temp,size(temp,1),size(temp,2));        
    end
end
3
Take a look at cellfun - Adriaan
Hi...so what's up? Did we help you? - rayryeng
If one of the answers has solved your question please consider accepting it by clicking the check-mark. This indicates that you've found a solution and this is no longer an open question. If your question is not answered, please explain what’s missing. - Daniel

3 Answers

2
votes

One thing I can suggest is to place every single value over all matrices into a single 1D vector, sort the vector and choose the 100 smallest values. Use cellfun and sort to do this first step. The logic behind this is that if we collect all of the values over all matrices and place them into a single vector then sort the entire vector, we will choose the 100 smallest values all together as you have mentioned.

Once you find these 100 smallest values, you'll have to loop over each cell then you can use intersect to see if there are any elements in each matrix in the cell array that are common with the 100 smallest values. You can use the second output of intersect then use this to index into each cell and set the values to 0. Because you're dealing with cells and each cell is a different size, looping is the only option.

Something like this comes to mind. This is assuming that details is a cell array where each cell is a matrix:

%// Create single 1D vector of all values
vals = cellfun(@(x) reshape(x, [], 1), details, 'uni', 0);
vals = cat(1, vals{:});

%// Sort the UNIQUE values and grab the 100 smallest values
vals = unique(vals);
vals = vals(1:100);

%// Loop through each cell, determine those values that are among
%// the 100 smallest and set to 0
for ii = 1 : numel(details)
    [~,ind,~] = intersect(details{ii}, vals);
    details{ii}(ind) = 0;
end

unique finds the unique values in an array or matrix but also has the added bonus of sorting the unique values too. Therefore, this code will find the 100 smallest and unique values to process your cell arrays.

1
votes

I've been fiddling with this for a little bit and basically came up with the same thing as rayryeng. The idea is to reshape each matrix into a 1D vector, sort each of the vectors and keep track of the original indices, then start zeroing the smallest elements at the beginning of the 1D vectors, using the indices of the unsorted 1D vector to get back to the 2D matrix and zero those elements. It sort of treats the 1D vectors as queues, I think. Anyway, I worked up a function that will follow this process for a cell array containing 3 matrices. It's not pretty or optimized or generalized for a cell array with any number of matrices, but I don't think it would be that tough to improve.

function [X] = wipeCellMinima(X,percent2zero)
%wipeCellMinima

x1 = X{1};
x2 = X{2};
x3 = X{3};

l1 = length(x1);
l2 = length(x2);
l3 = length(x3);

%reshape each matrix into a 1D vector and sort them, keeping sorted indices
[q1,I1] = sort(reshape(x1,1,l1^2));
[q2,I2] = sort(reshape(x2,1,l2^2));
[q3,I3] = sort(reshape(x3,1,l3^2));

%total number of elements to be "wiped"
n = fix((l1^2 + l2^2 + l3^2)*percent2zero*0.01); 

i1 = 1;
i2 = 1;
i3 = 1;

%treat the reshaped and sorted matrices kind of like queues
for j = 1:n
    %Find the smallest value indexed in sort1 by i1, in sort2 by i2,
    %and in sort3 by i3.
    [~,idx_min] = min([q1(i1),q2(i2),q3(i3)]);
    if(idx_min == 1)
        %The smallest element in all matrices is the first one in the
        %sorted version of x1. Use it's index in the unsorted version of q1
        %to set the corresponding element in x1 to zero. And increment i1.

        %In the unsorted version of q1, the I1(i1)th element needs to be
        %set to zero. The correct row and column can be extracted from the
        %size of the original matrix x1 and the position of the element in
        %the 1D vector.
        row = mod(I1(i1),l1);
        if(row == 0)
            row = l1;
            col = I1(i1)/l1;
        else
            col = floor(I1(i1)/l1) + 1;
        end
        x1(row,col) = 0;
        i1 = i1 + 1;
    elseif(idx_min == 2)
        row = mod(I2(i2),l2);
        if(row == 0)
            row = l2;
            col = I2(i2)/l2;
        else
            col = floor(I2(i2)/l2) + 1;
        end
        x2(row,col) = 0;
        i2 = i2 + 1;
    else
        row = mod(I3(i3),l3);
        if(row == 0)
            row = l3;
            col = I3(i3)/l3;
        else
            col = floor(I3(i3)/l3) + 1;
        end
        x3(row,col) = 0;
        i3 = i3 + 1;
    end
end

X = {x1,x2,x3};

end
-3
votes
n = sum(cellfun('prodofsize',details),2);
CoeffsToBeRemoved = round(percent2zero/100*n);
for i = 1:size(details,1)
    tmp = cellfun(@(x)x(:),details(i,:),'un',0);
    tmp = sort(abs(vertcat(tmp{:})));
    val = tmp(CoeffsToBeRemoved(i));
    for j = 1:size(details,2)
        details{i,j} = details{i,j}.*(abs(details{i,j})>val);
    end
end