【问题标题】:Matrix indexing for fast sums by group按组快速求和的矩阵索引
【发布时间】:2019-08-12 19:07:25
【问题描述】:

我在 MATLAB 中运行仿真时遇到了内存不足的问题。

举个简单的例子,假设我有一个 MATLAB 表/矩阵/向量集合,如下所示:

 id | t | var
----+---+-----
  1 | 1 | 100
  1 | 2 | 150
  2 | 2 | 200
  2 | 3 |  90
  2 | 4 | 980

其中id 表示个人,t 表示时间段,var 是一个数值变量。

随着时间的推移t,我需要对给定个体id 的不同值求和var。我能找到的最简单的方法是运行以下命令:

idx    = sparse(id == id');
sumvar = idx*sumvar;

产生所需结果(与var 长度相同的向量,其中每个元素是给定id 的总和)。

 id | t | var | sumvar 
----+---+-----+--------
  1 | 1 | 100 |   250  
  1 | 2 | 150 |   250  
  2 | 2 | 200 |  1270  
  2 | 3 |  90 |  1270   
  2 | 4 | 980 |  1270  

问题是计算idx非常计算机密集型的,并且我的计算机使用大约 150,000 个向量时内存不足。

一种可能的解决方案是使用以下代码:

len = length(id);
idx = sparse(len,len);
for i = 1:len
   idx(id == id(i),:) = 1;
end

但这似乎很慢。

我觉得这是其他人可能已经面临的问题。有没有什么东西可以既非计算密集型又足够快?

【问题讨论】:

    标签: matlab matrix indexing


    【解决方案1】:

    你可以试试accumarray,如下。让您的数据成为

    id = [1 1 2 2 2].';
    var = [100 150 200 90 980].';
    
    • 假设id 总是包含从1 开始的整数条目:

      result = accumarray(id, var);
      

      给予

      result =
               250
              1270
      
    • 如果id 是任意的,请使用:

      [~, ~, id_int] = unique(id);
      result = accumarray(id_int, var);
      
    • 如果您需要与代码一样重复结果,请添加:

      result_repeated = result(id_int);
      

    【讨论】:

      【解决方案2】:

      您可以尝试以下方法,仍然使用循环,但通过仅迭代唯一的 id 以更有效的方式。

      id = [1 1 2 2 2].';
      var = [100 150 200 90 980].';
      
      unique_ids = unique(id);    % get the unique ids
      sum_var = NaN(size(var));   % init the sum_var vector
      
      for k = unique_ids.'        % loop over the ids
          idx = find(id == k);    % find indices per id
          sum_var(idx) = sum(var(idx));   % sum per id
      end
      

      或者,如果您只需要一个带有每个 id 总和的向量:

      unique_ids = unique(id);           % get the unique idx
      sum_var = NaN(size(unique_ids));   % init the sum_var vector
      
      for k = 1:numel(unique_ids)
          idx = find(id == unique_ids(k));    % find indices per id
          sum_var(k) = sum(var(idx));         % sum per id
      end
      

      更新: 也可以不使用find,通过使用unique 可以返回的每个唯一元素的索引来完成。 假设您的数据按id 排序,您可以执行以下操作:

      
      [unique_ids, start_idx, ~] = unique(id);  % get the unique idx, and the first occuring idx per id
      
      sum_var = NaN(size(unique_ids));
      
      start_idx = [start_idx; numel(var)+1];    % append total number of elements+1 for last summation in loop below
      
      for k = 1:numel(unique_ids)
          ids = start_idx(k):start_idx(k+1)-1;  % indices in table for specific id
          sum_var(k) = sum(var(ids));
      end
      

      【讨论】:

      • 工作就像一个魅力,虽然 find 是计算密集型的。选择了另一个答案 b/c 这是我不必费心构造idx 开始的一种方式!
      【解决方案3】:

      对于您的示例表T

       id | t | var
      ----+---+-----
        1 | 1 | 100
        1 | 2 | 150
        2 | 2 | 200
        2 | 3 |  90
        2 | 4 | 980
      

      你可以使用varfun

      a = varfun( @sum, T, 'GroupingVariables', 'id', 'InputVariables', 'var' )
      

      结果:

      a =
      2×3 table
      id    GroupCount    sum_var
      __    __________    _______
      1     2              250   
      2     3             1270  
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-10-13
        • 1970-01-01
        • 2013-12-25
        • 2017-11-03
        • 1970-01-01
        • 2020-08-26
        • 1970-01-01
        相关资源
        最近更新 更多