Search code examples
matlabcell-array

Sensor time distributions from arrays and cell arrays


I have a cell array and a numeric array in matlab which are inherently linked. The numeric array (A) contains a series of times from several data sources e.g. the time of each measurement. The array is n sensors (columns) by n measurements (rows). The array is filled with -1 by default since 0 is a valid time.

A = [ [ 100 110  -1  -1 ] ; ...
      [  -1 200 180  -1 ] ; ...
      [  -1 200 210 240 ] ; ...
      [ 400  -1  -1 450 ] ];

The cell contains the sensors, in chronological order, for each row of the numeric array. Each cell elements contains a vector showing the sensors in the order they made the measurements.

C = { [1 2] [3 2] [2 3 4] [1 4]};

I want to see the distribution of times relative to each sensor e.g. what is the distribution of times from sensor 2/3/4 (when they are present), relative to sensor?

For example...

Sensor 1 is involved in the first and fourth measurements and the other detectors were +10 (100 -> 110) and +50 (400 -> 450). In this case I'm looking to return an array such as [10 50].

Sensor 2 is involved in the first three events, one of which is a three-way event. In this case it sensor2 isn't always the first to trigger, so some values will be negative. In this case I'm looking to return [-10 -20 +10 +40)]

Using the same logic sensor3 should return [20 -10 30] and sensor4 [-40 -30 -50].

I'm sure there should be an easy way to do this but I can't get my head round it. Of course the example I've given is a very simple one.... normally I'm dealing with tens of sensors and 100,000's measurements so looping over each and every col / row will take a long time... and often draw little results if only two (or so) of the sensors trigger in each measurement. For this reason I was hoping to use the elements in the cell array to access only the correct elements in the numeric array.

Any thoughts?


Solution

  • If I have understood the problem well enough for solving, it seems you don't need to worry about C for the output. Here's the code -

    num_sensors = size(A,2)%// No. of sensors
    
    A = A'; %//' The tracking goes row-wise, so transpose the input array
    A(A==-1)=nan; %//set minus 1's to NaNs as excluding elements
    out = cell(num_sensors,1);  %// storage for ouput
    for k1 = 1:num_sensors
    
        %// Per sensor subtractions
        per_sensor_subt = bsxfun(@minus,A,A(k1,:)); 
    
        %// Set all elements of its own row to NaNs to exclude own subtractions
        per_sensor_subt(k1,:)=nan; 
    
        %// Get all the non-nans that correspond to the valid output
        out{k1} = per_sensor_subt(~isnan(per_sensor_subt));
    end
    

    Output -

    >> celldisp(out)
    out{1} =
        10
        50
    out{2} =
       -10
       -20
        10
        40
    out{3} =
        20
       -10
        30
    out{4} =
       -40
       -30
       -50
    

    As you have confirmed that the order of the output for each cell isn't important, you can employ a simplified approach that could be faster -

    num_sensors = size(A,2)%// No. of sensors
    A(A==-1)=nan; %//set minus 1's to NaNs as excluding elements
    out = cell(num_sensors,1);  %// storage for ouput
    for k1 = 1:num_sensors
    
        %// Per sensor subtractions
        per_sensor_subt = bsxfun(@minus,A,A(:,k1)); 
    
        %// Set all elements of its own row to NaNs to exclude own subtractions
        per_sensor_subt(:,k1)=nan; 
    
        %// Get all the non-nans that correspond to the valid output
        out{k1} = per_sensor_subt(~isnan(per_sensor_subt));
    end
    

    Fully vectorized solution if memory permits -

    [m,n] = size(A)%// No. of sensors and measurements
    A(A==-1)=nan; %//set minus 1's to NaNs as excluding elements
    
    %// Per sensor subtractions
    per_sensor_subt = bsxfun(@minus,A,permute(A,[1 3 2]))
    
    %// Set all elements of its own row to NaNs to exclude own subtractions
    own_idx = bsxfun(@plus,bsxfun(@plus,[1:m]',[0:n-1]*numel(A)),[0:n-1]*m);%//'
    per_sensor_subt(own_idx)=nan;
    
    %// Linear and row-col-dim3 indices of valid subtractions
    idx = find(~isnan(per_sensor_subt))
    [x,y,z] = ind2sub(size(per_sensor_subt),idx)
    
    %// Get per sensor output
    out = arrayfun(@(n) per_sensor_subt(idx(z==n)),1:n,'un',0)
    

    If you would like to calculate C, use this approach -

    %// Sort A row-wise
    [sortedA,sorted_idx] = sort(A,2)
    
    %// Set all invalid indices to zeros, so that later on we can use `nonzeros`
    %// to extract out the valid indices
    valid_sorted_idx = sorted_idx.*(sortedA~=-1)
    
    %// Convert to a cell array
    valid_sorted_idx_cell = mat2cell(valid_sorted_idx,ones(1,size(A,1)),size(A,2))
    
    %// Extract the valid ones(nonzero indices) for the final output, C
    C = cellfun(@(x) nonzeros(x), valid_sorted_idx_cell,'un',0)