Calculations/Data-Analyzer/+Plotter/plotSecondaryGaussianRange.m

167 lines
5.8 KiB
Matlab
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

function plotSecondaryGaussianRange(fitResults, scanValues, varargin)
%% plotSecondaryGaussianRange
% Plots secondary Gaussian ranges (mu2 ± sigma2) in a heatmap style
% exactly like plotFitParameterPDF, with dashed lines for distribution
% edges and solid line for mean mu2.
% --- Parse optional inputs ---
p = inputParser;
addParameter(p, 'Title', '', @(x) ischar(x) || isstring(x));
addParameter(p, 'XLabel', '', @(x) ischar(x) || isstring(x));
addParameter(p, 'YLabel', '', @(x) ischar(x) || isstring(x));
addParameter(p, 'FigNum', 1, @(x) isscalar(x));
addParameter(p, 'FontName', 'Arial', @ischar);
addParameter(p, 'FontSize', 14, @isnumeric);
addParameter(p, 'SkipSaveFigures', false, @islogical);
addParameter(p, 'SaveFileName', 'SecondaryGaussianRange.fig', @ischar);
addParameter(p, 'SaveDirectory', pwd, @ischar);
addParameter(p, 'NumPoints', 200, @(x) isscalar(x));
addParameter(p, 'DataRange', [], @(x) isempty(x) || numel(x)==2);
addParameter(p, 'XLim', [], @(x) isempty(x) || numel(x)==2);
addParameter(p, 'YLim', [], @(x) isempty(x) || numel(x)==2);
addParameter(p, 'Colormap', @jet);
addParameter(p, 'PlotType', 'histogram', @(x) any(validatestring(x,{'kde','histogram'})));
addParameter(p, 'NumberOfBins', 50, @isscalar);
addParameter(p, 'NormalizeHistogram', true, @islogical);
addParameter(p, 'OverlayMeanSEM', true, @islogical);
parse(p, varargin{:});
opts = p.Results;
% --- Determine repetitions and scan parameters ---
N_params = numel(scanValues);
N_total = numel(fitResults);
N_reps = N_total / N_params;
% --- Extract mu2 and sigma2 ---
mu2_values = nan(N_reps, N_params);
sigma2_values = nan(N_reps, N_params);
for k = 1:N_total
paramIdxScan = mod(k-1, N_params) + 1;
repIdx = floor((k-1)/N_params) + 1;
mu2_values(repIdx, paramIdxScan) = fitResults(k).pFit(5);
sigma2_values(repIdx, paramIdxScan) = fitResults(k).pFit(6);
end
% --- Mask invalid fits ---
for i = 1:N_total
paramIdxScan = mod(i-1, N_params) + 1;
repIdx = floor((i-1)/N_params) + 1;
if ~fitResults(i).isValid && all(fitResults(i).pFit == 0)
mu2_values(repIdx, paramIdxScan) = NaN;
sigma2_values(repIdx, paramIdxScan) = NaN;
end
end
% --- Compute lower and upper bounds ---
lowerBound = mu2_values - sigma2_values;
upperBound = mu2_values + sigma2_values;
% Define how much to trim (e.g., 2% on each side)
trimFraction = 0.01; % can tune this (0.010.05 typical)
% Compute robust global limits
globalLower = quantile(lowerBound(:), trimFraction);
globalUpper = quantile(upperBound(:), 1 - trimFraction);
fprintf('Global lower bound (%.0f%%): %.4f\n', trimFraction*100, globalLower);
fprintf('Global upper bound (%.0f%%): %.4f\n', (1-trimFraction)*100, globalUpper);
% --- Prepare data per scan parameter ---
dataCell = cell(N_params,1);
for i = 1:N_params
combinedData = [];
for j = 1:N_reps
if ~isnan(mu2_values(j,i)) && ~isnan(sigma2_values(j,i))
combinedData = [combinedData; linspace(lowerBound(j,i), upperBound(j,i), 3)']; %#ok<AGROW>
end
end
dataCell{i} = combinedData;
end
% --- Determine y-range ---
if isempty(opts.DataRange)
allData = cell2mat(dataCell(:));
y_min = min(allData);
y_max = max(allData);
else
y_min = opts.DataRange(1);
y_max = opts.DataRange(2);
end
% --- Prepare PDF matrix ---
if strcmpi(opts.PlotType,'kde')
y_grid = linspace(y_min, y_max, opts.NumPoints);
pdf_matrix = zeros(numel(y_grid), N_params);
for i = 1:N_params
data = dataCell{i};
if isempty(data), continue; end
pdf_matrix(:,i) = ksdensity(data, y_grid);
end
else
edges = linspace(y_min, y_max, opts.NumberOfBins+1);
binCenters = (edges(1:end-1)+edges(2:end))/2;
pdf_matrix = zeros(numel(binCenters), N_params);
for i = 1:N_params
data = dataCell{i};
if isempty(data), continue; end
counts = histcounts(data, edges);
if opts.NormalizeHistogram
counts = counts / sum(counts);
end
pdf_matrix(:,i) = counts(:);
end
end
% --- Mask out non-data regions ---
maskPerScan = cellfun(@(c) ~isempty(c), dataCell);
pdf_matrix(:, ~maskPerScan) = NaN;
% --- Plot heatmap ---
fig = figure(opts.FigNum); clf(fig);
set(fig, 'Color', 'w', 'Position', [100 100 950 750]);
if strcmpi(opts.PlotType,'kde')
h = imagesc(scanValues, y_grid, pdf_matrix);
set(h, 'AlphaData', ~isnan(pdf_matrix));
else
h = imagesc(scanValues, binCenters, pdf_matrix);
set(h, 'AlphaData', ~isnan(pdf_matrix));
end
colormap([1 1 1; feval(opts.Colormap)]); % white for NaNs
c = colorbar;
ylabel(c, 'Probability Density', 'Rotation', -90, 'FontName', opts.FontName, 'FontSize', opts.FontSize);
set(gca, 'YDir','normal', 'FontName', opts.FontName, 'FontSize', opts.FontSize);
xlabel(opts.XLabel, 'FontName', opts.FontName, 'FontSize', opts.FontSize);
ylabel(opts.YLabel, 'FontName', opts.FontName, 'FontSize', opts.FontSize);
title(opts.Title, 'FontName', opts.FontName, 'FontSize', opts.FontSize+2, 'FontWeight','bold');
grid on; hold on;
% --- Overlay mu2 and distribution edges ---
meanMu2 = nanmean(mu2_values,1);
minLower = min(lowerBound,[],1);
maxUpper = max(upperBound,[],1);
plot(scanValues, meanMu2, 'k-', 'LineWidth', 2); % solid mu2
plot(scanValues, minLower, 'k--', 'LineWidth', 1.5); % dashed lower edge
plot(scanValues, maxUpper, 'k--', 'LineWidth', 1.5); % dashed upper edge
% --- Overlay mean ± SEM if requested ---
if opts.OverlayMeanSEM
semMu2 = nanstd(mu2_values,0,1) ./ sqrt(sum(~isnan(mu2_values),1));
xVec = reshape(scanValues,1,[]);
fill([xVec, fliplr(xVec)], [meanMu2 - semMu2, fliplr(meanMu2 + semMu2)], ...
[0.2 0.4 0.8], 'FaceAlpha',0.2, 'EdgeColor','none');
end
% --- Apply axis limits if specified ---
if ~isempty(opts.XLim)
xlim(opts.XLim);
end
if ~isempty(opts.YLim)
ylim(opts.YLim);
end
end