2010-08-17 73 views
2

我有一个函数ranker,该函数接受一个向量并按升序为其分配数字等级。例如,
ranker([5 1 3 600]) = [3 1 2 4]
ranker([42 300 42 42 1 42] = [3.5 6 3.5 3.5 1 3.5]将函数应用于所有行

我正在使用矩阵,variable_data,我想对variable data中的所有行的每一行应用排名函数。这是我目前的解决办法,但我觉得有一种方法向量化它,并把它作为同样快:P

variable_ranks = nan(size(variable_data)); 
for i=1:1:numel(nmac_ids) 
    variable_ranks(i,:) = ranker(abs(variable_data(i,:))); 
end 

回答

3

与荷银和Jonas

variable_ranks = tiedrank(variable_data')'; 
合作

排名器已取代在统计工具箱(抱歉那些没有它谁)MATLAB函数,

[R,TIEADJ] = tiedrank(X)计算的值的 行列矢量X. 如果有任何X值相关,tiedrank 将计算它们的平均等级。 返回值TIEADJ是一个调整 为非参数 测试signrank和rankingum所需的关系,并且 计算Spearman的等级 的相关性。

TIEDRANK将在Matlab 7.9.0(R2009b)中沿着列进行计算,但它是无证的。所以通过转置输入矩阵,行会变成列并将它们排序。第二个转置然后用于以与输入相同的方式组织数据。本质上是一个非常优雅的黑客:p

+0

如果你没有stat工具箱,那么还有第二个参数sort,它返回排序索引。 – 2010-08-17 21:07:26

+0

@Matt:这就是我在我的回答中所尝试的。但是,“排序”不会返回排名。 – Jonas 2010-08-17 22:15:25

3

如果将矩阵的行成一个单元阵列,就可以应用到每个功能细胞。

考虑应用排序函数每一行的这个简单的例子

a = rand(10,3); 
b = cell2mat(cellfun(@sort, num2cell(a,2), 'UniformOutput',false)); 
%# same as: b = sort(a,2); 

你甚至可以做到这一点:

b = cell2mat(arrayfun(@(i) sort(a(i,:)), 1:size(a,1), 'UniformOutput',false)'); 

同样,你的版本与for循环可能更快..

+0

是不是细胞本身比Matlab中的数组慢? – Elpezmuerto 2010-08-17 19:24:34

+0

我没有说这是更快:) – Amro 2010-08-17 19:26:14

+0

为+1提供一般的解决方案,并记住'tiedrank' – Jonas 2010-08-17 22:16:53

2

一种方法是重写ranker采取数组输入

sizeData = size(variable_data); 

[sortedData,almostRanks] = sort(abs(variable_data),2); 
[rowIdx,colIdx] = ndgrid(1:sizeData(1),1:sizeData(2)); 
linIdx = sub2ind(sizeData,rowIdx,almostRanks); 
variable_ranks = variable_data; 
variable_ranks(linIdx) = colIdx; 

%# break ties by finding subsequent equal entries in sorted data 
[rr,cc] = find(diff(sortedData,1,2) == 0); 
ii = sub2ind(sizeData,rr,cc); 
ii2 = sub2ind(sizeData,rr,cc+1); 
ii = sub2ind(sizeData,rr,almostRanks(ii)); 
ii2 = sub2ind(sizeData,rr,almostRanks(ii2)); 
variable_ranks(ii) = variable_ranks(ii2); 

编辑

相反,你可以只使用TIEDRANK从TMW(感谢,@Amro):

variable_rank = tiedrank(variable_data')'; 
+0

我想我正在考虑一个一般的解决方案,将适用于任何功能.. – Amro 2010-08-17 19:28:22

+0

@Amro:是的,你的解决方案当然更一般。我的速度可能会更快(虽然我不知道'ranker'是什么样子的) – Jonas 2010-08-17 19:36:53

+0

@Jones ...这不起作用,因为它没有正确分配关系,但会任意评定它们,请参阅我的第二个示例 – Elpezmuerto 2010-08-17 19:37:55

1

我写了一个函数来做到这一点,它在FileExchange tiedrank_(X,dim)上。它看起来像这样...

%[Step 0a]: force dim to be 1, and compress everything else into a single 
%dimension. We will reverse this process at the end. 
if dim > 1 
    otherDims = 1:length(size(X)); 
    otherDims(dim) = []; 
    perm = [dim otherDims]; 
    X = permute(X,perm); 
end 
originalSiz = size(X); 
X = reshape(X,originalSiz(1),[]); 
siz = size(X); 

%[Step 1]: sort and get sorting indicies 
[X,Ind] = sort(X,1); 

%[Step 2]: create matrix [D], which has +1 at the start of consecutive runs 
% and -1 at the end, with zeros elsewhere. 
D = zeros(siz,'int8'); 
D(2:end-1,:) = diff(X(1:end-1,:) == X(2:end,:)); 
D(1,:) = X(1,:) == X(2,:); 
D(end,:) = -(X(end,:) == X(end-1,:)); 

clear X 

%[Step 3]: calculate the averaged rank for each consecutive run 
[a,~] = find(D); 
a = reshape(a,2,[]); 
h = sum(a,1)/2; 

%[Step 4]: insert the troublseome ranks in the relevant places 
L = zeros(siz); 
L(D==1) = h; 
L(D==-1) = -h; 
L = cumsum(L); 
L(D==-1) = h; %cumsum set these ranks to zero, but we wanted them to be h 

clear D h 

%[Step 5]: insert the simple ranks (i.e. the ones that didn't clash) 
[L(~L),~] = find(~L); 

%[Step 6]: assign the ranks to the relevant position in the matrix 
Ind = bsxfun(@plus,Ind,(0:siz(2)-1)*siz(1)); %equivalent to using sub2ind + repmat 
r(Ind) = L; 

%[Step 0b]: As promissed, we reinstate the correct dimensional shape and order 
r = reshape(r,originalSiz); 
if dim > 1 
    r = ipermute(r,perm); 
end 

我希望能帮助别人。

相关问题