From dd18f3bda671696a07a54c56500fbf959ecb82d5 Mon Sep 17 00:00:00 2001 From: Patrick Fletcher Date: Sat, 17 Nov 2018 12:33:25 -0500 Subject: [PATCH 1/5] fix getters for ShowData, ShowNotches ShowData was checking Notch visibility, and ShowNotches was checking ScatterPlot visibility. Also, the "logical" cast is unnecessary --- Violin.m | 6 +++--- violinplot.m | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Violin.m b/Violin.m index 1ba59fd..ad7eeb6 100644 --- a/Violin.m +++ b/Violin.m @@ -255,7 +255,7 @@ end function yesno = get.ShowData(obj) - yesno = logical(strcmp(obj.NotchPlots(1).Visible, 'on')); + yesno = strcmp(obj.ScatterPlot.Visible, 'on'); end function set.ShowNotches(obj, yesno) @@ -269,7 +269,7 @@ end function yesno = get.ShowNotches(obj) - yesno = logical(strcmp(obj.ScatterPlot.Visible, 'on')); + yesno = strcmp(obj.NotchPlots(1).Visible, 'on'); end function set.ShowMean(obj, yesno) @@ -281,7 +281,7 @@ end function yesno = get.ShowMean(obj) - yesno = logical(strcmp(obj.MeanPlot.Visible, 'on')); + yesno = strcmp(obj.MeanPlot.Visible, 'on'); end end diff --git a/violinplot.m b/violinplot.m index 131ae6b..6b4fba3 100644 --- a/violinplot.m +++ b/violinplot.m @@ -48,7 +48,7 @@ % Copyright (c) 2016, Bastian Bechtold % This code is released under the terms of the BSD 3-clause license - hascategories = exist('cats') && not(isempty(cats)); + hascategories = exist('cats','var') && not(isempty(cats)); % tabular data if isa(data, 'dataset') || isstruct(data) || istable(data) From 09ba0ba7f31823b663e961fd0ed85123f90121fb Mon Sep 17 00:00:00 2001 From: Patrick Fletcher Date: Sat, 17 Nov 2018 14:08:29 -0500 Subject: [PATCH 2/5] mean of data, move mean plot on top of box plot --- Violin.m | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/Violin.m b/Violin.m index ad7eeb6..f2ec1aa 100644 --- a/Violin.m +++ b/Violin.m @@ -141,28 +141,32 @@ obj.ScatterPlot = ... scatter(pos + jitter.*jitterstrength, data, 'filled'); - % plot the data mean - meanValue = mean(value); - if length(density) > 1 - meanDensity = interp1(value, density, meanValue); - else % all data is identical: - meanDensity = density; - end - obj.MeanPlot = plot([pos-meanDensity*width pos+meanDensity*width], ... - [meanValue meanValue]); - obj.MeanPlot.LineWidth = 0.75; - % plot the violin obj.ViolinPlot = ... % plot color will be overwritten later fill([pos+density*width pos-density(end:-1:1)*width], ... [value value(end:-1:1)], [1 1 1]); % plot the mini-boxplot within the violin - quartiles = quantile(data, [0.25, 0.5, 0.75]); + quartiles = quantile(data, [0.25, 0.5, 0.75]); obj.BoxPlot = ... % plot color will be overwritten later fill([pos-0.01 pos+0.01 pos+0.01 pos-0.01], ... [quartiles(1) quartiles(1) quartiles(3) quartiles(3)], ... [1 1 1]); + + % plot the data mean + meanValue = mean(data); + if length(density) > 1 + meanDensityWidth = interp1(value, density, meanValue)*width; + else % all data is identical: + meanDensityWidth = density*width; + end + if meanDensityWidth lowhisker))); From bd5059a5546b879b2191c261c003868daca87c55 Mon Sep 17 00:00:00 2001 From: Patrick Fletcher Date: Sat, 17 Nov 2018 14:09:19 -0500 Subject: [PATCH 3/5] Add BoxWidth control for now in units of x axis; could also be done as fraction of violin width. --- Violin.m | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Violin.m b/Violin.m index f2ec1aa..6656771 100644 --- a/Violin.m +++ b/Violin.m @@ -63,6 +63,7 @@ ViolinAlpha % transparency of the violin area and data points EdgeColor % color of the violin area outline BoxColor % color of box, whiskers, and median/notch edges + BoxWidth % width of box between the quartiles in axis space (default 10% of Violin plot width, 0.03) MedianColor % fill color of median and notches ShowData % whether to show data points ShowNotches % whether to show notch indicators @@ -149,7 +150,7 @@ % plot the mini-boxplot within the violin quartiles = quantile(data, [0.25, 0.5, 0.75]); obj.BoxPlot = ... % plot color will be overwritten later - fill([pos-0.01 pos+0.01 pos+0.01 pos-0.01], ... + fill(pos+[-1,1,1,-1]*args.BoxWidth/2, ... [quartiles(1) quartiles(1) quartiles(3) quartiles(3)], ... [1 1 1]); @@ -186,6 +187,7 @@ obj.EdgeColor = args.EdgeColor; obj.BoxColor = args.BoxColor; + obj.BoxWidth = args.BoxWidth; obj.MedianColor = args.MedianColor; if not(isempty(args.ViolinColor)) obj.ViolinColor = args.ViolinColor; @@ -230,6 +232,15 @@ function color = get.BoxColor(obj) color = obj.BoxPlot.FaceColor; end + + function set.BoxWidth(obj,width) + pos=mean(obj.BoxPlot.XData); + obj.BoxPlot.XData=pos+[-1,1,1,-1]*width/2; + end + + function width = get.BoxWidth(obj) + width=max(obj.BoxPlot.XData)-min(obj.BoxPlot.XData); + end function set.ViolinColor(obj, color) obj.ViolinPlot.FaceColor = color; @@ -300,6 +311,7 @@ iscolor = @(x) (isnumeric(x) & length(x) == 3); p.addParameter('ViolinColor', [], iscolor); p.addParameter('BoxColor', [0.5 0.5 0.5], iscolor); + p.addParameter('BoxWidth', 0.03, isscalarnumber); p.addParameter('EdgeColor', [0.5 0.5 0.5], iscolor); p.addParameter('MedianColor', [1 1 1], iscolor); p.addParameter('ViolinAlpha', 0.3, isscalarnumber); From 95c2ad807ba93b5d65962c748d185a388fc93a34 Mon Sep 17 00:00:00 2001 From: Patrick Fletcher Date: Sat, 17 Nov 2018 17:50:41 -0500 Subject: [PATCH 4/5] add appropriate isempty check for all getters setters If there's only one datapoint, all plot objects except median marker are not generated so their associated get/set methods give errors --- Violin.m | 70 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/Violin.m b/Violin.m index 6656771..20a28d4 100644 --- a/Violin.m +++ b/Violin.m @@ -201,16 +201,20 @@ end function set.EdgeColor(obj, color) - obj.ViolinPlot.EdgeColor = color; + if ~isempty(obj.ViolinPlot) + obj.ViolinPlot.EdgeColor = color; + end end function color = get.EdgeColor(obj) - color = obj.ViolinPlot.EdgeColor; + if ~isempty(obj.ViolinPlot) + color = obj.ViolinPlot.EdgeColor; + end end function set.MedianColor(obj, color) obj.MedianPlot.MarkerFaceColor = color; - if not(isempty(obj.NotchPlots)) + if ~isempty(obj.NotchPlots) obj.NotchPlots(1).MarkerFaceColor = color; obj.NotchPlots(2).MarkerFaceColor = color; end @@ -221,21 +225,27 @@ end function set.BoxColor(obj, color) - obj.BoxPlot.FaceColor = color; - obj.BoxPlot.EdgeColor = color; - obj.WhiskerPlot.Color = color; - obj.MedianPlot.MarkerEdgeColor = color; - obj.NotchPlots(1).MarkerEdgeColor = color; - obj.NotchPlots(2).MarkerEdgeColor = color; + if ~isempty(obj.BoxPlot) + obj.BoxPlot.FaceColor = color; + obj.BoxPlot.EdgeColor = color; + obj.WhiskerPlot.Color = color; + obj.MedianPlot.MarkerEdgeColor = color; + obj.NotchPlots(1).MarkerFaceColor = color; + obj.NotchPlots(2).MarkerFaceColor = color; + end end function color = get.BoxColor(obj) - color = obj.BoxPlot.FaceColor; + if ~isempty(obj.BoxPlot) + color = obj.BoxPlot.FaceColor; + end end function set.BoxWidth(obj,width) - pos=mean(obj.BoxPlot.XData); - obj.BoxPlot.XData=pos+[-1,1,1,-1]*width/2; + if ~isempty(obj.BoxPlot) + pos=mean(obj.BoxPlot.XData); + obj.BoxPlot.XData=pos+[-1,1,1,-1]*width/2; + end end function width = get.BoxWidth(obj) @@ -270,33 +280,43 @@ end function yesno = get.ShowData(obj) - yesno = strcmp(obj.ScatterPlot.Visible, 'on'); + if ~isempty(obj.ScatterPlot) + yesno = strcmp(obj.ScatterPlot.Visible, 'on'); + end end function set.ShowNotches(obj, yesno) - if yesno - obj.NotchPlots(1).Visible = 'on'; - obj.NotchPlots(2).Visible = 'on'; - else - obj.NotchPlots(1).Visible = 'off'; - obj.NotchPlots(2).Visible = 'off'; + if ~isempty(obj.NotchPlots) + if yesno + obj.NotchPlots(1).Visible = 'on'; + obj.NotchPlots(2).Visible = 'on'; + else + obj.NotchPlots(1).Visible = 'off'; + obj.NotchPlots(2).Visible = 'off'; + end end end function yesno = get.ShowNotches(obj) - yesno = strcmp(obj.NotchPlots(1).Visible, 'on'); + if ~isempty(obj.NotchPlots) + yesno = strcmp(obj.NotchPlots(1).Visible, 'on'); + end end function set.ShowMean(obj, yesno) - if yesno - obj.MeanPlot.Visible = 'on'; - else - obj.MeanPlot.Visible = 'off'; + if ~isempty(obj.MeanPlot) + if yesno + obj.MeanPlot.Visible = 'on'; + else + obj.MeanPlot.Visible = 'off'; + end end end function yesno = get.ShowMean(obj) - yesno = strcmp(obj.MeanPlot.Visible, 'on'); + if ~isempty(obj.MeanPlot) + yesno = strcmp(obj.MeanPlot.Visible, 'on'); + end end end From 3416e5efbdd677361da9e40f07aae0d0c4d7815f Mon Sep 17 00:00:00 2001 From: Patrick Fletcher Date: Sun, 18 Nov 2018 12:53:10 -0500 Subject: [PATCH 5/5] fix box width default to match original also move the default Violin width (0.3) into the input parser (removes need to check if args.Width is empty) --- Violin.m | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/Violin.m b/Violin.m index 20a28d4..5940f7d 100644 --- a/Violin.m +++ b/Violin.m @@ -126,11 +126,7 @@ density = 1; end - if isempty(args.Width) - width = 0.3/max(density); - else - width = args.Width/max(density); - end + width = args.Width/max(density); % plot the data points within the violin area if length(density) > 1 @@ -150,7 +146,7 @@ % plot the mini-boxplot within the violin quartiles = quantile(data, [0.25, 0.5, 0.75]); obj.BoxPlot = ... % plot color will be overwritten later - fill(pos+[-1,1,1,-1]*args.BoxWidth/2, ... + fill(pos+[-1,1,1,-1]*args.BoxWidth, ... [quartiles(1) quartiles(1) quartiles(3) quartiles(3)], ... [1 1 1]); @@ -244,7 +240,7 @@ function set.BoxWidth(obj,width) if ~isempty(obj.BoxPlot) pos=mean(obj.BoxPlot.XData); - obj.BoxPlot.XData=pos+[-1,1,1,-1]*width/2; + obj.BoxPlot.XData=pos+[-1,1,1,-1]*width; end end @@ -326,12 +322,12 @@ p = inputParser(); p.addRequired('Data', @isnumeric); p.addRequired('Pos', isscalarnumber); - p.addParameter('Width', [], isscalarnumber); + p.addParameter('Width', 0.3, isscalarnumber); p.addParameter('Bandwidth', [], isscalarnumber); iscolor = @(x) (isnumeric(x) & length(x) == 3); p.addParameter('ViolinColor', [], iscolor); p.addParameter('BoxColor', [0.5 0.5 0.5], iscolor); - p.addParameter('BoxWidth', 0.03, isscalarnumber); + p.addParameter('BoxWidth', 0.01, isscalarnumber); p.addParameter('EdgeColor', [0.5 0.5 0.5], iscolor); p.addParameter('MedianColor', [1 1 1], iscolor); p.addParameter('ViolinAlpha', 0.3, isscalarnumber);