2010-08-17 6 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. Это мое текущее решение, но я чувствую, что есть способ векторизации его и он одинаково быстро: р

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

ответ

3

С сотрудничестве с Amro и Jonas

variable_ranks = tiedrank(variable_data')'; 

Ranker была заменена функцией Matlab в Stat инструментов (извините за тех, кто не имеет его),

[R, TIEADJ] = tiedrank (X) вычисляет ряды значений вектор X. Если любые значения X привязаны, tiedrank вычисляет их средний ранг. Возвращаемое значение TIEADJ - это регулировка для галстуков, требуемых непараметрическим сигналом , и для расчет ранга Спирмена .

TIEDRANK будет вычисляться по столбцам в Matlab 7.9.0 (R2009b), однако он не документирован. Поэтому, перенося входную матрицу, строки превращаются в столбцы и будут ранжировать их. Вторая транспозиция затем используется для организации данных таким же образом, как и вход. Там по существу очень классный взлом: p

+0

Если у вас нет инструментария stat, есть также второй аргумент sort, который возвращает индекс сортировки. –

+0

@Matt: Вот что я пробовал в своем ответе. Однако 'sort' не возвращает ранг. – Jonas

3

Если поместить матричные строки в массив ячеек, вы можете применить функцию к каждому клетка.

Рассмотрим простой пример применения функции SORT к каждой строке

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)'); 

Опять же, версия с цикл, вероятно, быстрее, ..

+0

не является ли ячейки по своей природе медленнее, чем массивы в Matlab? – Elpezmuerto

+0

Я не сказал, что это было быстрее :) – Amro

+0

+1 для обеспечения общего решения и для запоминания 'tiedrank' – Jonas

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); 

EDIT

Вместо этого, вы можете просто использовать TIEDRANK из TMW (спасибо, @Amro):

variable_rank = tiedrank(variable_data')'; 
+0

Я предполагаю, что я думал об общем решении, которое применимо к любой функции. – Amro

+0

@Amro: да, ваше решение безусловно, более общий. Mine может быть быстрее (хотя я не знаю, как выглядит 'ranker') – Jonas

+0

@Jones ... Это не сработает, потому что он неправильно назначает связи, но будет произвольно оценивать их, см. Мой второй пример, пожалуйста, – Elpezmuerto

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 

Я надеюсь, что это поможет кому-то.