generated from readthedocs/tutorial-template
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added MSPTDfast v1 development analysis
- Loading branch information
1 parent
2b4a9ca
commit 032ff2b
Showing
1 changed file
with
276 additions
and
0 deletions.
There are no files selected for viewing
276 changes: 276 additions & 0 deletions
276
source/publication_specific_scripts/msptdfast_cinc_analysis_post_20240701.m
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,276 @@ | ||
function msptdfast_cinc_analysis_post_20240701 | ||
% MSPTDFAST_CINC_ANALYSIS_POST_20240701 Used for MSPTDfast v.1 design. | ||
% MSPTDFAST_CINC_ANALYSIS_POST_20240701 analyses the performance | ||
% (execution time and F1-score) of different configurations of the MSPTD | ||
% PPG beat detector. | ||
% | ||
% # Inputs | ||
% * ppg_detect_stats.mat - the results file from analysis of PPG-DaLiA | ||
% lunchbreak data. | ||
% | ||
% # Outputs | ||
% * Plots illustrating the performance of PPG beat detectors. | ||
% | ||
% # Exemplary usage | ||
% | ||
% msptdfast_cinc_analysis_post_20240701 % runs the analysis | ||
% | ||
% # Documentation | ||
% <https://ppg-beats.readthedocs.io/> | ||
% | ||
% # Author | ||
% Peter H. Charlton, University of Cambridge, July 2024. | ||
% | ||
% # License - MIT | ||
% Copyright (c) 2024 Peter H. Charlton | ||
% Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||
% The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||
% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
|
||
% Setup universal parameters | ||
up = setup_up; | ||
|
||
% load data | ||
load(up.paths.stats_file) | ||
|
||
% generate plot for each option | ||
generate_results_plots(res, up); | ||
|
||
end | ||
|
||
function up = setup_up | ||
|
||
up.paths.stats_file = '/Users/petercharlton/Library/CloudStorage/[email protected]/My Drive/Work/temp_transfer/ppg_detect_stats_lunchbreak.mat'; | ||
up.paths.stats_file = '/Users/petercharlton/Documents/Data/ppg_dalia/conv_data/proc_data_ppg_dalia_lunch_break/ppg_detect_stats.mat'; | ||
up.paths.plot_folder = '/Users/petercharlton/Library/CloudStorage/[email protected]/My Drive/Work/Publications/In Preparation/2024 CinC/PCharlton - MSPTDfast/figures_20240718/'; | ||
|
||
up.options.name = {'lmss'; ... | ||
'lms_method'; ... | ||
'lms_scales'; ... | ||
'fs'; ... | ||
'win_durn'; ... | ||
'algs'; ... | ||
%'red_cols'; ... | ||
}; | ||
up.options.label = {'LMSs to calculate'; | ||
'LMS calculation method'; ... | ||
'LMS scales used'; ... | ||
'Sampling Frequency (Hz)'; ... | ||
'Window Duration (s)'; ... | ||
'Algorithm'; ... | ||
%'LMS columns used'; ... | ||
}; | ||
up.options.values = {{'peaks', 'onsets', 'peaks and onsets'}; ... | ||
{'vectorised', 'nested loops'}; ... | ||
{'HRmin=40', 'HRmin=30', 'N/2'}; ... | ||
{'10', '20', '30', 'orig'}; ... | ||
{'4', '6', '8', '10', '12'}; ... | ||
{'MSPTD', 'MSPTDfast', 'qppgfast'}; ... | ||
%{'Reduced set', 'All'}; ... | ||
}; | ||
up.options.type = {'class'; | ||
'class'; ... | ||
'continuous'; ... | ||
'continuous'; ... | ||
'continuous'; ... | ||
'class'; ... | ||
%'class'; ... | ||
}; | ||
up.options.alg_abbr = {{'MSPTDPC1', 'MSPTDPC2', 'MSPTDPC5'}; ... | ||
{'MSPTDPC3', 'MSPTDPC5'}; ... | ||
{'MSPTDPC12', 'MSPTDPC4', 'MSPTDPC5'}; ... | ||
{'MSPTDPC8', 'MSPTDPC7', 'MSPTDPC6', 'MSPTDPC5'}; ... | ||
{'MSPTDPC9', 'MSPTDPC10', 'MSPTDPC5', 'MSPTDPC11', 'MSPTDPC14'}; ... | ||
{'MSPTD', 'MSPTDPC13', 'qppgfast'}; ... | ||
%{'MSPTDPC15', 'MSPTDPC5'}; ... | ||
}; | ||
up.options.opt_config = {'onsets'; ... | ||
'nested loops'; ... | ||
'HRmin=30'; ... | ||
'20'; ... | ||
'8'; ... | ||
'MSPTDfast'; ... | ||
%'Reduced set';... | ||
}; | ||
|
||
end | ||
|
||
function generate_results_plots(res, up) | ||
|
||
% setup plots | ||
ftsize = 14; | ||
lwidth = 2; | ||
mksize = 8; | ||
plot_size = [20,20,600,350]; | ||
do_quartiles = false; | ||
|
||
% go through each potential improvement | ||
for imp_no = 1 : length(up.options.name) | ||
|
||
% extract results for this option | ||
res_rows = []; | ||
no_options = length(up.options.alg_abbr{imp_no}); | ||
for option_no = 1 : no_options | ||
res_rows = [res_rows, find(strcmp(res.noQual.num.strategy, up.options.alg_abbr{imp_no}{option_no}))]; | ||
end | ||
rel_res.f1 = [res.noQual.num.f1_score_med(res_rows), res.noQual.num.f1_score_lq(res_rows), res.noQual.num.f1_score_uq(res_rows)]; | ||
rel_res.runtime = [res.noQual.num.perc_time_med(res_rows), res.noQual.num.perc_time_lq(res_rows), res.noQual.num.perc_time_uq(res_rows)]; | ||
|
||
% identify optimal config | ||
if ~isempty(up.options.opt_config{imp_no}) | ||
opt_config = find(strcmp(up.options.values{imp_no}, up.options.opt_config{imp_no})); | ||
else | ||
opt_config = []; | ||
end | ||
|
||
% make plot | ||
figure('Position', plot_size) | ||
yyaxis left | ||
plot(1:no_options, rel_res.runtime(:,1), 'o-b', 'LineWidth', lwidth, 'MarkerSize', mksize - 3) | ||
hold on | ||
if do_quartiles | ||
for q_no = 1:2 | ||
plot(1:no_options, rel_res.runtime(:,1+q_no), '--b', 'LineWidth', lwidth-0.5) | ||
end | ||
end | ||
plot(opt_config, rel_res.runtime(opt_config, 1), 'sb', 'LineWidth', lwidth, 'MarkerSize', mksize+4) | ||
ylabel('Execution time (%)', 'FontSize', ftsize+2) | ||
if max(rel_res.runtime(:,1)) < 0.03 | ||
ylim([0.00, 0.03]) | ||
elseif max(rel_res.runtime(:,1)) < 0.05 | ||
ylim([0.00, 0.05]) | ||
elseif max(rel_res.runtime(:,1)) < 0.06 | ||
ylim([0.00, 0.06]) | ||
elseif max(rel_res.runtime(:,1)) < 0.07 | ||
ylim([0.00, 0.07]) | ||
else | ||
ylim([0.00, 0.10]) | ||
end | ||
yyaxis right | ||
plot(1:no_options, rel_res.f1(:,1), 'o-r', 'LineWidth', lwidth, 'MarkerSize', mksize - 3) | ||
hold on | ||
if do_quartiles | ||
for q_no = 1:2 | ||
plot(1:no_options, rel_res.f1(:,1+q_no), '--r', 'LineWidth', lwidth-0.5) | ||
end | ||
end | ||
plot(opt_config, rel_res.f1(opt_config, 1), 'sr', 'LineWidth', lwidth, 'MarkerSize', mksize+4) | ||
ylabel('F1-score (%)', 'FontSize', ftsize+2) | ||
if do_quartiles | ||
ylim([95 100]) | ||
elseif contains(up.paths.stats_file, 'lunchbreak') || contains(up.paths.stats_file, 'lunch_break') | ||
ylim([85 88]) | ||
else | ||
ylim([97 99]) | ||
end | ||
xlim([0.5, no_options+0.5]) | ||
set(gca, 'FontSize', ftsize, 'XTick', 1:no_options, 'XTickLabel', up.options.values{imp_no}) | ||
xlabel(up.options.label{imp_no}, 'FontSize', ftsize+2) | ||
ax = gca; | ||
ax.YAxis(1).Color = 'b'; | ||
ax.YAxis(2).Color = 'r'; | ||
box off | ||
|
||
% save plot | ||
print([up.paths.plot_folder, up.options.name{imp_no}], '-depsc') | ||
close all | ||
|
||
% output any text results for this improvement | ||
if strcmp(up.options.name{imp_no}, 'lmss') | ||
rel_stat = 'runtime'; rel_or_abs = 'rel'; | ||
orig_config_name = 'peaks and onsets'; | ||
curr_config_name = 'peaks'; | ||
pk_reduction_runtime = calc_reduction(rel_res, rel_stat, imp_no, orig_config_name, curr_config_name, up, rel_or_abs); | ||
rel_stat = 'f1'; rel_or_abs = 'abs'; | ||
pk_reduction_f1 = calc_reduction(rel_res, rel_stat, imp_no, orig_config_name, curr_config_name, up, rel_or_abs); | ||
rel_stat = 'runtime'; rel_or_abs = 'rel'; | ||
orig_config_name = 'peaks and onsets'; | ||
curr_config_name = 'onsets'; | ||
onset_reduction_runtime = calc_reduction(rel_res, rel_stat, imp_no, orig_config_name, curr_config_name, up, rel_or_abs); | ||
rel_stat = 'f1'; rel_or_abs = 'abs'; | ||
onset_reduction_f1 = calc_reduction(rel_res, rel_stat, imp_no, orig_config_name, curr_config_name, up, rel_or_abs); | ||
fprintf('\nCalculating only a single LMS corresponding to either peaks or onsets resulted in substantial reductions in execution time of %.1f\\%% or %.1f\\%% respectively, compared to calculating LMSs for both peaks and onsets', pk_reduction_runtime, onset_reduction_runtime) | ||
fprintf('\nThis was accompanied by reductions in \\fscore of %.1f%% or %.1f%%', pk_reduction_f1, onset_reduction_f1); | ||
elseif strcmp(up.options.name{imp_no}, 'lms_scales') | ||
rel_stat = 'runtime'; rel_or_abs = 'rel'; | ||
orig_config_name = 'N/2'; | ||
curr_config_name = 'HRmin=40'; | ||
forty_reduction_runtime = calc_reduction(rel_res, rel_stat, imp_no, orig_config_name, curr_config_name, up, rel_or_abs); | ||
rel_stat = 'f1'; rel_or_abs = 'abs'; | ||
forty_reduction_f1 = calc_reduction(rel_res, rel_stat, imp_no, orig_config_name, curr_config_name, up, rel_or_abs); | ||
rel_stat = 'runtime'; rel_or_abs = 'rel'; | ||
orig_config_name = 'N/2'; | ||
curr_config_name = 'HRmin=30'; | ||
thirty_reduction_runtime = calc_reduction(rel_res, rel_stat, imp_no, orig_config_name, curr_config_name, up, rel_or_abs); | ||
rel_stat = 'f1'; rel_or_abs = 'abs'; | ||
thirty_reduction_f1 = calc_reduction(rel_res, rel_stat, imp_no, orig_config_name, curr_config_name, up, rel_or_abs); | ||
fprintf('\nReducing the number of LMS scales substantially reduced the execution time by %.1f\\%% (with $HR_{min}=30$) or %.1f\\%% (with $HR_{min}=40$).', forty_reduction_runtime, thirty_reduction_runtime) | ||
fprintf('\nThis was accompanied by reductions in \\fscore of %.1f%% or %.1f%%', forty_reduction_f1, thirty_reduction_f1); | ||
elseif strcmp(up.options.name{imp_no}, 'fs') | ||
orig_config_name = 'orig'; | ||
rel_stat = 'runtime'; rel_or_abs = 'rel'; | ||
curr_config_name = '30'; | ||
thirty_reduction_runtime = calc_reduction(rel_res, rel_stat, imp_no, orig_config_name, curr_config_name, up, rel_or_abs); | ||
rel_stat = 'f1'; rel_or_abs = 'abs'; | ||
thirty_reduction_f1 = calc_reduction(rel_res, rel_stat, imp_no, orig_config_name, curr_config_name, up, rel_or_abs); | ||
rel_stat = 'runtime'; rel_or_abs = 'rel'; | ||
curr_config_name = '20'; | ||
twenty_reduction_runtime = calc_reduction(rel_res, rel_stat, imp_no, orig_config_name, curr_config_name, up, rel_or_abs); | ||
rel_stat = 'f1'; rel_or_abs = 'abs'; | ||
twenty_reduction_f1 = calc_reduction(rel_res, rel_stat, imp_no, orig_config_name, curr_config_name, up, rel_or_abs); | ||
rel_stat = 'runtime'; rel_or_abs = 'rel'; | ||
curr_config_name = '10'; | ||
ten_reduction_runtime = calc_reduction(rel_res, rel_stat, imp_no, orig_config_name, curr_config_name, up, rel_or_abs); | ||
rel_stat = 'f1'; rel_or_abs = 'abs'; | ||
ten_reduction_f1 = calc_reduction(rel_res, rel_stat, imp_no, orig_config_name, curr_config_name, up, rel_or_abs); | ||
fprintf('\nReducing the sampling frequency substantially reduced execution time, with values of 30, 20, and 10 Hz reducing execution time by by %.1f\\%%, %.1f\\%% and %.1f\\%% respectively.', thirty_reduction_runtime, twenty_reduction_runtime, ten_reduction_runtime) | ||
fprintf('\nThese were accompanied by reductions in \\fscore of %.1f\\%%, %.1f\\%% and %.1f\\%%', thirty_reduction_f1, twenty_reduction_f1, ten_reduction_f1); | ||
elseif strcmp(up.options.name{imp_no}, 'algs') | ||
orig_config_name = 'MSPTD'; | ||
rel_stat = 'runtime'; rel_or_abs = 'rel'; | ||
curr_config_name = 'MSPTDfast'; | ||
msptdfast_reduction_runtime = calc_reduction(rel_res, rel_stat, imp_no, orig_config_name, curr_config_name, up, rel_or_abs); | ||
rel_stat = 'f1'; rel_or_abs = 'abs'; | ||
msptdfast_reduction_f1 = calc_reduction(rel_res, rel_stat, imp_no, orig_config_name, curr_config_name, up, rel_or_abs); | ||
fprintf('\nThe new \\msptdfast algorithm had a execution time of approximately one-third (%.1f\\%%) of the previous \\msptd algorithm', 100-msptdfast_reduction_runtime) | ||
f1_msptd = rel_res.f1(find(strcmp(up.options.values{imp_no}, 'MSPTD')),1); | ||
f1_msptdfast = rel_res.f1(find(strcmp(up.options.values{imp_no}, 'MSPTDfast')),1); | ||
fprintf('\nThis was achieved at the expense of a very small reduction in \\fscore of %.1f\\%% (%.1f\\%% vs. %.1f\\%%)', msptdfast_reduction_f1, f1_msptd, f1_msptdfast); | ||
orig_config_name = 'qppgfast'; | ||
rel_stat = 'runtime'; rel_or_abs = 'rel'; | ||
curr_config_name = 'MSPTDfast'; | ||
msptdfast_reduction_runtime = calc_reduction(rel_res, rel_stat, imp_no, orig_config_name, curr_config_name, up, rel_or_abs); | ||
f1_qppgfast = rel_res.f1(find(strcmp(up.options.values{imp_no}, 'qppgfast')),1); | ||
fprintf('In comparison to \\qppgfast, \\msptdfast had a slightly longer execution time (%.1f\\%% longer) and a comparable \\fscore (%.1f\\%% vs. %.1f\\%%).', -1*msptdfast_reduction_runtime, f1_qppgfast, f1_msptdfast); | ||
orig_config_name = 'qppgfast'; | ||
rel_stat = 'runtime'; rel_or_abs = 'rel'; | ||
curr_config_name = 'MSPTD'; | ||
msptd_reduction_runtime = calc_reduction(rel_res, rel_stat, imp_no, orig_config_name, curr_config_name, up, rel_or_abs); | ||
fprintf('\nThe \\msptd algorithm had a %.1f\\%% longer execution time.', -1*msptd_reduction_runtime); | ||
|
||
end | ||
|
||
end | ||
|
||
|
||
|
||
|
||
end | ||
|
||
function red = calc_reduction(rel_res, rel_stat, imp_no, orig_config_name, curr_config_name, up, rel_or_abs) | ||
|
||
% identify stat to be used | ||
eval(['stat = rel_res.' rel_stat ';']) | ||
|
||
% extract results for the original config and comparator config | ||
orig_config = find(strcmp(up.options.values{imp_no}, orig_config_name)); | ||
curr_config = find(strcmp(up.options.values{imp_no}, curr_config_name)); | ||
|
||
% calculate reduction | ||
if strcmp(rel_or_abs, 'rel') | ||
red = 100*(stat(orig_config) - stat(curr_config))/stat(orig_config); | ||
elseif strcmp(rel_or_abs, 'abs') | ||
red = stat(orig_config) - stat(curr_config); | ||
end | ||
|
||
end |