【问题标题】:Grouping by nested unique values按嵌套唯一值分组
【发布时间】:2017-06-20 03:36:35
【问题描述】:

我在 Matlab 中有一个矩阵 A:

A = [176  5406  1  4  7903;
     155  5406  1  5  7903;
     122  5407  0  4  7903;
     140  5407  0  5  7904;
     130  5407  0  3  7904];

仅供参考 - 第二列是用户 ID,而第四列是时间。所以5406 是一个用户,5407 是另一个用户。这两个用户都有一些信息存储在我有兴趣访问的第一列和第四列中。

所以基本上我想做的是:

  1. 对于每个用户,在第一列中取其值的中值。我已经编写了适用于此的代码(如下)。

  2. 如果每个用户在第 5 列中有两个相等的“时间”值,那么我想对第 4 列中的值进行平均。所以对于用户 5406,时间值都是 7903,我想要到第 4 列中值的平均值 - 即 45 的平均值最终得到一个值 (4.5)。

但是例如对于下一个用户5407,我将有两个最终值 - 一个是 5 和 3 的平均值(因为 7904 重复),一个是 4(因为 7903 不重复)。

我对如何做到这一点有点困惑,我知道需要有某种if 声明,但我已经坚持了很长时间。有人可以帮忙吗?

谢谢

第一部分的代码:

u=unique(A(:,2));
for i=1:size(u,1)
    M=find(A(i,2)==u(i));
    med(i)=median(A(M,1));
end

【问题讨论】:

  • 我会使用逻辑索引,例如,对于您想做的第一件事,您可以使用:median(A((A(:,2)==5406),1)) 对于第一个用户等...
  • 为了这个问题,我刚刚缩小了矩阵,但我有大约 20,000 个用户,我无法列出每个用户...
  • 但您可以循环遍历示例中编写的u...只需将值 5406 替换为 u(i)

标签: matlab for-loop matrix duplicates unique


【解决方案1】:

您可以为每个用户的时间值(在循环内)运行 unique,并执行类似的子循环来收集该用户唯一时间戳的平均值。

但在这里我认为使用accumarray 更简洁。在下面的第一个示例中,我稍微修改了您的代码。

% Get unique
[user, ~, userIdx] = unique(A(:,2));
nUser = numel(user);

% Allocate container for result
med = zeros(nUser,1);
men = cell(nUser,1); % <-- Need a cell since length of result could vary

for i = 1:nUser
    % Median of col #1
    med(i) = median(A(userIdx == i, 1));

    % Mean of col #4 for unique times
    [~, ~, timeIdx] = unique(A(userIdx == i, 5));
    men{i} = accumarray(timeIdx, A(userIdx == i, 4), [], @mean);
end

结果:

>> med =
        165.5
          130

>> celldisp(men)
men{1} =
          4.5
men{2} =
          4
          4

为了进一步压缩它,您可以为整个 A 使用独特的时间,并为两者使用 accumarray

[~, ~, userIdx] = unique(A(:,2));
[~, ~, timeIdx] = unique(A(:,5));

med = accumarray(userIdx, A(:,1), [], @median);
men = accumarray([userIdx timeIdx], A(:,4), [], @mean, NaN);

这使得men 不是一个单元格而是一个矩阵。因此必须填充空格(这里我选择NaN,因为0 可能是@mean 的结果)。

men =
          4.5          NaN
            4            4

如果您希望它作为没有NaN 的单元格,您可以循环遍历行并选择非NaN 值,或者仅将men 计算放在循环中,或任何其他方式...

如果您确定 A 的第 4 列不包含任何负数或零数(平均值永远不会冒 0 的风险),您可以将 men 的结果收集为稀疏矩阵

men = accumarray([userIdx timeIdx], A(:,4), [], @mean, 0, true);
men =
   (1,1)             4.5
   (2,1)               4
   (2,2)               4

【讨论】:

    【解决方案2】:

    我在不使用任何循环的情况下为您的任务找到了另一种解决方案:

    中值。

    u=unique(A(:,2));
    umedians = arrayfun( @(x) median (A( A(:,2)==x, 1)), u);
    

    说明: 首先找到所有唯一用户。然后使用arrayfun查找当前用户的所有数据,并为每个用户计算median

    第 4 列的平均值。

    这个任务有点难。我们可以这样走:

    temp = arrayfun( @(x) unique(A ( A(:,2)==x,5 )), u, 'UniformOutput',false);
    result = cellfun( @(y,z) arrayfun( @(x) mean( A( A(:,2) == u(z) & A(:,5) == x ,4) ), ... 
              y, 'UniformOutput',false), temp , num2cell( [1:size(u,1)]'), 'UniformOutput',false)
    

    说明:首先让我们找出每个用户的所有唯一时间。将其保存到元胞数组temp。现在我们需要为每个单元格找到相同的时间并计算平均值。因此,让我们使用cellfuntemp 的每个单元格生成它,并在其中使用arrayfun 来计算mean

    希望对你有帮助!

    【讨论】:

      猜你喜欢
      • 2020-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-02
      • 2021-05-31
      • 1970-01-01
      相关资源
      最近更新 更多