【问题标题】:Apply function to all rows将函数应用于所有行
【发布时间】:2010-08-17 19:13:49
【问题描述】:

我有一个函数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

【问题讨论】:

    标签: matlab vectorization


    【解决方案1】:

    如果将矩阵行放入元胞数组中,则可以对每个元胞应用一个函数。

    考虑这个将 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)' );
    

    同样,您使用 for 循环的版本可能更快..

    【讨论】:

    • 在 Matlab 中,单元格不是天生就比数组慢吗?
    • 我没说这更快:)
    • +1 用于提供通用解决方案,并用于记住 tiedrank
    【解决方案2】:

    在 Amro 和 Jonas 的合作下

    variable_ranks = tiedrank(variable_data')';
    

    Ranker 已被 Stat 工具箱中的 Matlab 函数取代(抱歉没有的人),

    [R,TIEADJ] =tierank(X) 计算 向量 X 中的值的等级。 如果任何 X 值是并列的,tiedrank 计算他们的平均排名。这 返回值 TIEADJ 是一个调整 对于非参数所需的关系 测试 signrank 和ranksum,并且对于 斯皮尔曼等级的计算 相关性。

    TIEDRANK 将沿 Matlab 7.9.0 (R2009b) 中的列进行计算,但未记录。因此,通过转置输入矩阵,行变成列并对它们进行排序。然后使用第二个转置以与输入相同的方式组织数据。本质上是一个非常经典的黑客:p

    【讨论】:

    • 如果你没有统计工具箱,还有 sort 的第二个参数返回排序索引。
    • @Matt:这就是我在回答中所尝试的。但是,sort 不会返回排名。
    【解决方案3】:

    一种方法是重写 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);
    

    编辑

    相反,您可以只使用来自 TMW 的 TIEDRANK(感谢 @Amro):

    variable_rank = tiedrank(variable_data')';
    

    【讨论】:

    • @Amro:是的,您的解决方案肯定更通用。我的可能会更快(虽然我不知道ranker 是什么样子)
    • @Jones...这不起作用,因为它没有正确分配关系,但会任意评价它们,请参阅我的第二个示例
    • @Elpezmuerto:我想我必须添加一个决胜局:)
    • 您要查找的函数是 TIEDRANK:mathworks.com/access/helpdesk/help/toolbox/stats/tiedrank.html 例如:r = tiedrank(M')'; 将其应用于矩阵的行 M
    • @Amro:哎呀,要是你再快一点就好了!不管怎么说,还是要谢谢你。但是,tierank 也不适用于数组。
    【解决方案4】:

    我编写了一个函数来执行此操作,它位于 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
    

    我希望对某人有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-09-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-28
      • 2017-05-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多