I have the following signal with some peaks and some valleys. Is it possible to make the baseline zero?

45 ビュー (過去 30 日間)
Basically, I want all the "Almost" horizontal lines to be converted to a baseline on y = 0. Such that a peak will be above zero, and a valley below zero.
Also, if this output would fluctuate more up and down, i.e. the "Baseline" might at some point be at +1 or -1 or something and constantly change. Is there a way to make it always zero?
Which funtion would I need to use?
Thaks in advance,
Jonah

回答 (3 件)

Image Analyst
Image Analyst 2023 年 4 月 29 日
How about you threshold to extract everything between 0 and -0.5? Then fit a line to it and subtract it from the original signal. Untested code (because you forgot to attach your data):
baseLineIndexes = (y < 0) & (y > -0.5);
yForFit = y(baseLineIndexes);
xForFit = x(baseLineIndexes);
% Fit a line
coefficients = polyfit(xForFit, yForFit, 1)
% Get values of that line everywhere.
yFitted = polyval(coefficients, x); % Get the baseline.
yShifted = y - yFitted; % Subtract baseline from original signal
If you have any more questions, then attach your data and code to read it in with the paperclip icon after you read this:

Star Strider
Star Strider 2023 年 4 月 30 日
編集済み: Star Strider 2023 年 4 月 30 日
The approach I would take would be to find the minimum just before each of the tall spikes (use findpeaks or islocalmax to find the indices of the spikes, and assuming a regular sampling frequency, that would require a specific window to identify the minimum), then use those as the baseline definition. Fit a polynomial of the y-values of those points and subtract it from the entire signal to detrend it. I am not certain that there is any other way to define something similar to a ‘baseline’ in that signal.
The approach (using simulated data) work like this —
x = linspace(0, 100, 1000); % Create Data
v = (10:20:90).';
y = 3*sum((x-v).*exp(-0.5*abs((x-v))))+sin(2*pi*x/130)/5+2; % Create Data
figure
plot(x, y)
grid
xlabel('X')
ylabel('Y')
title('Original Signal')
[pks,locs] = findpeaks(y, 'MinPeakProminence',2);
offset = 75; % 'Window' Length
for k = 1:numel(locs)
idxrng = max(1,locs(k)-offset) : locs(k); % 'Window' For This Peak
[minv,idx] = min(y(idxrng)); % Find Minimum Within Window
prev(k) = locs(k)-offset + idx; % Return Absolute Index Into Vectors
end
% prev
figure
plot(x, y)
hold on
plot(x(prev), y(prev), 'ms')
hold off
grid
xlabel('X')
ylabel('Y')
title('Original Signal Showing Selected Previous Minima')
[p,S,mu] = polyfit(x(prev), y(prev), 3); % Use 'polyfit' To Fit Trend Line Of Selected Minima
trnd = polyval(p,x,S,mu); % Create Detrending Vector For Entire Data Vector
y_dtrnd = y - trnd; % Detrend 'y'
figure
plot(x, y_dtrnd)
grid
xlabel('X')
ylabel('Y')
title('Detrended Signal')
Something similar to this will likely help detrend your signal to remove the baseline offset, although it may need to be tweaked (specifically the ‘offset’ value) to work with your data.
EDIT — (30 Apr 2023 at 22:58)
With respect to the electrocardiogram (EKG), the P-R interval is defined to be isoelectric (have a potential of zero), so using my code with a represenative EKG would produce —
EKG = readtable('EKG_Record.txt', 'VariableNamingRule','preserve')
EKG = 15580×3 table
sec ECG Heart Rate _____ _________ __________ 0.23 -0.063587 0 0.231 -0.064486 0 0.232 -0.065624 0 0.233 -0.066934 0 0.234 -0.068341 0 0.235 -0.069759 0 0.236 -0.071105 0 0.237 -0.07232 0 0.238 -0.073357 0 0.239 -0.074175 0 0.24 -0.074743 0 0.241 -0.075041 0 0.242 -0.075073 0 0.243 -0.074869 0 0.244 -0.074474 0 0.245 -0.073927 0
% x = linspace(0, 100, 1000); % Create Data
% v = (10:20:90).';
% y = 3*sum((x-v).*exp(-0.5*abs((x-v))))+sin(2*pi*x/130)/5+2; % Create Data
x = EKG.sec;
y = EKG.ECG;
figure
plot(x, y)
grid
xlabel('X')
ylabel('Y')
title('Original Signal')
figure
plot(x, y)
grid
xlim([0 5])
xlabel('X')
ylabel('Y')
title('Original Signal (Detail) P-R Intervals Are Not Isoelectric')
[pks,locs] = findpeaks(y, 'MinPeakProminence',0.5);
offset = 75; % 'Window' Length
for k = 1:numel(locs)
idxrng = max(1,locs(k)-offset) : locs(k); % 'Window' For This Peak
[minv,idx] = min(y(idxrng)); % Find Minimum Within Window
prev(k) = locs(k)-offset + idx; % Return Absolute Index Into Vectors
end
% prev
figure
plot(x, y)
hold on
plot(x(prev), y(prev), 'ms')
hold off
grid
xlabel('X')
ylabel('Y')
title('Original Signal Showing Selected Previous Minima')
[p,S,mu] = polyfit(x(prev), y(prev), 3); % Use 'polyfit' To Fit Trend Line Of Selected Minima
trnd = polyval(p,x,S,mu); % Create Detrending Vector For Entire Data Vector
y_dtrnd = y - trnd; % Detrend 'y'
figure
plot(x, y_dtrnd)
grid
xlabel('X')
ylabel('Y')
title('Detrended Signal')
figure
plot(x, y_dtrnd)
grid
xlim([0 5])
xlabel('X')
ylabel('Y')
title('Detrended Signal (Detail) P-R Intervals Are Isoelectric')
.

Adam Danz
Adam Danz 2023 年 4 月 30 日
編集済み: Adam Danz 2023 年 4 月 30 日
Since you just want the baseline shift, you just need to fit the y-intercept which would be a 0-degree polynomial.
Here's a demo that fits similar data and plots the fit as a horizontal reference line.
Load and plot data
Replace x and y with your data.
file = 'http://people.ucalgary.ca/~ranga/enel563/SIGNAL_DATA_FILES/ecgpvc.dat';
datastr = urlread(file);
y = str2num(datastr);
y(501:end) = [];
x = 1:numel(y);
plot(x,y)
Fit a horizontal line and plot the fitted line
yint = polyfit(x,y,0); % 0-degree polynomial
yline(yint, 'k--')
Create a second figure that shifts the baseline to 0
dataShifted = y - yint;
figure()
plot(x, dataShifted)
yline(0, 'k-')
Note that this approach may be affected by large outliers so if you find that the fit does not center around the section of the signal you consider to be baseline, isolate parts of the signal as suggested in Image Analyst's answer.
Demo data from FEX.

カテゴリ

Help Center および File ExchangeDescriptive Statistics についてさらに検索

製品


リリース

R2022b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by