Split vector in MATLAB

后端 未结 6 1003
独厮守ぢ
独厮守ぢ 2020-12-15 07:06

I\'m trying to elegantly split a vector. For example,

vec = [1 2 3 4 5 6 7 8 9 10]

According to another vector of 0\'s and 1\'s of the same

6条回答
  •  独厮守ぢ
    2020-12-15 07:34

    Solution code

    You can use cumsum & accumarray for an efficient solution -

    %// Create ID/labels for use with accumarray later on
    id = cumsum(cut)+1   
    
    %// Mask to get valid values from cut and vec corresponding to ones in cut
    mask = cut==0        
    
    %// Finally get the output with accumarray using masked IDs and vec values 
    out = accumarray(id(mask).',vec(mask).',[],@(x) {x})
    

    Benchmarking

    Here are some performance numbers when using a large input on the three most popular approaches listed to solve this problem -

    N = 100000;  %// Input Datasize
    
    vec = randi(100,1,N); %// Random inputs
    cut = randi(2,1,N)-1;
    
    disp('-------------------- With CUMSUM + ACCUMARRAY')
    tic
    id = cumsum(cut)+1;
    mask = cut==0;
    out = accumarray(id(mask).',vec(mask).',[],@(x) {x});
    toc
    
    disp('-------------------- With FIND + ARRAYFUN')
    tic
    N = numel(vec);
    ind = find(cut);
    ind_before = [ind-1 N]; ind_before(ind_before < 1) = 1;
    ind_after = [1 ind+1]; ind_after(ind_after > N) = N;
    out = arrayfun(@(x,y) vec(x:y), ind_after, ind_before, 'uni', 0);
    toc
    
    disp('-------------------- With CUMSUM + ARRAYFUN')
    tic
    cutsum = cumsum(cut);
    cutsum(cut == 1) = NaN;  %Don't include the cut indices themselves
    sumvals = unique(cutsum);      % Find the values to use in indexing vec for the output
    sumvals(isnan(sumvals)) = [];  %Remove NaN values from sumvals
    output = arrayfun(@(val) vec(cutsum == val), sumvals, 'UniformOutput', 0);
    toc
    

    Runtimes

    -------------------- With CUMSUM + ACCUMARRAY
    Elapsed time is 0.068102 seconds.
    -------------------- With FIND + ARRAYFUN
    Elapsed time is 0.117953 seconds.
    -------------------- With CUMSUM + ARRAYFUN
    Elapsed time is 12.560973 seconds.
    

    Special case scenario: In cases where you might have runs of 1's, you need to modify few things as listed next -

    %// Mask to get valid values from cut and vec corresponding to ones in cut
    mask = cut==0  
    
    %// Setup IDs differently this time. The idea is to have successive IDs.
    id = cumsum(cut)+1
    [~,~,id] = unique(id(mask))
          
    %// Finally get the output with accumarray using masked IDs and vec values 
    out = accumarray(id(:),vec(mask).',[],@(x) {x})
    

    Sample run with such a case -

    >> vec
    vec =
         1     2     3     4     5     6     7     8     9    10
    >> cut
    cut =
         1     0     0     1     1     0     0     0     1     0
    >> celldisp(out)
    out{1} =
         2
         3
    out{2} =
         6
         7
         8
    out{3} =
        10
    

提交回复
热议问题