我在MATLAB中有一个单元格数组,它包含一些降序大小的矩阵(例如[512x512 double] [256x256 double] [128x128 double]等。
现在我想找到并擦除(变成零)单元格数组中矩阵中的100个最小元素完全(不是单独在每个矩阵中)。例如,最小元素可能在128x128矩阵中,下一个在512x512矩阵中,依此类推。
我怎样才能最有效地做到这一点?
下一个是我的超慢速代码,用于三个这样的单元阵列--H,D,V(percent2zero
是从整个单元阵列中擦除的元素百分比的参数):
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
答案 0 :(得分:2)
我可以建议的一件事是将所有矩阵上的每个单值放入单个1D向量中,对向量进行排序并选择100个最小值。使用cellfun
和sort
执行此操作。这背后的逻辑是,如果我们收集所有矩阵的所有值并将它们放入单个向量然后对整个向量进行排序,我们将根据您的提及选择100个最小值 all all 。
一旦找到这100个最小值,就必须遍历每个单元格然后你可以使用intersect
来查看单元格数组中每个矩阵中是否有与100个最小值相同的元素值。您可以使用intersect
的第二个输出,然后使用它来索引每个单元格并将值设置为0.因为您正在处理单元格并且每个单元格的大小不同,所以循环是唯一的选择。
想到这样的事情。这假设details
是一个单元格数组,其中每个单元格都是一个矩阵:
%// 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
在数组或矩阵中查找唯一值,但也可以对唯一值进行排序。因此,此代码将找到100个最小且唯一值来处理您的单元格数组。
答案 1 :(得分:1)
我一直在摆弄这一点,基本上想出了与rayryeng相同的东西。我们的想法是将每个矩阵重新整形为一维向量,对每个向量进行排序并跟踪原始索引,然后开始将1D向量开头的最小元素归零,使用未排序的1D向量的索引返回到2D矩阵并将这些元素归零。我认为它将1D向量视为队列。无论如何,我编写了一个函数,它将遵循这个过程,包含3个矩阵的单元格数组。对于具有任意数量矩阵的单元阵列来说,它并不漂亮或优化或推广,但我认为改进它并不困难。
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
答案 2 :(得分:-3)
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