diff --git a/+nla/+edge/+result/Base.m b/+nla/+edge/+result/Base.m index 39de0ad0..e582cf2a 100755 --- a/+nla/+edge/+result/Base.m +++ b/+nla/+edge/+result/Base.m @@ -44,7 +44,7 @@ function output(obj, net_atlas, flags, prob_label) fig = nla.gfx.createFigure(); matrix_plot = nla.gfx.plots.MatrixPlot(fig, coeff_label, obj.coeff, net_atlas.nets, nla.gfx.FigSize.LARGE, 'lower_limit', obj.coeff_range(1),... - 'upper_limit', obj.coeff_range(2)); + 'upper_limit', obj.coeff_range(2), 'app_plot', false); matrix_plot.displayImage(); w = matrix_plot.image_dimensions("image_width"); h = matrix_plot.image_dimensions("image_height"); diff --git a/+nla/+gfx/+plots/MatrixPlot.m b/+nla/+gfx/+plots/MatrixPlot.m index 0b90ec04..a43b8ee2 100644 --- a/+nla/+gfx/+plots/MatrixPlot.m +++ b/+nla/+gfx/+plots/MatrixPlot.m @@ -35,6 +35,7 @@ current_settings % the settings for the plot (upper, lower, scale) display_legend p_value_max + app_plot % Used when called for FC Avg end properties (Dependent) @@ -112,12 +113,13 @@ addParameter(matrix_input_parser, 'y_position', 0, validNumberInput); addParameter(matrix_input_parser, 'discrete_colorbar', false, @islogical); addParameter(matrix_input_parser, 'plot_scale', nla.gfx.ProbPlotMethod.DEFAULT); - addParameter(matrix_input_parser, 'p_value_max', 0.05) + addParameter(matrix_input_parser, 'p_value_max', 0.05); + addParameter(matrix_input_parser, 'app_plot', true, @islogical) parse(matrix_input_parser, figure, name, matrix, networks, figure_size, varargin{:}); properties = {'figure', 'name', 'matrix', 'networks', 'figure_size', 'network_clicked_callback',... 'marked_networks', 'figure_margins', 'draw_legend', 'draw_colorbar', 'color_map', 'lower_limit',... - 'upper_limit', 'x_position', 'y_position', 'discrete_colorbar', 'plot_scale', 'p_value_max'}; + 'upper_limit', 'x_position', 'y_position', 'discrete_colorbar', 'plot_scale', 'p_value_max', 'app_plot'}; for property = properties obj.(property{1}) = matrix_input_parser.Results.(property{1}); if property{1} == "marked_networks" @@ -182,6 +184,18 @@ function displayImage(obj) 'HorizontalAlignment', 'center'); end + if isequal(obj.app_plot, false) + settings_menu = uimenu(obj.figure, 'Text', 'Settings'); + change_scale_option = uimenu(settings_menu,'Text','Change Scale'); + change_scale_option.MenuSelectedFcn = {... + @obj.adjustScale, [obj.lower_limit, obj.upper_limit], obj.figure,... + struct('lower_bound', obj.lower_limit, 'upper_bound', obj.upper_limit, 'p_value_plot_max', obj.p_value_max),... + false... + }; + change_color_map = uimenu(settings_menu, 'Text', 'Change Color Map'); + change_color_map.MenuSelectedFcn = {@obj.adjustColor, obj.figure, struct('color_map', obj.color_map, 'color_map_name', "")}; + end + obj.fixRendering(); hold(obj.axes, 'off') % This may have been turned on. Does nothing if it wasn't. @@ -196,7 +210,7 @@ function applyScale(obj, ~, ~, upper_limit_box, lower_limit_box, old_scale, new_ obj.color_bar.Ticks = []; color_map = color_map_select; - if ~isstring(color_map_select) && ~ischar(color_map_select) + if ~isstring(color_map_select) && ~ischar(color_map_select) && ~isequal(color_map, false) color_map = get(color_map_select, "Value"); color_map = obj.colormap_choices{color_map}; end @@ -207,13 +221,20 @@ function applyScale(obj, ~, ~, upper_limit_box, lower_limit_box, old_scale, new_ if isnumeric(upper_limit_box) obj.upper_limit = upper_limit_box; else - obj.upper_limit = get(upper_limit_box, "String"); + obj.upper_limit = get(upper_limit_box, 'String'); end if isnumeric(lower_limit_box) obj.lower_limit = lower_limit_box; else - obj.lower_limit = get(lower_limit_box, "String"); + obj.lower_limit = get(lower_limit_box, 'String'); + end + + if isstring(obj.upper_limit) || ischar(obj.upper_limit) + obj.upper_limit = str2double(obj.upper_limit); + end + if isstring(obj.lower_limit) || ischar(obj.lower_limit) + obj.lower_limit = str2double(obj.lower_limit); end if ~isstring(obj.plot_scale) @@ -239,7 +260,11 @@ function applyScale(obj, ~, ~, upper_limit_box, lower_limit_box, old_scale, new_ discrete_colors = NetworkResultPlotParameter().default_discrete_colors; if new_scale == "nla.gfx.ProbPlotMethod.DEFAULT" - new_color_map = NetworkResultPlotParameter.getColormap(discrete_colors, obj.upper_limit, color_map); + if ~isequal(color_map, false) + new_color_map = NetworkResultPlotParameter.getColormap(discrete_colors, obj.upper_limit, color_map); + else + new_color_map = obj.color_map; + end obj.plot_scale = new_scale; elseif new_scale == "nla.gfx.ProbPlotMethod.LOG" new_color_map = NetworkResultPlotParameter.getLogColormap(discrete_colors, obj.matrix, obj.upper_limit, color_map); @@ -337,7 +362,7 @@ function removeLegend(obj) image_height = image_height + 20; end - % colorbar margins + % colorbar marginsobj if obj.draw_colorbar image_width = image_width + obj.colorbar_width + obj.colorbar_offset + obj.colorbar_text_w; end @@ -435,10 +460,10 @@ function addCallback(obj, x) initial_render = false; upper_value = varargin{2}; lower_value = varargin{1}; - if isstring(upper_value) + if isstring(upper_value) || ischar(upper_value) % why are strings not chars??? upper_value = str2double(upper_value); end - if isstring(lower_value) + if isstring(lower_value) || ischar(lower_value) lower_value = str2double(lower_value); end end @@ -677,5 +702,22 @@ function applyColorToData(obj, position_x, position_y, chunk_height, chunk_width obj.image_display.CData(position_y:position_y + chunk_height - 1, position_x + chunk_width, :) =... repelem(chunk_color(1:size(chunk_color, 1), size(chunk_color, 2), :), obj.elementSize(), 1); end + + function adjustScale(obj, src, ~, bounds, plot_figure, parameters, ~) + nla.gfx.scaleSelector(src, bounds, plot_figure, parameters, false, @obj.applyScaleWrapper); + end + + function adjustColor(obj, src, ~, plot_figure, parameters) + nla.gfx.colorSelector(src, plot_figure, parameters, false, @obj.applyScaleWrapper, obj.colormap_choices); + end + + function applyScaleWrapper(obj, ~, ~, upper_limit_box, lower_limit_box, color_map, ~, ~, ~) + % This is used for the edge trimatrix, fc avg which is why we keep the ProbPlotMethod to DEFAULT + if isequal(upper_limit_box, false) + upper_limit_box = obj.upper_limit; + lower_limit_box = obj.lower_limit; + end + obj.applyScale(false, false, upper_limit_box, lower_limit_box, "nla.gfx.ProbPlotMethod.DEFAULT", "nla.gfx.ProbPlotMethod.DEFAULT", color_map) + end end end diff --git a/+nla/+gfx/colorSelector.m b/+nla/+gfx/colorSelector.m new file mode 100644 index 00000000..867459db --- /dev/null +++ b/+nla/+gfx/colorSelector.m @@ -0,0 +1,46 @@ +function colorSelector(src, plot_figure, parameters, chord_type, callback_function, color_choices) + original_figure = src.Parent.Parent; + modal = figure('WindowStyle', 'normal', 'Units', 'pixels', 'Position',... + [original_figure.Position(1), original_figure.Position(2), original_figure.Position(3) / 2, original_figure.Position(4) / 3]); + + % Color Map selector + % Adapted from colormap-dropdown: https://www.mathworks.com/matlabcentral/fileexchange/43659-colormap-dropdown-menu + + color_map_select = uicontrol('Style', 'popupmenu',... + 'Position', [90, 130, 275, 30], "Units", "pixels",... + 'FontName', 'Courier'); + uicontrol("Style", "text", "string", "Colormaps", "Units", "pixels",... + "Position", [color_map_select.Position(1) - 80, color_map_select.Position(2) - 2, 80, color_map_select.Position(4)]); + initial_colors = 16; + colormap_html = {}; + for colors = 1:numel(color_choices) + colormap_function = str2func(strcat(strcat("@(x) ",lower(color_choices{colors})), "(x)")); + CData = colormap_function(initial_colors); + new_html_start = ' '; + new_html = ''; + for color_iterator = initial_colors:-1:1 + hex_code = nla.gfx.rgb2hex([CData(color_iterator, 1), CData(color_iterator, 2),... + CData(color_iterator, 3)]); + new_html = [new_html '__']; + end + new_html_end = [new_html ' ']; + new_html = [new_html_start new_html_end]; + colormap_html = [colormap_html; new_html]; + end + + if ~isfield(parameters, "color_map_name") || ischar(parameters.color_map_name) || isstring(parameters.color_map_name) + parameters.color_map_name = 1; + end + set(color_map_select, "Value", parameters.color_map_name, "String", colormap_html); + + apply_button_position = [10, 10, 100, 30]; + apply_button = uicontrol('String', 'Apply',... + "Callback", {callback_function, false, false, color_map_select, plot_figure, parameters, chord_type},... + "Units", "pixels",... + 'Position', apply_button_position); + + close_button_position = [apply_button.Position(1) + apply_button.Position(3) + 10,... + apply_button.Position(2), apply_button.Position(3), apply_button.Position(4)]; + uicontrol('String', 'Close', 'Callback', @(~, ~)close(modal), "Units", "pixels", 'Position',... + close_button_position); +end \ No newline at end of file diff --git a/+nla/+gfx/scaleSelector.m b/+nla/+gfx/scaleSelector.m new file mode 100644 index 00000000..c4d7c46a --- /dev/null +++ b/+nla/+gfx/scaleSelector.m @@ -0,0 +1,28 @@ +function scaleSelector(src, bounds, plot_figure, parameters, chord_type, callback_function) + original_figure = src.Parent.Parent; + modal = figure('WindowStyle', 'normal', 'Units', 'pixels', 'Position',... + [original_figure.Position(1), original_figure.Position(2), original_figure.Position(3) / 3, original_figure.Position(4) / 3]); + + upper_limit_box = uicontrol('Style', 'edit', "Units", "pixels", 'Position', [90, 130, 100, 30], "String",... + bounds(2)); + upper_limit_box.Position(4) = upper_limit_box.FontSize * 2; + lower_limit_box = uicontrol('Style', 'edit', "Units", "pixels", 'Position', [90, 100, 100, 30], "String",... + bounds(1)); + lower_limit_box.Position(4) = lower_limit_box.FontSize * 2; + + uicontrol('Style', 'text', 'String', 'Upper Limit', "Units", "pixels", 'Position',... + [upper_limit_box.Position(1) - 80, upper_limit_box.Position(2) - 2, 80, upper_limit_box.Position(4)]); + uicontrol('Style', 'text', 'String', 'Lower Limit', "Units", "pixels", 'Position',... + [lower_limit_box.Position(1) - 80, lower_limit_box.Position(2) - 2, 80, lower_limit_box.Position(4)]); + + apply_button_position = [10, 10, 100, 30]; + apply_button = uicontrol('String', 'Apply',... + "Callback", {callback_function, upper_limit_box, lower_limit_box, false, plot_figure, parameters, chord_type},... + "Units", "pixels",... + 'Position', apply_button_position); + + close_button_position = [apply_button.Position(1) + apply_button.Position(3) + 10,... + apply_button.Position(2), apply_button.Position(3), apply_button.Position(4)]; + uicontrol('String', 'Close', 'Callback', @(~, ~)close(modal), "Units", "pixels", 'Position',... + close_button_position); +end \ No newline at end of file diff --git a/+nla/+inputField/NetworkAtlasFuncConn.m b/+nla/+inputField/NetworkAtlasFuncConn.m index 0f1a47f8..775bc08d 100755 --- a/+nla/+inputField/NetworkAtlasFuncConn.m +++ b/+nla/+inputField/NetworkAtlasFuncConn.m @@ -322,8 +322,9 @@ function buttonViewFCAvgClickedCallback(obj) fc_avg = copy(obj.func_conn); fc_avg.v = mean(fc_avg.v, 2); fig_l = nla.gfx.createFigure(); + matrix_plot = nla.gfx.plots.MatrixPlot(fig_l, 'FC Average (Fisher Z(R))', fc_avg, obj.net_atlas.nets,... - nla.gfx.FigSize.LARGE); + nla.gfx.FigSize.LARGE, "app_plot", false); fig_l.Position(3) = matrix_plot.image_dimensions("image_width"); fig_l.Position(4) = matrix_plot.image_dimensions("image_height"); matrix_plot.displayImage(); diff --git a/+nla/+net/+result/+chord/ChordPlotter.m b/+nla/+net/+result/+chord/ChordPlotter.m index 27272895..15ff4165 100644 --- a/+nla/+net/+result/+chord/ChordPlotter.m +++ b/+nla/+net/+result/+chord/ChordPlotter.m @@ -9,6 +9,8 @@ axis_width = 750; % Constant for the size of the chord trimatrix_width = 500; % Constant for the size of the Trimatrix plotted with bottom_text_height = 250; % How far from the bottom of the trimatrix the text appears + colormap_choices = {"Parula", "Turbo", "HSV", "Hot", "Cool", "Spring", "Summer", "Autumn", "Winter", "Gray",... + "Bone", "Copper", "Pink"}; % Colorbar choices end properties @@ -16,6 +18,7 @@ edge_test_result % Edge test results split_plot = false % This is an option that is set automatically during operation. edge_plot_type = "nla.gfx.EdgeChordPlotMethod.PROB" % Default chord type for edges + chord_plotter = false end methods @@ -30,11 +33,14 @@ end end - function generateChordFigure(obj, parameters, chord_type) + function generateChordFigure(obj, parameters, chord_type, parent_figure) % generateChordFigure plots chords for a network test import nla.gfx.SigType nla.net.result.plot.PermutationTestPlotter nla.gfx.EdgeChordPlotMethod nla.gfx.setTitle coefficient_bounds = [0, parameters.p_value_plot_max]; + if isfield(parameters, "lower_bound") + coefficient_bounds(1) = parameters.lower_bound; + end if parameters.significance_type == SigType.INCREASING && parameters.p_value_plot_max < 1 coefficient_bounds = [parameters.p_value_plot_max, 1]; end @@ -54,6 +60,10 @@ function generateChordFigure(obj, parameters, chord_type) plot_figure = nla.gfx.createFigure(obj.axis_width , obj.axis_width); end + if ~isequal(parent_figure, false) + nla.gfx.moveFigToParentUILocation(plot_figure, parent_figure); + end + % Plot a standard chord plot if chord_type == "nla.PlotType.CHORD" figure_axis = axes(plot_figure, 'Units', 'pixels', 'Position', [0, 0, obj.axis_width,... @@ -68,26 +78,38 @@ function generateChordFigure(obj, parameters, chord_type) % thresholding below the "insignificance" value statistic_matrix = copy(parameters.statistic_plot_matrix); statistic_matrix.v(~parameters.significance_plot.v) = insignificance; - chord_plotter = nla.gfx.chord.ChordPlot(obj.network_atlas, figure_axis, 500, statistic_matrix,... + obj.chord_plotter = nla.gfx.chord.ChordPlot(obj.network_atlas, figure_axis, 500, statistic_matrix,... 'color_map', parameters.color_map, 'direction', parameters.significance_type, 'upper_limit',... coefficient_bounds(2), 'lower_limit', coefficient_bounds(1), 'chord_type', chord_type); - chord_plotter.drawChords(); + obj.chord_plotter.drawChords(); setTitle(figure_axis, parameters.name_label) + settings_menu = uimenu(plot_figure, 'Text', 'Settings'); + change_scale_option = uimenu(settings_menu,'Text','Change Scale'); + change_scale_option.MenuSelectedFcn = {@obj.adjustScale, coefficient_bounds, plot_figure, parameters, chord_type}; + change_color_map = uimenu(settings_menu, 'Text', 'Change Color Map'); + change_color_map.MenuSelectedFcn = {@obj.adjustColor, plot_figure, parameters, chord_type}; else % Plot edge chord - obj.generateEdgeChordFigure(plot_figure, parameters, chord_type) + obj.generateEdgeChordFigure(plot_figure, parameters, chord_type, parent_figure) end + + end end methods (Access = protected) - function generateEdgeChordFigure(obj, plot_figure, parameters, chord_type) + function generateEdgeChordFigure(obj, plot_figure, parameters, chord_type, parent_figure) % generateEdgeChordFigure generates the edge chord plotting import nla.gfx.EdgeChordPlotMethod nla.gfx.setTitle - range_limit = std(obj.edge_test_result.coeff.v) * 5; - coefficient_min = -range_limit; - coefficient_max = range_limit; + if isfield(parameters, "lower_bound") + coefficient_min = parameters.lower_bound; + coefficient_max = parameters.p_value_plot_max; + else + range_limit = std(obj.edge_test_result.coeff.v) * 5; + coefficient_min = -range_limit; + coefficient_max = range_limit; + end clipped_values = nla.TriMatrix(obj.network_atlas.numROIs(), nla.TriMatrixDiag.REMOVE_DIAGONAL); clipped_values.v = obj.edge_test_result.coeff.v; @@ -96,7 +118,7 @@ function generateEdgeChordFigure(obj, plot_figure, parameters, chord_type) clipped_values_positive.v = obj.edge_test_result.coeff.v; clipped_values_positive.v(obj.edge_test_result.coeff.v < 0) = 0; - color_map = turbo(1000); + color_map = parula(1000); significance_type = "nla.gfx.SigType.ABS_INCREASING"; insignificance = 0; % This is basically the background for the plot % This is the title for the positive (or non-split) chord plot @@ -110,21 +132,39 @@ function generateEdgeChordFigure(obj, plot_figure, parameters, chord_type) switch obj.edge_plot_type case "nla.gfx.EdgeChordPlotMethod.COEFF" main_title = positive_title; + if isfield(parameters, "lower_bound") + coefficient_min = parameters.lower_bound; + coefficient_max = parameters.p_value_plot_max; + end case "nla.gfx.EdgeChordPlotMethod.COEFF_SPLIT" main_title = negative_title; positive_main_title = positive_title; + if isfield(parameters, "lower_bound") + coefficient_min = parameters.lower_bound; + coefficient_max = parameters.p_value_plot_max; + end case "nla.gfx.EdgeChordPlotMethod.COEFF_BASE_SPLIT" - coefficient_min = obj.edge_test_result.coeff_range(1); - coefficient_max = obj.edge_test_result.coeff_range(2); + if isfield(parameters, "lower_bound") + coefficient_min = parameters.lower_bound; + coefficient_max = parameters.p_value_plot_max; + else + coefficient_min = obj.edge_test_result.coeff_range(1); + coefficient_max = obj.edge_test_result.coeff_range(2); + end main_title = negative_title; positive_main_title = positive_title; case "nla.gfx.edgeChordPlotMethod.COEFF_BASE" - coefficient_min = obj.edge_test_result.coeff_range(1); - coefficient_max = obj.edge_test_result.coeff_range(2); + if isfield(parameters, "lower_bound") + coefficient_min = parameters.lower_bound; + coefficient_max = parameters.p_value_plot_max; + else + coefficient_min = obj.edge_test_result.coeff_range(1); + coefficient_max = obj.edge_test_result.coeff_range(2); + end main_title = positive_title; @@ -134,13 +174,19 @@ function generateEdgeChordFigure(obj, plot_figure, parameters, chord_type) clipped_values.v = obj.edge_test_result.prob.v; significance_type = "nla.gfx.SigType.DECREASING"; - coefficient_min = 0; - coefficient_max = obj.edge_test_result.prob_max; + if isfield(parameters, "lower_bound") + coefficient_min = parameters.lower_bound; + coefficient_max = parameters.p_value_plot_max; + else + coefficient_min = 0; + coefficient_max = obj.edge_test_result.prob_max; + end insignificance = 1; main_title = sprintf("Edge-level P-values (P < %g) (Within Significant Net-Pair)",... obj.edge_test_result.prob_max); end + coefficient_bounds = [coefficient_min, coefficient_max]; % Filtering/Thresholding out values for network1 = 1:obj.network_atlas.numNets() @@ -165,8 +211,8 @@ function generateEdgeChordFigure(obj, plot_figure, parameters, chord_type) obj.edge_plot_type == "nla.gfx.EdgeChordPlotMethod.COEFF_SPLIT" || obj.edge_plot_type == "nla.gfx.EdgeChordPlotMethod.COEFF_BASE_SPLIT"... ) positive_chord_plotter = nla.gfx.chord.ChordPlot(obj.network_atlas, plot_axis, 450, clipped_values_positive,... - 'direction', significance_type, 'chord_type', chord_type, 'color_map', color_map, 'lower_limit',... - coefficient_min, 'upper_limit', coefficient_max); + 'direction', significance_type, 'chord_type', chord_type, 'color_map', parameters.color_map, 'lower_limit',... + coefficient_bounds(1), 'upper_limit', coefficient_bounds(2)); positive_chord_plotter.drawChords(); setTitle(plot_axis, positive_main_title); @@ -177,12 +223,12 @@ function generateEdgeChordFigure(obj, plot_figure, parameters, chord_type) plot_axis.Visible = true; end - chord_plotter = nla.gfx.chord.ChordPlot(obj.network_atlas, plot_axis, 450, clipped_values, 'chord_type', chord_type,... - 'direction', significance_type, 'color_map', color_map, 'lower_limit', coefficient_min, 'upper_limit', coefficient_max); - chord_plotter.drawChords(); + obj.chord_plotter = nla.gfx.chord.ChordPlot(obj.network_atlas, plot_axis, 450, clipped_values, 'chord_type', chord_type,... + 'direction', significance_type, 'color_map', parameters.color_map, 'lower_limit', coefficient_bounds(1), 'upper_limit', coefficient_bounds(2)); + obj.chord_plotter.drawChords(); setTitle(plot_axis, main_title); - colormap(plot_axis, color_map); + colormap(plot_axis, parameters.color_map); color_bar = colorbar(plot_axis); color_bar.Units = 'pixels'; color_bar.Location = 'east'; @@ -198,6 +244,40 @@ function generateEdgeChordFigure(obj, plot_figure, parameters, chord_type) labels{tick + 1} = sprintf("%.2g", coefficient_min + (tick * ((coefficient_max - coefficient_min) / number_ticks))); end color_bar.TickLabels = labels; + + settings_menu = uimenu(plot_figure, 'Text', 'Settings'); + change_scale_option = uimenu(settings_menu,'Text','Change Scale'); + change_scale_option.MenuSelectedFcn = {@obj.adjustScale, coefficient_bounds, plot_figure, parameters, chord_type}; + change_color_map = uimenu(settings_menu, 'Text', 'Change Color Map'); + change_color_map.MenuSelectedFcn = {@obj.adjustColor, plot_figure, parameters, chord_type}; + end + + function adjustScale(obj, src, ~, bounds, plot_figure, parameters, chord_type) + nla.gfx.scaleSelector(src, bounds, plot_figure, parameters, chord_type, @obj.applyScale); + end + + function adjustColor(obj, src, ~, plot_figure, parameters, chord_type) + nla.gfx.colorSelector(src, plot_figure, parameters, chord_type, @obj.applyScale, obj.colormap_choices); + end + + function applyScale(obj, src, ~, upper_limit_box, lower_limit_box, color_map, plot_figure, parameters, chord_type) + + if ~isequal(upper_limit_box, false) + parameters.p_value_plot_max = str2double(get(upper_limit_box, "String")); + parameters.lower_bound = str2double(get(lower_limit_box, "String")); + end + + if ~isequal(color_map, false) + color_map = get(color_map, "Value"); + color_map_name = str2func(lower(obj.colormap_choices{color_map})); + parameters.color_map = flip(color_map_name(1000)); + parameters.color_map_name = color_map; + end + + obj.generateChordFigure(parameters, chord_type, plot_figure) + + close(plot_figure); + close(src.Parent); end end end \ No newline at end of file diff --git a/+nla/+net/+result/+plot/NetworkTestPlotApp.mlapp b/+nla/+net/+result/+plot/NetworkTestPlotApp.mlapp index 17c2e792..58732f76 100644 Binary files a/+nla/+net/+result/+plot/NetworkTestPlotApp.mlapp and b/+nla/+net/+result/+plot/NetworkTestPlotApp.mlapp differ diff --git a/+nla/+net/+result/+plot/NetworkTestPlotApp_exported.m b/+nla/+net/+result/+plot/NetworkTestPlotApp_exported.m index ba7b11aa..42ce990b 100644 --- a/+nla/+net/+result/+plot/NetworkTestPlotApp_exported.m +++ b/+nla/+net/+result/+plot/NetworkTestPlotApp_exported.m @@ -24,9 +24,9 @@ MultipleComparisonCorrectionDropDown matlab.ui.control.DropDown ROIcentroidsonbrainplotsCheckBox matlab.ui.control.CheckBox ViewNetChordPlotsButton matlab.ui.control.Button - CohensDThresholdCheckBox matlab.ui.control.CheckBox ViewEdgeChordPlotsButton matlab.ui.control.Button EdgeChordPlotTypeDropDownLabel matlab.ui.control.Label + CohensDThresholdCheckBox matlab.ui.control.CheckBox EdgeChordPlotTypeDropDown matlab.ui.control.DropDown ViewConvergenceMapButton matlab.ui.control.Button ConvergencePlotColorDropDownLabel matlab.ui.control.Label @@ -322,7 +322,7 @@ function drawChords(app, event) chord_plotter = nla.net.result.chord.ChordPlotter(app.edge_test_options.net_atlas, app.edge_test_result); probability_parameters.edge_chord_plot_method = app.EdgeChordPlotTypeDropDown.Value; - chord_plotter.generateChordFigure(probability_parameters, plot_type) + chord_plotter.generateChordFigure(probability_parameters, plot_type, app.UIFigure) end % Value changed function: ColormapDropDown, @@ -602,13 +602,6 @@ function createComponents(app) app.ViewNetChordPlotsButton.Position = [263 82 140 22]; app.ViewNetChordPlotsButton.Text = 'View Net Chord Plots'; - % Create CohensDThresholdCheckBox - app.CohensDThresholdCheckBox = uicheckbox(app.Panel); - app.CohensDThresholdCheckBox.Enable = 'off'; - app.CohensDThresholdCheckBox.Visible = 'off'; - app.CohensDThresholdCheckBox.Text = 'Cohen''s D Threshold'; - app.CohensDThresholdCheckBox.Position = [402 4 134 22]; - % Create ViewEdgeChordPlotsButton app.ViewEdgeChordPlotsButton = uibutton(app.Panel, 'push'); app.ViewEdgeChordPlotsButton.ButtonPushedFcn = createCallbackFcn(app, @ViewEdgeChordPlotsButtonPushed, true); @@ -621,6 +614,13 @@ function createComponents(app) app.EdgeChordPlotTypeDropDownLabel.Position = [13 150 123 22]; app.EdgeChordPlotTypeDropDownLabel.Text = 'Edge Chord Plot Type'; + % Create CohensDThresholdCheckBox + app.CohensDThresholdCheckBox = uicheckbox(app.Panel); + app.CohensDThresholdCheckBox.Enable = 'off'; + app.CohensDThresholdCheckBox.Visible = 'off'; + app.CohensDThresholdCheckBox.Text = 'Cohen''s D Threshold'; + app.CohensDThresholdCheckBox.Position = [402 4 134 22]; + % Create EdgeChordPlotTypeDropDown app.EdgeChordPlotTypeDropDown = uidropdown(app.Panel); app.EdgeChordPlotTypeDropDown.Items = {}; diff --git a/+nla/+qualityControl/checkHeadMotion.m b/+nla/+qualityControl/checkHeadMotion.m index a09d25c1..c937609a 100755 --- a/+nla/+qualityControl/checkHeadMotion.m +++ b/+nla/+qualityControl/checkHeadMotion.m @@ -1,14 +1,24 @@ -function checkHeadMotion(fig, input_struct, motion) +function checkHeadMotion(fig, input_struct, motion, remove_index) + network_atlas = input_struct.net_atlas; + functional_connectivity = input_struct.func_conn; + if remove_index ~= 0 + [new_netatlas, functional_connectivity] = nla.removeNetworks(input_struct.net_atlas, input_struct.net_atlas.nets(remove_index).name, strcat(input_struct.net_atlas.name, '_-', input_struct.net_atlas.nets(remove_index).name), input_struct.func_conn); + network_atlas = nla.NetworkAtlas(new_netatlas); + if ~isa(functional_connectivity, 'nla.TriMatrix') + functional_connectivity = nla.TriMatrix(functional_connectivity); + end + end + prog = uiprogressdlg(fig, 'Title', 'Generating figures', 'Message', 'Generating head motion figures'); prog.Value = 0.02; - distances = nla.helpers.euclidianDistanceROIs(input_struct.net_atlas); + distances = nla.helpers.euclidianDistanceROIs(network_atlas); prog.Value = 0.75; - [r_vec, p_vec] = corr(motion, input_struct.func_conn.v', 'type', 'Pearson'); + [r_vec, p_vec] = corr(motion, functional_connectivity.v', 'type', 'Pearson'); - prob = nla.TriMatrix(input_struct.net_atlas.numROIs()); - r = nla.TriMatrix(input_struct.net_atlas.numROIs()); - h = nla.TriMatrix(input_struct.net_atlas.numROIs(), 'logical'); + prob = nla.TriMatrix(network_atlas.numROIs()); + r = nla.TriMatrix(network_atlas.numROIs()); + h = nla.TriMatrix(network_atlas.numROIs(), 'logical'); prob.v = p_vec'; r.v = r_vec'; h.v = nla.lib.fdr_bh(prob.v); @@ -25,7 +35,7 @@ function checkHeadMotion(fig, input_struct, motion) ulimit = 0.3; fig = nla.gfx.createFigure(1800, 900); - matrix_plot = nla.gfx.plots.MatrixPlot(fig, "FC-motion correlation (Pearson's r)", r, input_struct.net_atlas.nets,... + matrix_plot = nla.gfx.plots.MatrixPlot(fig, "FC-motion correlation (Pearson's r)", r, network_atlas.nets,... nla.gfx.FigSize.LARGE, 'lower_limit', llimit, 'upper_limit', ulimit); matrix_plot.displayImage(); width = matrix_plot.image_dimensions("image_width"); @@ -35,14 +45,14 @@ function checkHeadMotion(fig, input_struct, motion) ax = subplot('Position', [0.780, 0.540, 0.20, 0.40]); nla.gfx.setTitle(ax, sprintf("FC-motion correlation (Pearson's r) (q < 0.05)\n")); - nla.gfx.drawROIsOnCortex(ax, input_struct.net_atlas, ctx, mesh_alpha, ROI_radius, nla.gfx.ViewPos.DORSAL, false,... + nla.gfx.drawROIsOnCortex(ax, network_atlas, ctx, mesh_alpha, ROI_radius, nla.gfx.ViewPos.DORSAL, false,... nla.gfx.BrainColorMode.NONE); - for col = 1:input_struct.net_atlas.numROIs() - for row = (col + 1):input_struct.net_atlas.numROIs() + for col = 1:network_atlas.numROIs() + for row = (col + 1):network_atlas.numROIs() if h.get(row, col) - pos1 = input_struct.net_atlas.ROIs(row).pos; - pos2 = input_struct.net_atlas.ROIs(col).pos; + pos1 = network_atlas.ROIs(row).pos; + pos2 = network_atlas.ROIs(col).pos; edge_color = nla.gfx.valToColor(r.get(row, col), llimit, ulimit, color_map); diff --git a/+nla/+qualityControl/checkNormalityWithKS.m b/+nla/+qualityControl/checkNormalityWithKS.m index 19ec0634..3bb8eacb 100644 --- a/+nla/+qualityControl/checkNormalityWithKS.m +++ b/+nla/+qualityControl/checkNormalityWithKS.m @@ -1,4 +1,4 @@ -function checkNormalityWithKS(fig, input_struct, test_pool) +function checkNormalityWithKS(fig, input_struct, test_pool, remove_index) prog = uiprogressdlg(... fig, 'Title', 'Checking Normaility', 'Message', 'Running Kolmogorov-Smirnov Test'... @@ -9,25 +9,31 @@ function checkNormalityWithKS(fig, input_struct, test_pool) edge_test_result = test_pool.runEdgeTest(input_struct); prog.Value = 0.5; - ks_result = runKolmogorovSmirnovTest(input_struct, edge_test_result); + ks_result = runKolmogorovSmirnovTest(input_struct, edge_test_result, remove_index); prog.Value = 0.75; - qcKSOutput(ks_result.p, input_struct) + qcKSOutput(ks_result.p, input_struct, remove_index) end -function ks_result = runKolmogorovSmirnovTest(input_struct, edge_result) +function ks_result = runKolmogorovSmirnovTest(input_struct, edge_result, remove_index) import nla.TriMatrix nla.TriMatrixDiag + network_atlas = input_struct.net_atlas; + if remove_index ~= 0 + [new_netatlas] = nla.removeNetworks(input_struct.net_atlas, input_struct.net_atlas.nets(remove_index).name, strcat(input_struct.net_atlas.name, '_-', input_struct.net_atlas.nets(remove_index).name)); + network_atlas = nla.NetworkAtlas(new_netatlas); + end + ks_result = struct(); - number_of_networks = input_struct.net_atlas.numNets(); + number_of_networks = network_atlas.numNets(); ks_result.p = TriMatrix(number_of_networks, TriMatrixDiag.KEEP_DIAGONAL); ks_result.ks = TriMatrix(number_of_networks, TriMatrixDiag.KEEP_DIAGONAL); for network1 = 1:number_of_networks for network2 = 1:network1 - network_rho = edge_result.coeff.get(input_struct.net_atlas.nets(network1).indexes,... - input_struct.net_atlas.nets(network2).indexes); + network_rho = edge_result.coeff.get(network_atlas.nets(network1).indexes,... + network_atlas.nets(network2).indexes); [~, p, ks] = kstest(network_rho); ks_result.p.set(network1, network2, p); ks_result.ks.set(network1, network2, ks); @@ -35,7 +41,7 @@ function checkNormalityWithKS(fig, input_struct, test_pool) end end -function qcKSOutput(ks_result_p_value, edge_test_options) +function qcKSOutput(ks_result_p_value, edge_test_options, remove_index) % This will open the qc figure for the KS test network_test_options = nla.net.genBaseInputs(); @@ -45,15 +51,20 @@ function qcKSOutput(ks_result_p_value, edge_test_options) edge_test_options.prob_max = 0.05; default_discrete_colors = 1000; - [~, p_value_max] = network_test_options.fdr_correction.correct(edge_test_options.net_atlas,... - edge_test_options, ks_result_p_value); + network_atlas = edge_test_options.net_atlas; + if remove_index ~= 0 + [new_netatlas] = nla.removeNetworks(edge_test_options.net_atlas, edge_test_options.net_atlas.nets(remove_index).name, strcat(edge_test_options.net_atlas.name, '-', edge_test_options.net_atlas.nets(remove_index).name)); + network_atlas = nla.NetworkAtlas(new_netatlas); + end + + [~, p_value_max] = network_test_options.fdr_correction.correct(network_atlas, edge_test_options, ks_result_p_value); color_map = nla.net.result.NetworkResultPlotParameter.getColormap(default_discrete_colors,... p_value_max); fig = nla.gfx.createFigure(); % Also remember to move this in read the docs - matrix_plot = nla.gfx.plots.MatrixPlot(fig, sprintf("Non-permuted Kolmogorov-Smirnov Test p-value\nSmaller values are less normal"), ks_result_p_value, edge_test_options.net_atlas.nets, nla.gfx.FigSize.LARGE,... + matrix_plot = nla.gfx.plots.MatrixPlot(fig, sprintf("Non-permuted Kolmogorov-Smirnov Test p-value\nSmaller values are less normal"), ks_result_p_value, network_atlas.nets, nla.gfx.FigSize.LARGE,... 'lower_limit', 0.00, 'upper_limit', p_value_max, 'color_map', color_map); matrix_plot.displayImage(); width = matrix_plot.image_dimensions('image_width'); diff --git a/+nla/NetworkAtlas.m b/+nla/NetworkAtlas.m index d47740de..b1023c31 100755 --- a/+nla/NetworkAtlas.m +++ b/+nla/NetworkAtlas.m @@ -12,10 +12,13 @@ % :param space: (Optional) The mesh that the atlas` ROI locations/parcels are in. Two options - ``Talairach (TT)`` or ``Montreal Neurological Institute (MNI)`` properties (SetAccess = private) - nets % This is the net_names - + nets + net_names ROIs + ROI_key + ROI_pos ROI_order + net_colors name space anat = false; @@ -37,7 +40,8 @@ end net_names = net_struct.net_names; - + obj.net_names = net_names; + net_count = numel(net_names); ROI_count = size(net_struct.ROI_key, 1); @@ -45,6 +49,7 @@ if isfield(net_struct, 'net_colors') net_colors = net_struct.net_colors; end + obj.net_colors = net_colors; ROI_positions = zeros(ROI_count, 3); if isfield(net_struct, 'ROI_pos') @@ -60,6 +65,9 @@ net_struct.ROI_order = net_struct.ROI_order(sort_idx); ROI_positions = ROI_positions(sort_idx, :); end + obj.ROI_pos = ROI_positions; + obj.ROI_key = net_struct.ROI_key; + obj.ROI_order = net_struct.ROI_order; %% Network atlas name obj.name = net_struct.name; diff --git a/+nla/removeNetworks.m b/+nla/removeNetworks.m index e8de1295..f16c1c3d 100755 --- a/+nla/removeNetworks.m +++ b/+nla/removeNetworks.m @@ -78,7 +78,8 @@ %% Functional connectivity (optional) if exist('fc_in', 'var') - fc_ordered = fc_in(atlas_in.ROI_order, atlas_in.ROI_order, :); + fc_in_matrix = fc_in.asMatrix(); + fc_ordered = fc_in_matrix(atlas_in.ROI_order, atlas_in.ROI_order, :); fc_reduced = fc_ordered(ROI_mask, ROI_mask, :); ROI_order_inverse(atlas_out.ROI_order) = [1:numel(atlas_out.ROI_order)]'; fc_out = fc_reduced(ROI_order_inverse, ROI_order_inverse, :); diff --git a/NLAQualityControl.mlapp b/NLAQualityControl.mlapp index 97ded08b..cfb56c2c 100644 Binary files a/NLAQualityControl.mlapp and b/NLAQualityControl.mlapp differ