How do I remove a group of rows based on condition met in one in matlab? Have code but not sure what's going wrong?

Viewed 31

I am trying to detect high amplitude events and remove them along with rows above and below. I have the following code which does this in part but not fully and I'm not sure where the error is. I have commented out the the audioread function and added randi to allow reproducible results. Code:

%[data, fs] = audioread("noise.wav");

%t1 = linspace(0, (numel(data)-1)/fs, numel(data));

rng(1)
data = randi(10,1000,1);

threshold = 5;
clear_range = 10; %rows/samples
data = clearRange(data, threshold, clear_range);

%t1 = linspace(0, (numel(data)-1)/fs, numel(data));

%plot(t1, data);

plot(data)

function [data] = clearRange(data, threshold, clear_range, compare_column)
% data: matrix of values to clean
% threshold: value to compare values against
% clear_range: number of rows to delete
% compare_column: column to check for value to compare against threshold
    if nargin < 4
        compare_column = 1;
    end

    for i = 1:length(data)
        if i > length(data)
            break
        end
        if data(i,compare_column) > threshold
            data(max(1, i-clear_range):min(length(data), i+clear_range),:) = [];
        end
    end
end
1 Answers

I think the main problem with your code is that you modify data while looping over it. This means, you delete peaks (or high amplitude events in your words) in rows with an index greater than i, so that they cannot be taken into account in following iterations.

E.g. consider peaks in rows with indices 4 and 6, which should cause that rows up to index 16 are removed (with a value of clear_range equal to 10). However, when i is equal to 4, you remove rows up to index 14. Consequently, you also remove the peak at position 6, so that it is not taken into account in further iterations.

In general, it is easier to rely on MATLAB's matrix/array operations instead of using loops. Please find below a possible solution with explanations inline.

clc;
% I adjusted inputs to get a minimal example
data            = randi(10,30,1);
threshold       = 9; 
rangeToClear    = 1;
columnToCompare = 1;

dataOut         = clearRange(data, threshold, rangeToClear, columnToCompare );

disp('In:')
disp( data' ); 

disp('Out:')
disp( dataOut' ); % Plot for cross-check


function data = clearRange(data, threshold, rangeToClear, columnToCompare)

% rowsWithPeak is 1-D logical array showing where columnToCompare is greater than the threshold 
rowsWithPeak = data( :, columnToCompare ) > threshold;

% kernel is a column vector of ones of size Nx1, where N is the number of rows 
% that should be removed around a peak
kernel = ones( 2*rangeToClear+1, 1 );

% rowsToRemove is a column vector being greater than one at row indices
% that should be removed from the data. To obtain rowsToRemove, 
% we convolute rowsWithPeak with the kernel. The argument 'same' to 
% the conv2 function, specifies that rowsToRemove will have the same
% size as rowsWithPeak.
rowsToRemove = conv2( rowsWithPeak, kernel, 'same' );

% rowsToRemoveLogical is a logical array being one, where rowsToRemove is greater than 0.
% Note that, rowsToRemoveLogical = rowsToRemove > 0 would also work here
rowsToRemoveLogical = logical( rowsToRemove);

% Finally, we use rowsToRemoveLogical to mask positions of the rows that should be removed.
data( rowsToRemoveLogical, : ) = [];

end
Related