How do I set the threshold value on the action potential on the upward and downward slope to define width?
조회 수: 7 (최근 30일)
이전 댓글 표시
Hi,
I am doing action potential analysis and at the moment I am defining my threshold value manually using ginput for analysing width, however I would like to set identical y values for analysis of width of the curve. Right now, I zoom into the figure to try and set the x and y points to get the width of the AP.
My code for producing the figure and setting ginput values manually for the defined variables:
% Roksana's script to plot .smr AP data
% 20210127
% For troubleshooting analysis - cleans the workspace
clear
close all
%Import data
load('C:\xyz.mat')
%Define channel to plot
data = xyz;
vm = data.values;
%scale x-axis by interval
t = linspace(0, length(vm)*data.interval,length(vm));
plot(t,vm)
xlabel('Time (s)')
ylabel('Voltage (mV)')
title('Membrane potential')
%find max Vm value between 2 points selected on the figure
[x, y] = ginput(2);
%Report values towards AP width
xfirst = x(1,1)
xsecond = x(2,1)
%Round values for calculations of parameters
xpt1 = round(x(1));
xpt2 = round(x(2));
ypt1 = round(y(1));
ypt2 = round(y(2));
%Find a point between the first and second points on x-axis
isin = (t >= xpt1 & t <= xpt2);
%Identify the peak between the two points on the x-axis
%peak = max(vm(isin))
peak = max(vm)
%Display trough (mV); from baseline
APtrough = min(vm(isin))
%%Display AP amplitude (mV)
APamp = (peak + (ypt1*(-1)))
%%Display half-amplitude(mV)
APhalfamp = ((peak-APtrough)*0.5)
%%%Area under the curve and width values from threshold value - require zoom%%%
%pause script until button press for zooming in
zoom on;
pause ();
zoom off;
%find max Vm value between 2 points selected on the figure
[a, b] = ginput(2);
%Report values towards AP width
thresholdt1 = a(1,1)
thresholdt2 = a(2,1)
thresholdValue1 = b(1)
thresholdValue2 = b(2)
%%Display width(ms)
width = (thresholdt2 - thresholdt1)*1000 %%%%needs to be defined by threshold
%%Display half-width(ms)
APhalfwidth = (width/2)
댓글 수: 7
채택된 답변
Star Strider
2021년 2월 2일
편집: Star Strider
2021년 2월 2일
I am not certain how robust this is, since we only have one record. However it could likely be adapted easily to other records if they are similar.
D = load('xyz.mat');
F = fieldnames(D);
C = struct2cell(D);
Ch1s = C{1}.values; % Signal Vector
L1 = numel(Ch1s); % Length
Ch1t = linspace(0, L1, L1).'*C{1}.interval; % Time Vector
[CPL,m,b] = ischange(Ch1s, 'linear', 'Threshold',10); % Requires R2017b Or Later
cp1 = find((m>1), 1, 'first');
cp2 = find((m<-1), 1, 'last');
figure
plot(Ch1t, Ch1s)
hold on
% plot(Ch1t, m)
plot(Ch1t(cp1), Ch1s(cp1), '^r')
plot(Ch1t(cp2), Ch1s(cp2), 'vr')
hold off
grid
xlabel('Time (s)')
ylabel('Voltage (mV)')
title('Membrane potential')
pw = Ch1t(cp2) - Ch1t(cp1); % Pulse Width
text(Ch1t(cp1), Ch1s(cp1), '\rightarrow', 'HorizontalAlignment','right', 'VerticalAlignment','middle', 'FontWeight','bold')
text(Ch1t(cp2), Ch1s(cp2), sprintf('\\leftarrow Width = %.4f', pw), 'HorizontalAlignment','left', 'VerticalAlignment','middle', 'FontWeight','bold')
producing this plot:
EDIT — (2 Feb 2021 at 19:28)
My code using the newly-posted ‘AP2.mat’:
My code is otherwise unchanged, except for importing the new data file, and increasing the precision of the sprintf numeric field.
.
댓글 수: 4
Star Strider
2021년 2월 2일
My pleasure!
If my Answer helped you solve your problem, please Accept it!
.
추가 답변 (2개)
Mathieu NOE
2021년 2월 2일
this is my first attempt to make it fully automatic
based on this excellent submission (attached function) : Piecewise linear interpolation
so I try to fit a 5 segment of linear segments to your data, and the rest comes automatically
other methods could also work (e.g. based on derivatives , but this can be sensitive to noise)
% Roksana's script to plot .smr AP data
% 20210127
% For troubleshooting analysis - cleans the workspace
clear
close all
%Import data
load('xyz.mat')
%Define channel to plot
% data = xyz;
% vm = data.values;
% S2C1_17082020_WM_apomorphine_20uM_Ch1 =
%
% struct with fields:
%
% title: 'voltage'
% comment: 'No comment'
% interval: 4.0000e-05
% scale: 0.0153
% offset: 0
% units: 'mV'
% start: 0
% length: 1122
% values: [1122×1 double]
vm = S2C1_17082020_WM_apomorphine_20uM_Ch1.values;
samples = length(vm);
dt = S2C1_17082020_WM_apomorphine_20uM_Ch1.interval;
t_final = samples*dt;
%scale x-axis by interval
% t = linspace(0, length(vm)*data.interval,length(vm));
t = linspace(0, t_final,samples);
figure(1);
plot(t,vm)
xlabel('Time (s)')
ylabel('Voltage (mV)')
title('Membrane potential')
% five linear segments approximation
% first select 3 of the 5 points : start , peak (max) , end
[vm_max,ind] =max(vm);
x_vm_max = t(ind);
% Init of fminsearch
xdata = t(:);ydata = vm(:);
a = (max(xdata)-min(xdata))/4; b = a*2;
global xdata ydata x_vm_max vm_max
x0 = [a,b];
x = fminsearch(@obj_fun, x0);
a_sol = x(1); b_sol = x(2);
XI = [min(xdata),a_sol,x_vm_max,b_sol,max(xdata)]; % vector of 1-D look-up table "x" points
YI = lsq_lut_piecewise( xdata, ydata, XI ); % obtain vector of 1-D look-up table "y" points
% plot fit
figure(2);
plot(xdata,ydata,'.',XI,YI,'+-')
legend('experimental data (x,y(x))','LUT points (XI,YI)')
title('Piecewise 1-D look-up table least square estimation')
% selected threshold = max value of the y values corresponding to x = a_sol and x = b_sol
vm1 = interp1(XI,YI,a_sol);
vm2 = interp1(XI,YI,b_sol);
threshold = ceil(max(vm1,vm2)); % let's round it to the upper integer value
% finally, selected y values above "threshold" are
ind = find(vm>threshold);
vm_select = vm(ind);
t_select = t(ind);
figure(3);
plot(t,vm,t_select,vm_select,'+-')
legend('experimental data','selected data')
xlabel('Time (s)')
ylabel('Voltage (mV)')
title('Membrane potential')
%%Display AP amplitude (mV)
APamp = (vm_max - (vm1+vm2)/2) % compute difference between peak and "baseline" at same x location = average of vm1 and vm2
%%Display half-amplitude(mV)
% APhalfamp = ((peak-APtrough)*0.5) % not sure what to do here
% %%%Area under the curve and width values from threshold value - require zoom%%%
Area = trapz(t_select,vm_select-min(vm_select)) % units ? mV*s ; NB : to compute area, y must be offset so that y min value is zero
%%Display width(ms)
% this is the x distance between a_sol and b_sol
Xwidth = 1000*(-a_sol+b_sol) % units : ms
% width = (thresholdt2 - thresholdt1)*1000 %%%%needs to be defined by threshold
% %%Display half-width(ms)
% APhalfwidth = (width/2)
APhalfwidth = (Xwidth/2)
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function err = obj_fun(x)
global xdata ydata x_vm_max vm_max
XI = [min(xdata),x(1),x_vm_max,x(2),max(xdata)]; % vector of 1-D look-up table "x" points
YI = lsq_lut_piecewise( xdata, ydata, XI ); % obtain vector of 1-D look-up table "y" points
Yint = interp1(XI,YI,xdata);
err = sum((Yint-ydata).^2);
end
댓글 수: 13
vignesh
2024년 10월 22일
data = scale(data)
data.Time = (data.Time - data.Time(1))/1000;
data.X = 1.5*data.X;
data.X = data.X - mean(data.X);
data.Y = data.Y - mean(data.Y);
end
댓글 수: 0
참고 항목
카테고리
Help Center 및 File Exchange에서 Labels and Annotations에 대해 자세히 알아보기
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!