具有相同标签的列的平均值

Poe*_*lie 2 optimization matlab vector vectorization mean

我有两个向量

data vector: A = [1 2 2 1 2 6; 2 3 2 3 3 5]
label vector: B = [1 2 1 2 3 NaN]
Run Code Online (Sandbox Code Playgroud)

我想取所有具有相同标签的列的平均值,并将它们输出为按标签号排序的矩阵,忽略NaN.所以,在这个例子中我想要:

labelmean(A,B) = [1.5 1.5 2; 2 3 3]
Run Code Online (Sandbox Code Playgroud)

这可以通过像这样的for循环来完成.

function out = labelmean(data,label)
out=[];
for i=unique(label)
    if isnan(i); continue; end
    out = [out, mean(data(:,label==i),2)];
end 
Run Code Online (Sandbox Code Playgroud)

但是,我正在处理包含许多数据点和标签的巨大数组.此外,此代码段将经常执行.我想知道是否有更有效的方法来做到这一点,而不是遍历每个单独的标签.

ray*_*ica 5

这将是一个很好的使用案例accumarray.可以将其accumarray视为一种微型MapReduce范例.有键和值,所以工作accumarray是将所有共享相同键的值组合在一起,并对这些值执行某些操作.在您的情况下,键将是元素,B但值将是相应值所需的行位置B.基本上,对于每个值B,位置B告诉您需要访问哪一行A.因此,我们只需要获取映射到相同ID的所有行位置,访问行A,然后查找所有行的平均值.我们需要小心,因为我们忽略了它们的价值NaN.我们可以在打电话之前过滤掉这些accumarray.accumarray传统上你做的"东西" 应该输出一个数字,但实际上我们为每个标签输出一个列向量.因此,一个技巧是将输出包装到单元格数组中,然后cat结合使用逗号分隔列表将输出转换为矩阵.

因此,这样的事情应该有效:

% Sample data
A = [1 2 2 1 2 6; 2 3 2 3 3 5];
B = [1 2 1 2 3 NaN];

% Find non-NaN locations
mask = ~isnan(B);

% Generate row locations that are not NaN as well as the labels
ind = 1 : numel(B);
Bf = B(mask).';
ind = ind(mask).';

% Find label-wise means
C = accumarray(Bf, ind, [], @(x) {mean(A(:,x), 2)});

% Convert to numeric matrix
out = cat(2, C{:});
Run Code Online (Sandbox Code Playgroud)

如果您不喜欢使用临时变量来查找这些非NaN值,我们可以使用较少的代码行来完成此操作,但您仍需要行索引向量来确定我们需要从哪里进行采样:

% Sample data
A = [1 2 2 1 2 6; 2 3 2 3 3 5];
B = [1 2 1 2 3 NaN];

% Solution
ind = 1 : numel(B);
C = accumarray(B(~isnan(B)).', ind(~isnan(B)).', [], @(x) {mean(A(:,x), 2)});
out = cat(2, C{:});
Run Code Online (Sandbox Code Playgroud)

根据您的数据,我们得到:

>> out

out =

    1.5000    1.5000    2.0000
    2.0000    3.0000    3.0000
Run Code Online (Sandbox Code Playgroud)


Lui*_*ndo 5

这是一种方法:

  1. 获取不包含NaNs 的标签索引.
  2. 创建一个零稀疏矩阵,乘以一个A将得到所需行总和的稀疏矩阵.
  3. 将该矩阵除以每列的总和,使得总和成为平均值.
  4. 应用矩阵乘法来获得结果,并转换为完整矩阵.

码:

I = find(~isnan(B));                                 % step 1
t = sparse(I, B(I), 1, size(A,2), max(B(I)));        % step 2
t = bsxfun(@rdivide, t, sum(t,1));                   % step 3
result = full(A*t);                                  % step 4
Run Code Online (Sandbox Code Playgroud)