I have an EEG data base that I would like to plot.
The database is a 19*1000*134 matrix, with:
19 being the number of channel. On a first approach, I'm working with only one channel.
1000 the size of a sample (1000 points for a sampling rate of 500 Hz, i.e. 2 sec of data)
134 the number of epochs (number of different 2 second experience)
The idea is to plot epoch n right after epoch n-1 on the same graph. The (X,Y) matrix used to plot this has a 134 000 * not_much size, and I would like to be able to scroll horizontally on the plot, to see individually each epoch.
My code right now, plotting only one channel:
fs = s_EEG.sampling_rate;
[channel, length, nb_epoch] = size(s_EEG.data)
display(s_EEG.data, fs, length, channel, nb_epoch)
function display(data, fs, length, channel, nb_epoch)
figure("Name", "Epoch display")
for j = 1:nb_epoch
time = 0.002+(2*j-2):1/fs:2*j;
epoch = data(1,:,j);
plot(time, epoch)
hold on
end
hold off
end
Current output:
I'm completely new to Matlab, and I don't use it well yet, but I would like to find a way to see on the same graph, individually, and at a correct visualization scale, all of my 134 epochs (one color = one epoch above).
Thanks !
This is very similar to something I already had so I tweaked it a bit for you. Basically pass plotData your data matrix. It will plot each of your items sequentially as you already have now.
Pressing the slider will change your x-limits so that you will step through 1 element (epochs) at a time. Clicking in the area will advance 2-epochs at a time. It currently just displays what you currently viewed "epoch" # is at the command line disp(['Current Epoch: ' num2str(viewI)]) However, it should be easy for you to redirect that to a text box on the figure to more readily know which you are viewing ... besides mentally dividing the x-limits by 2.
Use the list box to switch to a new channel which will reset the plot & x-limits.
Call it like this at the command line.
>> plotData( data )
CODE: Save everything below as plotData.m
function plotData( data )
% data = rand(19,1000,134);
f = figure('Units','Normalized','Position',[0.25 0.25 0.5 0.5]);
a = axes('Units','Normalized','Position',[0.05 0.15, 0.75 0.75]);
s = uicontrol(f, 'Style','Slider', 'Units','Normalized','Position',[0.05 0.025, 0.75 0.05],...
'Min',1,'Max',size(data,3),'Value',1, 'Callback',{#sliderChange,a} );
l = uicontrol(f, 'Style','listbox','Units','Normalized','Position',[0.85 0.15, 0.1, 0.75],...
'String',cellstr(num2str([1:size(data,1)]')),'Callback',{#changeChannel,a,s,data} );
stepSize = 1/(s.Max - s.Min);
s.SliderStep = [stepSize 2*stepSize];
changeChannel(l,[],a,s,data)
function changeChannel(l,evtData,a,s,data)
cla(a);
chanNum = str2double(l.String{l.Value});
sR = 500; %500Hz
tempData = reshape(data(chanNum,:,:),[],size(data,3)); %Reshape each epoch into a column
tempTime = [0:1/sR:(size(data,2)-1)/sR]' + (0:1:size(data,3)-1)*2; %Build time array
plot(a,tempTime,tempData) %plot all the lines
s.Value = 1; %Rest Slider Position
function sliderChange(s,evtData,a)
viewI = round(s.Value);
disp(['Current Epoch: ' num2str(viewI)])
xlim(a,[(viewI-1)*2 viewI*2] + [-.1 .1])
Note: This uses implicit expansion so you need Matlab 2016b or higher.
Related
I am using MATLAB R2020a on a MacOS. I have an ECG signal with a sampling frequency of 1kHz. From cycle to cycle, there are a different number of samples since the cycle lenghts are different. However, I would like to have an array which has an equal number of samples (around 800) for each cycle such that those 800 sample points are automatically fitted to the original sample points regardless of the number of original sample points. I know that the resample function allows resampling at a fraction of the frequency of the input signal, but I am not sure how this would help me achieve my aim given that I would like to resample for a fixed number of points.
I would very much appreciate any suggestions. Thanks in advance
Here is my code:
% Delimit cycles in original maximum amplitude signal using indices from
% Pan Tompkins algorithm
number_cycles = round(length(qrs_i_raw)/2);
number_samples = 900;
cycle_points_maxamp = zeros(number_cycles, length(number_samples));
x_eachcycle = zeros(number_cycles, length(number_samples));
values_x = zeros(length(number_samples), 1);
y_eachcycle = zeros(number_cycles, length(number_samples));
values_y = zeros(length(number_samples), 1);
z_eachcycle = zeros(number_cycles, length(number_samples));
values_z = zeros(length(number_samples), 1);
v_eachcycle = zeros(number_cycles, length(number_samples));
w_eachcycle = zeros(number_cycles, length(number_samples));
for currentcycle = 1:length(number_cycles)
values_maxamp = maxamp(qrs_i_raw(currentcycle):qrs_i_raw(currentcycle + 1)); % need to resample to only generate 900 samples
cycle_points_maxamp(currentcycle, 1:length(values_maxamp)) = values_maxamp;
values_z(1 + 2*tau_milli_rounded(currentcycle):end) = cycle_points_maxamp(currentcycle, 1:end - 2*tau_milli_rounded(currentcycle));
z_eachcycle(currentcycle, 1:length(values_z)) = values_z;
values_y(1 + tau_milli_rounded(currentcycle):end) = cycle_points_maxamp(currentcycle, 1:end - tau_milli_rounded(currentcycle));
y_eachcycle(currentcycle, 1:length(values_y)) = values_y;
values_x(1:end) = cycle_points_maxamp(currentcycle, 1:end);
x_eachcycle(currentcycle, 1:length(values_x)) = values_x;
values_v = ((1/sqrt(6))*(x_eachcycle(currentcycle, 1:length(values_x))) + (y_eachcycle(currentcycle, 1:length(values_y))) - 2*(z_eachcycle(currentcycle, 1:length(values_z))));
v_eachcycle(currentcycle, 1:length(values_v)) = values_v;
values_w = ((1/sqrt(2))*(x_eachcycle(currentcycle, 1:length(values_x))) - (y_eachcycle(currentcycle, 1:length(values_y))));
w_eachcycle(currentcycle, 1:length(values_w)) = values_w;
end
Here are the relevant variables:
maxamp
qrs_i_raw
cyclepointsarray
To resample a single cycle of the signal to have a length of 800 points/samples the resample() function can be used with parameters 800 and length(Random_Cycle). Here I used a sinc signal arbitrarily. The resample() function will interpolate (upsample) or decimate (downsample) according to the number of available sample points in the original signal. I would first partition your signal into separate cycles apply this type of resampling the concatenate the resampled components to generate your full resampled ECG signal.
Random_Cycle = sinc(-2*pi:0.5:2*pi);
subplot(1,2,1); stem(Random_Cycle);
title("Number of Samples: " + num2str(length(Random_Cycle)));
Resampled_Signal = resample(Random_Cycle,800,length(Random_Cycle));
subplot(1,2,2); stem(Resampled_Signal);
title("Number of Samples: " + num2str(length(Resampled_Signal)));
Ran using MATLAB R2019b
I'm trying to calculate the DT value from a model I set up on Sim4Life. Firstly, i'd like to say that I am a complete beginner and I am trying to understand how programming works in general.
Now, I have a function with some constants and two variables, the one being time Dt (starting from 1 sec to 900 secs) and the other being the initial DT_i value. I want to calculate the increase of temperature for every second and create a loop that replaces the DT_i value with the DT_1_i value and also calculates the increased temperature DT_i_1. The function looks like this: DT_1_i=DT_i+Dt.
I know it is a very simple problem but I couldn't work my way through other similar questions. Any help would be appreciated.
Temperature variation:
You need initial temperature variation , I used 0
T(i+1) stands for Next temperature variation
T(i) stands for present temperature variation
i stands for time step, dt
Read through comment in my code
Time
Use for loop to set the time for i = 1 : 900 %Temperature increase end
i =1:900 just means
first run use time = 1s,
second run time = 1+1 = 2
so on till 900
The code is as follow
% Initial Temperature variation is set to zero, unless you have some data
d = 1.3;
c = 3.7;
S_i = 3*10^3;
t_reg = 900;
%Time
t = 1:900;
% Length oftime to help me know the size of the variable needed to
% initialize
l = length(t);
% Initialize variable that used to store DT it helps speed up
% comutation
% Initial Temperature variation is set to zero, unless you have some data
DT = zeros(1, l);
for i = 1:900
% the value of i represent dt, first run i = 1, dt = 1, second run
% i = 2 and dt = 2 so on
if i == 900
%do nothing already reached the last index 900, i+1 = 901 will be
%out of range
else
DT(i+1) = DT(i) + (i./t_reg).*(d.*sqrt(c*S_i)-DT(i+1));
end
end
I have the following data that predicts a curve in the middle of the two curves which has different equation and datas.I also need to spline and smothen the curve of the middle curve
I've tried searching other codes here in stackoverflow but this is the most close to the right solution. So far the plot for the two curve is right but the interpolated point gives me wrong plot.
Im trying to find the plot for val=30 given that (a25,vel25)=25 and (a50,vel50)=50. Please help me troubleshoot and get a table of data (x,y) for the generated interpolated curve. Thanks for your help
generated plot using this program
a50=[1.05
0.931818182
0.931818182
0.968181818
1.045454545
1.136363636
1.354545455
1.568181818
1.718181818
1.945454545
2.159090909
2.454545455
2.772727273
];
vel50=[0.85
0.705555556
0.605555556
0.533333333
0.472222222
0.45
0.427777778
0.45
0.477777778
0.533333333
0.611111111
0.711111111
0.827777778
];
a25=[0.5
0.613636364
0.686363636
0.795454545
0.918181818
0.963636364
1.090909091
1.236363636
1.304545455
1.431818182
1.545454545
1.659090909
1.818181818
];
vel25=[0.425555556
0.354444444
0.302222222
0.266666667
0.233333333
0.226666667
0.211111111
0.222222222
0.237777778
0.266666667
0.311111111
0.35
0.402222222
];
plot(a25,vel25,'b-');
hold on
plot(a50,vel50,'g-');
minX = min([a25 a50]);
maxX = max([a25,a50]);
xx = linspace(minX,maxX,100);
vel25_inter = interp1(a25,vel25,xx);
vel50_inter = interp1(a50,vel50,xx);
val = 30; % The interpolated point
interpVel = vel25_inter + ((val-25).*(vel50_inter-vel25_inter))./(50-25);
plot(xx,interpVel,'r-');
The question and answer linked in comment still apply and can be a solution.
In your case, it is not so direct because your data are not on the same grid and some are not monotonic, but once they are packaged properly, the easiest solution is still to use griddata.
By packaged properly, I mean finding the maximum common interval (on x, or what you call a), so the data can be interpolated between curve without producing NaNs.
This seems to work:
The red dashed line is the values interpolated at val=30, all the other lines are interpolations for values between 25 to 50.
The code to get there:
% back up original data, just for final plot
bkp_a50 = a50 ; bkp_vel50 = vel50 ;
% make second x vector monotonic
istart = find( diff(a50)>0 , 1 , 'first') ;
a50(1:istart-1) = [] ;
vel50(1:istart-1) = [] ;
% prepare a 3rd dimension vector (from 25 to 50)
T = [repmat(25,size(a25)) ; repmat(50,size(a50)) ] ;
% merge all observations together
A = [ a25 ; a50] ;
V = [vel25 ; vel50] ;
% find the minimum domain on which data can be interpolated
% (anything outside of that will return NaN)
Astart = max( [min(a25) min(a50)] ) ;
Astop = min( [max(a25) max(a50)] ) ;
% use the function 'griddata'
[TI,AI] = meshgrid( 25:50 , linspace(Astart,Astop,10) ) ;
VI = griddata(T,A,V,TI,AI) ;
% plot all the intermediate curves
plot(AI,VI)
hold on
% the original curves
plot(a25,vel25,'--k','linewidth',2)
plot(bkp_a50,bkp_vel50,'--k','linewidth',2)
% Highlight the curve at T = 30 ;
c30 = find( TI(1,:) == 30 ) ;
plot(AI(:,c30),VI(:,c30),'--r','linewidth',2)
There were quite a few issues with your code that's why it was not executing properly. I have just made very little changes to your code and made it running,
clc
%13
a50=[1.05
0.931818182
0.932
0.968181818
1.045454545
1.136363636
1.354545455
1.568181818
1.718181818
1.945454545
2.159090909
2.454545455
2.772727273
];
%13
vel50=[0.85
0.705555556
0.605555556
0.533333333
0.472222222
0.45
0.427777778
0.45
0.477777778
0.533333333
0.611111111
0.711111111
0.827777778
];
%13
a25=[0.5
0.613636364
0.686363636
0.795454545
0.918181818
0.963636364
1.090909091
1.236363636
1.304545455
1.431818182
1.545454545
1.659090909
1.818181818
];
%13
vel25=[0.425555556
0.354444444
0.302222222
0.266666667
0.233333333
0.226666667
0.211111111
0.222222222
0.237777778
0.266666667
0.311111111
0.35
0.402222222
];
plot(a25,vel25,'b-');
hold on
plot(a50,vel50,'g-');
minX = min([a25 a50])
maxX = max([a25 a50])
%xx = linspace(minX,maxX);
xx = linspace(0.5,2.7727,100);
vel25_inter = interp1(a25,vel25,xx);
vel50_inter = interp1(a50,vel50,xx);
val = 30; % The interpolated point
interpVel = vel25_inter + ((val-25).*(vel50_inter-vel25_inter))./(50-25);
plot(xx,interpVel,'r-');
There issues were
The interval on which you wanted to interpolate is xx = linspace(minX,maxX); but it gives an error of the type,
Matrix dimensions must agree.
Because you were assigning two values for the starting point and two for the ending point. So I replace it with xx = linspace(0.5,2.7727,100); where the starting point is being the minimum of the two minimum minX and the same for maxX
There are values of a50 (0.931818182) that repeat which was producing the following error
The grid vectors are not strictly monotonic increasing.
I change one of the value and replaced it with 0.932
The output is not that promising but is suppose the one you want?
I'm trying to use aero.animation to animate a missile motion in matlab.
When I use the following code it plays the video but saves only the first frame.
*data is in form of [time, x, y, angle] matrix where all time, x, y and angle are nx1 vectors which I calculated beforehand. In my case n = 1312 and I've taken x and y as zeros of column vectors. angle is the fifth column of SOLUTION which is a matrix of 1312x7, I had calculate before.
data = [time, zeros(length(time), 1), zeros(length(time), 1), SOLUTION(:, 5)];
h=Aero.Animation;
f=figure;
h.Figure=f;
h.initialize();
h.FramesPerSecond=10
h.TimeScaling = 5;
idx1=h.createBody('testrocket.ac','ac');
h.bodies{1}.TimeseriesSourceType='Array3DoF';
h.bodies{1}.timeseriesSource=data;
h.Camera.offset=[-150 -150 0];
h.show()
h.VideoRecord = 'on';
h.VideoQuality = 50;
h.VideoCompression = 'Motion JPEG AVI'
h.VideoFilename = 'astMotion_JPEG';
h.play()
h.VideoRecord='off';
The code looks like this now, yet the video still recording same image for few seconds insted of recording the actual video with the missile movment. ( when i play the record video it shows like frozen situation).
Thanks.
I have found a solution but you have to know the time duration of your animation.
Now your problem is caused by running h.VideoRecord = 'off'; command shortly after h.play command but h.VideoRecord = 'off'; command doesn' t wait for the previous command finish. Anyway you can use pause() function to avoid the problem.
% // ...
h.play()
pause(timeDurationOfAnimation) % // timeDurationOfAnimation -> time unit in seconds
h.VideoRecord = 'off';
EDIT: Time duration of animation/video depends on you specified values of FramesPerSecond and TimeScaling properties.
The combination of FramesPerSecond and TimeScaling property determine the time step of the simulation. Your settings result in a time step of approximately 0.5 s.
+++ More Explanation:
My data is 100x4 matrix means length of time vector is 100. Time resolution is 0.1 second so time vector like that Time = [0, 0.1, 0.2, 0.3, ..., 9.8, 9.9, 10]'. If I use your time set up which is FramesPerSecond = 10 and TimeScaling = 5, I get 2 seconds of video. Because TimeScaling = 5 means display 5 data pack in per second, but one data pack contains 10 frames so according to your set up animation displays 50 frame per second. My whole data contains 100 frames so 100/50 = 2 seconds so time duration of animation/video is 2 seconds. Remember TimeScaling is due to speeding up the animation and you record your animation as speeded up one.
My suggestion is that specify value of FramesPerSecond according to frequency of your Time update:
% // ...
h.FramesPerSecond = 1 / ( data(2, 1) - data(1, 1) ) % // According to my Time resolution that would be 10;
h.TimeScaling = 1;
% // ...
pause( data(end, 1) )
h.VideoRecord = 'off';
I have two sets of data, but the sets have a different sizes.
Each set contains the measurements itself (MeasA and MeasB, both double) and the time point (TimeA and TimeB, datenum or julian date) when the measuring happened.
Now I want to match the smaller data set to the bigger one, and to do this, I want to mean the data points of the bigger set around the data resp. time points of the smaller set, to finally do some correlation analysis.
Edit:
Small Example how the data would look like:
MeasA = [2.7694 -1.3499 3.0349 0.7254 -0.0631];
TimeA = [0.2 0.4 0.7 0.8 1.3];
MeasB = [0.7147 -0.2050 -0.1241 1.4897 1.4090 1.4172 0.6715 -1.2075 0.7172 1.6302];
TimeB = [0.1 0.2 0.3 0.6 0.65 0.68 0.73 0.85 1.2 1.4];
And now I want to collapse MeasB and TimeB so that I get the mean of the measurement close to the timepoints in TimeA, so for example TimeB should look like this:
TimeB = [mean([0.1 0.2]) mean([0.3 0.6]) mean([0.65 0.68 0.73]) mean([0.85]) mean([1.2 1.4])]
TimeB = [0.15 0.4 0.69 0.85 1.3]
And then collapse MeasB like this too:
MeasB = [mean([0.7147 -0.2050]) mean([-0.1241 1.4897]) mean([1.4090 1.4172 0.6715]) mean([-1.2075]) mean([0.7172 1.6302])];
MeasB = [0.2549 0.6828 1.1659 -1.2075 1.1737]
The function interp1 is your friend.
You can get a new set of measurement for your set B, at the same time than set A by using:
newMeasB = interp1( TimeB , MeasB , TimeA ) ;
The first 2 parameters are your original Time and Measurements of the set you want to re interpolate, the last parameter is the new x axis (time in your example) on which you want the interpolated values to be calculated.
This way you do not end up with different sets of time between your 2 sets of measurements, you can compare them point by point.
Check the documentation of interp1 for more explanations and for options about the interpolation or any potential extrapolation.
edit:
Matlab doc used to have a great illustration of the function but I can't find it online so here goes:
So with the linear method, if the value is interpolated exactly between 2 points, the function will return the exact mean. If the interpolation is done closer to one point than another, the value returned will be proportionally closer to the value of the closest point.
The NaN can appear on the sides (beginning or end of returned vector) if the TimeA was not completely overlapped by timeB. The function cannot "interpolate" because there is no anchor point. However, the different options of interp1 allow you to "extrapolate" outside of the input range, or to assign another default value instead of the NaNs.