diff --git a/Violin.m b/Violin.m
index 93c0595..19f646a 100644
--- a/Violin.m
+++ b/Violin.m
@@ -1,569 +1,695 @@
-classdef Violin < handle
- % Violin creates violin plots for some data
- % A violin plot is an easy to read substitute for a box plot
- % that replaces the box shape with a kernel density estimate of
- % the data, and optionally overlays the data points itself.
- % It is also possible to provide two sets of data which are supposed
- % to be compared by plotting each column of the two datasets together
- % on each side of the violin.
- %
- % Additional constructor parameters include the width of the
- % plot, the bandwidth of the kernel density estimation, the
- % X-axis position of the violin plot, and the categories.
- %
- % Use violinplot for a
- % boxplot-like wrapper for
- % interactive plotting.
- %
- % See for more information on Violin Plots:
- % J. L. Hintze and R. D. Nelson, "Violin plots: a box
- % plot-density trace synergism," The American Statistician, vol.
- % 52, no. 2, pp. 181-184, 1998.
- %
- % Violin Properties:
- % ViolinColor - Fill color of the violin area and data points.
- % Can be either a matrix nx3 or an array of up to two
- % cells containing nx3 matrices.
- % Defaults to the next default color cycle.
- % ViolinAlpha - Transparency of the violin area and data points.
- % Can be either a single scalar value or an array of
- % up to two cells containing scalar values.
- % Defaults to 0.3.
- % EdgeColor - Color of the violin area outline.
- % Defaults to [0.5 0.5 0.5]
- % BoxColor - Color of the box, whiskers, and the outlines of
- % the median point and the notch indicators.
- % Defaults to [0.5 0.5 0.5]
- % MedianColor - Fill color of the median and notch indicators.
- % Defaults to [1 1 1]
- % ShowData - Whether to show data points.
- % Defaults to true
- % ShowNotches - Whether to show notch indicators.
- % Defaults to false
- % ShowMean - Whether to show mean indicator.
- % Defaults to false
- % ShowBox - Whether to show the box.
- % Defaults to true
- % ShowMedian - Whether to show the median indicator.
- % Defaults to true
- % ShowWhiskers - Whether to show the whiskers
- % Defaults to true
- %
- % Violin Children:
- % ScatterPlot - scatter plot of the data points
- % ScatterPlot2 - scatter second plot of the data points
- % ViolinPlot - fill plot of the kernel density estimate
- % ViolinPlot2 - fill second plot of the kernel density estimate
- % BoxPlot - fill plot of the box between the quartiles
- % WhiskerPlot - line plot between the whisker ends
- % MedianPlot - scatter plot of the median (one point)
- % NotchPlots - scatter plots for the notch indicators
- % MeanPlot - line plot at mean value
-
- % Copyright (c) 2016, Bastian Bechtold
- % This code is released under the terms of the BSD 3-clause license
-
- properties (Access=public)
- ScatterPlot % scatter plot of the data points
- ScatterPlot2 % comparison scatter plot of the data points
- ViolinPlot % fill plot of the kernel density estimate
- ViolinPlot2 % comparison fill plot of the kernel density estimate
- BoxPlot % fill plot of the box between the quartiles
- WhiskerPlot % line plot between the whisker ends
- MedianPlot % scatter plot of the median (one point)
- NotchPlots % scatter plots for the notch indicators
- MeanPlot % line plot of the mean (horizontal line)
- end
-
- properties (Dependent=true)
- ViolinColor % fill color of the violin area and data points
- ViolinAlpha % transparency of the violin area and data points
- MarkerSize % marker size for the median dot
- LineWidth % linewidth of the median plot
- 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
- ShowMean % whether to show mean indicator
- ShowBox % whether to show the box
- ShowMedian % whether to show the median line
- ShowWhiskers % whether to show the whiskers
- end
-
- methods
- function obj = Violin(data, pos, varargin)
- %Violin plots a violin plot of some data at pos
- % VIOLIN(DATA, POS) plots a violin at x-position POS for
- % a vector of DATA points.
- %
- % VIOLIN(..., 'PARAM1', val1, 'PARAM2', val2, ...)
- % specifies optional name/value pairs:
- % 'Width' Width of the violin in axis space.
- % Defaults to 0.3
- % 'Bandwidth' Bandwidth of the kernel density
- % estimate. Should be between 10% and
- % 40% of the data range.
- % 'ViolinColor' Fill color of the violin area
- % and data points.Can be either a matrix
- % nx3 or an array of up to two cells
- % containing nx3 matrices.
- % 'ViolinAlpha' Transparency of the violin area and data
- % points. Can be either a single scalar
- % value or an array of up to two cells
- % containing scalar values. Defaults to 0.3.
- % 'EdgeColor' Color of the violin area outline.
- % Defaults to [0.5 0.5 0.5]
- % 'BoxColor' Color of the box, whiskers, and the
- % outlines of the median point and the
- % notch indicators. Defaults to
- % [0.5 0.5 0.5]
- % 'MedianColor' Fill color of the median and notch
- % indicators. Defaults to [1 1 1]
- % 'ShowData' Whether to show data points.
- % Defaults to true
- % 'ShowNotches' Whether to show notch indicators.
- % Defaults to false
- % 'ShowMean' Whether to show mean indicator.
- % Defaults to false
- % 'ShowBox' Whether to show the box
- % Defaults to true
- % 'ShowMedian' Whether to show the median line
- % Defaults to true
- % 'ShowWhiskers' Whether to show the whiskers
- % Defaults to true
-
- args = obj.checkInputs(data, pos, varargin{:});
-
- if length(data)==1
- data2 = [];
- data = data{1};
-
- else
- data2 = data{2};
- data = data{1};
- end
-
- if isempty(args.ViolinColor)
- C = colororder;
- args.ViolinColor = {repmat(C,ceil(size(data,2)/length(C)),1)};
- end
-
- data = data(not(isnan(data)));
- data2 = data2(not(isnan(data2)));
- if numel(data) == 1
- obj.MedianPlot = scatter(pos, data, 'filled');
- obj.MedianColor = args.MedianColor;
- obj.MedianPlot.MarkerEdgeColor = args.EdgeColor;
- return
- end
-
- hold('on');
-
-
- %% calculate kernel density estimation for the violin
- [density, value, width] = obj.calcKernelDensity(data, args.Bandwidth, args.Width);
-
- % also calculate the kernel density of the comparison data if
- % provided
- if ~isempty(data2)
- [densityC, valueC, widthC] = obj.calcKernelDensity(data2, args.Bandwidth, args.Width);
- end
-
- %% plot the data points within the violin area
- if length(density) > 1
- jitterstrength = interp1(value, density*width, data);
- else % all data is identical:
- jitterstrength = density*width;
- end
- if isempty(data2) % if no comparison data
- jitter = 2*(rand(size(data))-0.5); % both sides
- else
- jitter = rand(size(data)); % only right side
- end
- obj.ScatterPlot = ...
- scatter(pos + jitter.*jitterstrength, data, 'filled');
-
- if ~isempty(data2)
- % plot the data points within the violin area
- if length(densityC) > 1
- jitterstrength = interp1(valueC, densityC*widthC, data2);
- else % all data is identical:
- jitterstrength = densityC*widthC;
- end
- jitter = rand(size(data2))-1;
- obj.ScatterPlot2 = ...
- scatter(pos + jitter.*jitterstrength, data2, 'filled');
- end
-
- %% plot the violins
- if isempty(data2)
- % plot the violin on boths sides
- 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]);
- else
- % plot right half of the violin
- obj.ViolinPlot = ...
- fill([pos+density*width pos-density(1)*width], ...
- [value value(1)], [1 1 1]);
- % plot left half of the violin
- obj.ViolinPlot2 = ...
- fill([pos-densityC(end)*widthC pos-densityC(end:-1:1)*widthC], ...
- [valueC(end) valueC(end:-1:1)], [1 1 1]);
- end
-
- %% 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, ...
- [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)));
- hiwhisker = quartiles(3) + 1.5*IQR;
- hiwhisker = min(hiwhisker, max(data(data < hiwhisker)));
- if ~isempty(lowhisker) && ~isempty(hiwhisker)
- obj.WhiskerPlot = plot([pos pos], [lowhisker hiwhisker]);
- end
-
- obj.MedianPlot = scatter(pos, quartiles(2), args.MarkerSize, [1 1 1], 'filled');
-
- obj.NotchPlots = ...
- scatter(pos, quartiles(2)-1.57*IQR/sqrt(length(data)), ...
- [], [1 1 1], 'filled', '^');
- obj.NotchPlots(2) = ...
- scatter(pos, quartiles(2)+1.57*IQR/sqrt(length(data)), ...
- [], [1 1 1], 'filled', 'v');
-
- %% set graphical preferences
- obj.EdgeColor = args.EdgeColor;
- obj.MedianPlot.LineWidth = args.LineWidth;
- obj.BoxColor = args.BoxColor;
- obj.BoxWidth = args.BoxWidth;
- obj.MedianColor = args.MedianColor;
- obj.ShowData = args.ShowData;
- obj.ShowNotches = args.ShowNotches;
- obj.ShowMean = args.ShowMean;
- obj.ShowBox = args.ShowBox;
- obj.ShowMedian = args.ShowMedian;
- obj.ShowWhiskers = args.ShowWhiskers;
-
- if not(isempty(args.ViolinColor))
- if size(args.ViolinColor{1},1) > 1
- ViolinColor{1} = args.ViolinColor{1}(pos,:);
- else
- ViolinColor{1} = args.ViolinColor{1};
- end
- if length(args.ViolinColor)==2
- if size(args.ViolinColor{2},1) > 1
- ViolinColor{2} = args.ViolinColor{2}(pos,:);
- else
- ViolinColor{2} = args.ViolinColor{2};
- end
- else
- ViolinColor{2} = ViolinColor{1};
- end
- else
- % defaults
- ViolinColor{1} = obj.ScatterPlot.CData;
- ViolinColor{2} = [0 0 0];
- end
- obj.ViolinColor = ViolinColor;
-
-
- if not(isempty(args.ViolinAlpha))
- if length(args.ViolinAlpha{1})>1
- error('Only scalar values are accepted for the alpha color channel');
- else
- ViolinAlpha{1} = args.ViolinAlpha{1};
- end
- if length(args.ViolinAlpha)==2
- if length(args.ViolinAlpha{2})>1
- error('Only scalar values are accepted for the alpha color channel');
- else
- ViolinAlpha{2} = args.ViolinAlpha{2};
- end
- else
- ViolinAlpha{2} = ViolinAlpha{1}/2; % default unless specified
- end
- else
- % default
- ViolinAlpha = {1,1};
- end
- obj.ViolinAlpha = ViolinAlpha;
-
-
- end
-
- %% SET METHODS
- function set.EdgeColor(obj, color)
- if ~isempty(obj.ViolinPlot)
- obj.ViolinPlot.EdgeColor = color;
- if ~isempty(obj.ViolinPlot2)
- obj.ViolinPlot2.EdgeColor = color;
- end
- end
- end
-
- function color = get.EdgeColor(obj)
- if ~isempty(obj.ViolinPlot)
- color = obj.ViolinPlot.EdgeColor;
- end
- end
-
-
- function set.MedianColor(obj, color)
- obj.MedianPlot.MarkerFaceColor = color;
- if ~isempty(obj.NotchPlots)
- obj.NotchPlots(1).MarkerFaceColor = color;
- obj.NotchPlots(2).MarkerFaceColor = color;
- end
- end
-
- function color = get.MedianColor(obj)
- color = obj.MedianPlot.MarkerFaceColor;
- end
-
-
- function set.BoxColor(obj, 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)
- if ~isempty(obj.BoxPlot)
- color = obj.BoxPlot.FaceColor;
- end
- end
-
-
- function set.BoxWidth(obj,width)
- if ~isempty(obj.BoxPlot)
- pos=mean(obj.BoxPlot.XData);
- obj.BoxPlot.XData=pos+[-1,1,1,-1]*width;
- end
- 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{1};
- obj.ScatterPlot.MarkerFaceColor = color{1};
- obj.MeanPlot.Color = color{1};
- if ~isempty(obj.ViolinPlot2)
- obj.ViolinPlot2.FaceColor = color{2};
- obj.ScatterPlot2.MarkerFaceColor = color{2};
- end
- end
-
- function color = get.ViolinColor(obj)
- color{1} = obj.ViolinPlot.FaceColor;
- if ~isempty(obj.ViolinPlot2)
- color{2} = obj.ViolinPlot2.FaceColor;
- end
- end
-
-
- function set.ViolinAlpha(obj, alpha)
- obj.ViolinPlot.FaceAlpha = alpha{1};
- obj.ScatterPlot.MarkerFaceAlpha = alpha{1};
- if ~isempty(obj.ViolinPlot2)
- obj.ViolinPlot2.FaceAlpha = alpha{2};
- obj.ScatterPlot2.MarkerFaceAlpha = alpha{2};
- end
- end
-
- function alpha = get.ViolinAlpha(obj)
- alpha{1} = obj.ViolinPlot.FaceAlpha;
- if ~isempty(obj.ViolinPlot2)
- alpha{2} = obj.ViolinPlot2.FaceAlpha;
- end
- end
-
-
- function set.ShowData(obj, yesno)
- if yesno
- obj.ScatterPlot.Visible = 'on';
- else
- obj.ScatterPlot.Visible = 'off';
- end
- if ~isempty(obj.ScatterPlot2)
- obj.ScatterPlot2.Visible = obj.ScatterPlot.Visible;
- end
-
- end
-
- function yesno = get.ShowData(obj)
- if ~isempty(obj.ScatterPlot)
- yesno = strcmp(obj.ScatterPlot.Visible, 'on');
- end
- end
-
-
- function set.ShowNotches(obj, yesno)
- 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)
- if ~isempty(obj.NotchPlots)
- yesno = strcmp(obj.NotchPlots(1).Visible, 'on');
- end
- end
-
-
- function set.ShowMean(obj, yesno)
- if ~isempty(obj.MeanPlot)
- if yesno
- obj.MeanPlot.Visible = 'on';
- else
- obj.MeanPlot.Visible = 'off';
- end
- end
- end
-
- function yesno = get.ShowMean(obj)
- if ~isempty(obj.BoxPlot)
- yesno = strcmp(obj.BoxPlot.Visible, 'on');
- end
- end
-
-
- function set.ShowBox(obj, yesno)
- if ~isempty(obj.BoxPlot)
- if yesno
- obj.BoxPlot.Visible = 'on';
- else
- obj.BoxPlot.Visible = 'off';
- end
- end
- end
-
- function yesno = get.ShowBox(obj)
- if ~isempty(obj.BoxPlot)
- yesno = strcmp(obj.BoxPlot.Visible, 'on');
- end
- end
-
-
- function set.ShowMedian(obj, yesno)
- if ~isempty(obj.MedianPlot)
- if yesno
- obj.MedianPlot.Visible = 'on';
- else
- obj.MedianPlot.Visible = 'off';
- end
- end
- end
-
- function yesno = get.ShowMedian(obj)
- if ~isempty(obj.MedianPlot)
- yesno = strcmp(obj.MedianPlot.Visible, 'on');
- end
- end
-
-
- function set.ShowWhiskers(obj, yesno)
- if ~isempty(obj.WhiskerPlot)
- if yesno
- obj.WhiskerPlot.Visible = 'on';
- else
- obj.WhiskerPlot.Visible = 'off';
- end
- end
- end
-
- function yesno = get.ShowWhiskers(obj)
- if ~isempty(obj.WhiskerPlot)
- yesno = strcmp(obj.WhiskerPlot.Visible, 'on');
- end
- end
-
- end
-
- methods (Access=private)
- function results = checkInputs(~, data, pos, varargin)
- isscalarnumber = @(x) (isnumeric(x) & isscalar(x));
- p = inputParser();
- p.addRequired('Data', @(x)isnumeric(vertcat(x{:})));
- p.addRequired('Pos', isscalarnumber);
- p.addParameter('Width', 0.3, isscalarnumber);
- p.addParameter('Bandwidth', [], isscalarnumber);
- iscolor = @(x) (isnumeric(x) & size(x,2) == 3);
- p.addParameter('ViolinColor', [], @(x)iscolor(vertcat(x{:})));
- p.addParameter('MarkerSize', 36, @isnumeric);
- p.addParameter('LineWidth', 0.75, @isnumeric);
- p.addParameter('BoxColor', [0.5 0.5 0.5], iscolor);
- 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,0.15}, @(x)isnumeric(vertcat(x{:})));
- isscalarlogical = @(x) (islogical(x) & isscalar(x));
- p.addParameter('ShowData', true, isscalarlogical);
- p.addParameter('ShowNotches', false, isscalarlogical);
- p.addParameter('ShowMean', false, isscalarlogical);
- p.addParameter('ShowBox', true, isscalarlogical);
- p.addParameter('ShowMedian', true, isscalarlogical);
- p.addParameter('ShowWhiskers', true, isscalarlogical);
- p.parse(data, pos, varargin{:});
- results = p.Results;
- end
- end
-
- methods (Static)
- function [density, value, width] = calcKernelDensity(data, bandwidth, width)
- if isempty(data)
- error('Empty input data');
- end
- [density, value] = ksdensity(data, 'bandwidth', bandwidth);
- density = density(value >= min(data) & value <= max(data));
- value = value(value >= min(data) & value <= max(data));
- value(1) = min(data);
- value(end) = max(data);
- value = [value(1)*(1-1E-5), value, value(end)*(1+1E-5)];
- density = [0, density, 0];
-
- % all data is identical
- if min(data) == max(data)
- density = 1;
- end
-
- width = width/max(density);
- end
- end
-end
+classdef Violin < handle
+ % Violin creates violin plots for some data
+ % A violin plot is an easy to read substitute for a box plot
+ % that replaces the box shape with a kernel density estimate of
+ % the data, and optionally overlays the data points itself.
+ % It is also possible to provide two sets of data which are supposed
+ % to be compared by plotting each column of the two datasets together
+ % on each side of the violin.
+ %
+ % Additional constructor parameters include the width of the
+ % plot, the bandwidth of the kernel density estimation, the
+ % X-axis position of the violin plot, and the categories.
+ %
+ % Use violinplot for a
+ % boxplot-like wrapper for
+ % interactive plotting.
+ %
+ % See for more information on Violin Plots:
+ % J. L. Hintze and R. D. Nelson, "Violin plots: a box
+ % plot-density trace synergism," The American Statistician, vol.
+ % 52, no. 2, pp. 181-184, 1998.
+ %
+ % Violin Properties:
+ % ViolinColor - Fill color of the violin area and data points.
+ % Can be either a matrix nx3 or an array of up to two
+ % cells containing nx3 matrices.
+ % Defaults to the next default color cycle.
+ % ViolinAlpha - Transparency of the violin area and data points.
+ % Can be either a single scalar value or an array of
+ % up to two cells containing scalar values.
+ % Defaults to 0.3.
+ % EdgeColor - Color of the violin area outline.
+ % Defaults to [0.5 0.5 0.5]
+ % BoxColor - Color of the box, whiskers, and the outlines of
+ % the median point and the notch indicators.
+ % Defaults to [0.5 0.5 0.5]
+ % MedianColor - Fill color of the median and notch indicators.
+ % Defaults to [1 1 1]
+ % ShowData - Whether to show data points.
+ % Defaults to true
+ % ShowNotches - Whether to show notch indicators.
+ % Defaults to false
+ % ShowMean - Whether to show mean indicator.
+ % Defaults to false
+ % ShowBox - Whether to show the box.
+ % Defaults to true
+ % ShowMedian - Whether to show the median indicator.
+ % Defaults to true
+ % ShowWhiskers - Whether to show the whiskers
+ % Defaults to true
+ % HalfViolin - Whether to do a half violin(left, right side) or
+ % full. Defaults to full.
+ % QuartileStyle - Option on how to display quartiles, with a
+ % boxplot, shadow or none. Defaults to boxplot.
+ % DataStyle - Defines the style to show the data points. Opts:
+ % 'scatter', 'histogram' or 'none'. Default is 'Scatter'.
+ %
+ %
+ % Violin Children:
+ % ScatterPlot - scatter plot of the data points
+ % ScatterPlot2 - scatter second plot of the data points
+ % ViolinPlot - fill plot of the kernel density estimate
+ % ViolinPlot2 - fill second plot of the kernel density estimate
+ % BoxPlot - fill plot of the box between the quartiles
+ % WhiskerPlot - line plot between the whisker ends
+ % MedianPlot - scatter plot of the median (one point)
+ % NotchPlots - scatter plots for the notch indicators
+ % MeanPlot - line plot at mean value
+
+
+ % Copyright (c) 2016, Bastian Bechtold
+ % This code is released under the terms of the BSD 3-clause license
+
+ properties (Access=public)
+ ScatterPlot % scatter plot of the data points
+ ScatterPlot2 % comparison scatter plot of the data points
+ ViolinPlot % fill plot of the kernel density estimate
+ ViolinPlot2 % comparison fill plot of the kernel density estimate
+ BoxPlot % fill plot of the box between the quartiles
+ WhiskerPlot % line plot between the whisker ends
+ MedianPlot % scatter plot of the median (one point)
+ NotchPlots % scatter plots for the notch indicators
+ MeanPlot % line plot of the mean (horizontal line)
+ HistogramPlot % histogram of the data
+ ViolinPlotQ % fill plot of the Quartiles as shadow
+ end
+
+ properties (Dependent=true)
+ ViolinColor % fill color of the violin area and data points
+ ViolinAlpha % transparency of the violin area and data points
+ MarkerSize % marker size for the median dot
+ LineWidth % linewidth of the median plot
+ 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
+ ShowMean % whether to show mean indicator
+ ShowBox % whether to show the box
+ ShowMedian % whether to show the median line
+ ShowWhiskers % whether to show the whiskers
+ HalfViolin % whether to do a half violin(left, right side) or full
+ end
+
+ methods
+ function obj = Violin(data, pos, varargin)
+ %Violin plots a violin plot of some data at pos
+ % VIOLIN(DATA, POS) plots a violin at x-position POS for
+ % a vector of DATA points.
+ %
+ % VIOLIN(..., 'PARAM1', val1, 'PARAM2', val2, ...)
+ % specifies optional name/value pairs:
+ % 'Width' Width of the violin in axis space.
+ % Defaults to 0.3
+ % 'Bandwidth' Bandwidth of the kernel density
+ % estimate. Should be between 10% and
+ % 40% of the data range.
+ % 'ViolinColor' Fill color of the violin area
+ % and data points.Can be either a matrix
+ % nx3 or an array of up to two cells
+ % containing nx3 matrices.
+ % 'ViolinAlpha' Transparency of the violin area and data
+ % points. Can be either a single scalar
+ % value or an array of up to two cells
+ % containing scalar values. Defaults to 0.3.
+ % 'EdgeColor' Color of the violin area outline.
+ % Defaults to [0.5 0.5 0.5]
+ % 'BoxColor' Color of the box, whiskers, and the
+ % outlines of the median point and the
+ % notch indicators. Defaults to
+ % [0.5 0.5 0.5]
+ % 'MedianColor' Fill color of the median and notch
+ % indicators. Defaults to [1 1 1]
+ % 'ShowData' Whether to show data points.
+ % Defaults to true
+ % 'ShowNotches' Whether to show notch indicators.
+ % Defaults to false
+ % 'ShowMean' Whether to show mean indicator.
+ % Defaults to false
+ % 'ShowBox' Whether to show the box
+ % Defaults to true
+ % 'ShowMedian' Whether to show the median line
+ % Defaults to true
+ % 'ShowWhiskers' Whether to show the whiskers
+ % Defaults to true
+ % 'HalfViolin' Whether to do a half violin(left, right side) or
+ % full. Defaults to full.
+ % 'QuartileStyle' Option on how to display quartiles, with a
+ % boxplot or as a shadow. Defaults to boxplot.
+ % 'DataStyle' Defines the style to show the data points. Opts:
+ % 'scatter', 'histogram' or 'none'. Default is 'Scatter'.
+
+ st = dbstack; % get the calling function for reporting errors
+ namefun = st.name;
+ args = obj.checkInputs(data, pos, varargin{:});
+
+ if length(data)==1
+ data2 = [];
+ data = data{1};
+
+ else
+ data2 = data{2};
+ data = data{1};
+ end
+
+ if isempty(args.ViolinColor)
+ C = colororder;
+ args.ViolinColor = {repmat(C,ceil(size(data,2)/length(C)),1)};
+ end
+
+ data = data(not(isnan(data)));
+ data2 = data2(not(isnan(data2)));
+ if numel(data) == 1
+ obj.MedianPlot = scatter(pos, data, 'filled');
+ obj.MedianColor = args.MedianColor;
+ obj.MedianPlot.MarkerEdgeColor = args.EdgeColor;
+ return
+ end
+
+ hold('on');
+
+
+ %% Calculate kernel density estimation for the violin
+ [density, value, width] = obj.calcKernelDensity(data, args.Bandwidth, args.Width);
+
+ % also calculate the kernel density of the comparison data if
+ % provided
+ if ~isempty(data2)
+ [densityC, valueC, widthC] = obj.calcKernelDensity(data2, args.Bandwidth, args.Width);
+ end
+
+ %% Plot the data points within the violin area
+ if length(density) > 1
+ jitterstrength = interp1(value, density*width, data);
+ else % all data is identical:
+ jitterstrength = density*width;
+ end
+ if isempty(data2) % if no comparison data
+ jitter = 2*(rand(size(data))-0.5); % both sides
+ else
+ jitter = rand(size(data)); % only right side
+ end
+ switch args.HalfViolin % this is more modular
+ case 'left'
+ jitter = -1*(rand(size(data))); %left
+ case 'right'
+ jitter = 1*(rand(size(data))); %right
+ case 'full'
+ jitter = 2*(rand(size(data))-0.5);
+ end
+ % Make scatter plot
+ switch args.DataStyle
+ case 'scatter'
+ if ~isempty(data2)
+ jitter = 1*(rand(size(data))); %right
+ obj.ScatterPlot = ...
+ scatter(pos + jitter.*jitterstrength, data, 'filled');
+ % plot the data points within the violin area
+ if length(densityC) > 1
+ jitterstrength = interp1(valueC, densityC*widthC, data2);
+ else % all data is identical:
+ jitterstrength = densityC*widthC;
+ end
+ jitter = -1*rand(size(data2));% left
+ obj.ScatterPlot2 = ...
+ scatter(pos + jitter.*jitterstrength, data2, 'filled');
+ else
+ obj.ScatterPlot = ...
+ scatter(pos + jitter.*jitterstrength, data, 'filled');
+
+ end
+ case 'histogram'
+ [counts,edges] = histcounts(data, size(unique(data),1));
+ switch args.HalfViolin
+ case 'right'
+ obj.HistogramPlot= plot([pos-((counts')/max(counts))*max(jitterstrength)*2, pos*ones(size(counts,2),1)]',...
+ [edges(1:end-1)+max(diff(edges))/2; edges(1:end-1)+max(diff(edges))/2],'-','LineWidth',1, 'Color', 'k');
+ case 'left'
+ obj.HistogramPlot= plot([pos*ones(size(counts,2),1), pos+((counts')/max(counts))*max(jitterstrength)*2]',...
+ [edges(1:end-1)+max(diff(edges))/2; edges(1:end-1)+max(diff(edges))/2],'-','LineWidth',1, 'Color', 'k');
+ otherwise
+ fprintf([namefun, ' No histogram/bar plot option available for full violins, as it would look overcrowded.\n'])
+ end
+ case 'none'
+ end
+
+ %% Plot the violin
+ halfViol= ones(1, size(density,2));
+ if isempty(data2) % if no comparison data
+ switch args.HalfViolin
+ case 'right'
+ obj.ViolinPlot = ... % plot color will be overwritten later
+ fill([pos+density*width halfViol*pos], ...
+ [value value(end:-1:1)], [1 1 1]);
+ case 'left'
+ obj.ViolinPlot = ... % plot color will be overwritten later
+ fill([halfViol*pos pos-density(end:-1:1)*width], ...
+ [value value(end:-1:1)], [1 1 1]);
+ case 'full'
+ 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]);
+ end
+ else
+ % plot right half of the violin
+ obj.ViolinPlot = ...
+ fill([pos+density*width pos-density(1)*width], ...
+ [value value(1)], [1 1 1]);
+ % plot left half of the violin
+ obj.ViolinPlot2 = ...
+ fill([pos-densityC(end)*widthC pos-densityC(end:-1:1)*widthC], ...
+ [valueC(end) valueC(end:-1:1)], [1 1 1]);
+ end
+
+ %% Plot the quartiles within the violin
+ quartiles = quantile(data, [0.25, 0.5, 0.75]);
+ flat= [halfViol*pos halfViol*pos];
+ switch args.QuartileStyle
+ case 'shadow'
+ switch args.HalfViolin
+ case 'right'
+ w = [pos+density*width halfViol*pos];
+ h= [value value(end:-1:1)];
+ case 'left'
+ w = [halfViol*pos pos-density(end:-1:1)*width];
+ h= [value value(end:-1:1)];
+ case 'full'
+ w = [pos+density*width pos-density(end:-1:1)*width];
+ h= [value value(end:-1:1)];
+ end
+ w(hquartiles(3))=flat((h>quartiles(3)));
+ obj.ViolinPlotQ = ... % plot color will be overwritten later
+ fill(w, ...
+ h, [1 1 1]);
+ case 'boxplot'
+ obj.BoxPlot = ... % plot color will be overwritten later
+ fill(pos+[-1,1,1,-1]*args.BoxWidth, ...
+ [quartiles(1) quartiles(1) quartiles(3) quartiles(3)], ...
+ [1 1 1]);
+ case 'none'
+ end
+
+ %% 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)));
+ hiwhisker = quartiles(3) + 1.5*IQR;
+ hiwhisker = min(hiwhisker, max(data(data < hiwhisker)));
+ if ~isempty(lowhisker) && ~isempty(hiwhisker)
+ obj.WhiskerPlot = plot([pos pos], [lowhisker hiwhisker]);
+ end
+
+ % Median
+ obj.MedianPlot = scatter(pos, quartiles(2), args.MarkerSize, [1 1 1], 'filled');
+
+ % Notches
+ obj.NotchPlots = ...
+ scatter(pos, quartiles(2)-1.57*IQR/sqrt(length(data)), ...
+ [], [1 1 1], 'filled', '^');
+ obj.NotchPlots(2) = ...
+ scatter(pos, quartiles(2)+1.57*IQR/sqrt(length(data)), ...
+ [], [1 1 1], 'filled', 'v');
+
+ %% Set graphical preferences
+ obj.EdgeColor = args.EdgeColor;
+ obj.MedianPlot.LineWidth = args.LineWidth;
+ obj.BoxColor = args.BoxColor;
+ obj.BoxWidth = args.BoxWidth;
+ obj.MedianColor = args.MedianColor;
+ obj.ShowData = args.ShowData;
+ obj.ShowNotches = args.ShowNotches;
+ obj.ShowMean = args.ShowMean;
+ obj.ShowBox = args.ShowBox;
+ obj.ShowMedian = args.ShowMedian;
+ obj.ShowWhiskers = args.ShowWhiskers;
+
+ if not(isempty(args.ViolinColor))
+ if size(args.ViolinColor{1},1) > 1
+ ViolinColor{1} = args.ViolinColor{1}(pos,:);
+ else
+ ViolinColor{1} = args.ViolinColor{1};
+ end
+ if length(args.ViolinColor)==2
+ if size(args.ViolinColor{2},1) > 1
+ ViolinColor{2} = args.ViolinColor{2}(pos,:);
+ else
+ ViolinColor{2} = args.ViolinColor{2};
+ end
+ else
+ ViolinColor{2} = ViolinColor{1};
+ end
+ else
+ % defaults
+ if args.scpltBool
+ ViolinColor{1} = obj.ScatterPlot.CData;
+ else
+ ViolinColor{1} = [0 0 0];
+ end
+ ViolinColor{2} = [0 0 0];
+ end
+ obj.ViolinColor = ViolinColor;
+
+
+ if not(isempty(args.ViolinAlpha))
+ if length(args.ViolinAlpha{1})>1
+ error('Only scalar values are accepted for the alpha color channel');
+ else
+ ViolinAlpha{1} = args.ViolinAlpha{1};
+ end
+ if length(args.ViolinAlpha)==2
+ if length(args.ViolinAlpha{2})>1
+ error('Only scalar values are accepted for the alpha color channel');
+ else
+ ViolinAlpha{2} = args.ViolinAlpha{2};
+ end
+ else
+ ViolinAlpha{2} = ViolinAlpha{1}/2; % default unless specified
+ end
+ else
+ % default
+ ViolinAlpha = {1,1};
+ end
+ obj.ViolinAlpha = ViolinAlpha;
+
+
+ end
+
+ %% SET METHODS
+ function set.EdgeColor(obj, color)
+ if ~isempty(obj.ViolinPlot)
+ obj.ViolinPlot.EdgeColor = color;
+ obj.ViolinPlotQ.EdgeColor = color;
+ if ~isempty(obj.ViolinPlot2)
+ obj.ViolinPlot2.EdgeColor = color;
+ end
+ end
+ end
+
+ function color = get.EdgeColor(obj)
+ if ~isempty(obj.ViolinPlot)
+ color = obj.ViolinPlot.EdgeColor;
+ end
+ end
+
+
+ function set.MedianColor(obj, color)
+ obj.MedianPlot.MarkerFaceColor = color;
+ if ~isempty(obj.NotchPlots)
+ obj.NotchPlots(1).MarkerFaceColor = color;
+ obj.NotchPlots(2).MarkerFaceColor = color;
+ end
+ end
+
+ function color = get.MedianColor(obj)
+ color = obj.MedianPlot.MarkerFaceColor;
+ end
+
+
+ function set.BoxColor(obj, 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;
+ elseif ~isempty(obj.ViolinPlotQ)
+ 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)
+ if ~isempty(obj.BoxPlot)
+ color = obj.BoxPlot.FaceColor;
+ end
+ end
+
+
+ function set.BoxWidth(obj,width)
+ if ~isempty(obj.BoxPlot)
+ pos=mean(obj.BoxPlot.XData);
+ obj.BoxPlot.XData=pos+[-1,1,1,-1]*width;
+ end
+ 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{1};
+ obj.ScatterPlot.MarkerFaceColor = color{1};
+ obj.MeanPlot.Color = color{1};
+ if ~isempty(obj.ViolinPlot2)
+ obj.ViolinPlot2.FaceColor = color{2};
+ obj.ScatterPlot2.MarkerFaceColor = color{2};
+ end
+ if ~isempty(obj.ViolinPlotQ)
+ obj.ViolinPlotQ.FaceColor = color{1};
+ end
+ for idx = 1: size(obj.HistogramPlot,1)
+ obj.HistogramPlot(idx).Color = color{1};
+ end
+ end
+
+ function color = get.ViolinColor(obj)
+ color{1} = obj.ViolinPlot.FaceColor;
+ if ~isempty(obj.ViolinPlot2)
+ color{2} = obj.ViolinPlot2.FaceColor;
+ end
+ end
+
+
+ function set.ViolinAlpha(obj, alpha)
+ obj.ViolinPlotQ.FaceAlpha = .8;
+ obj.ViolinPlot.FaceAlpha = alpha{1};
+ obj.ScatterPlot.MarkerFaceAlpha = alpha{1};
+ if ~isempty(obj.ViolinPlot2)
+ obj.ViolinPlot2.FaceAlpha = alpha{2};
+ obj.ScatterPlot2.MarkerFaceAlpha = alpha{2};
+ end
+ end
+
+ function alpha = get.ViolinAlpha(obj)
+ alpha{1} = obj.ViolinPlot.FaceAlpha;
+ if ~isempty(obj.ViolinPlot2)
+ alpha{2} = obj.ViolinPlot2.FaceAlpha;
+ end
+ end
+
+
+ function set.ShowData(obj, yesno)
+ if yesno
+ obj.ScatterPlot.Visible = 'on';
+ for idx = 1: size(obj.HistogramPlot,1)
+ obj.HistogramPlot(idx).Visible = 'on';
+ end
+ else
+ obj.ScatterPlot.Visible = 'off';
+ for idx = 1: size(obj.HistogramPlot,1)
+ obj.HistogramPlot(idx).Visible = 'off';
+ end
+ end
+ if ~isempty(obj.ScatterPlot2)
+ obj.ScatterPlot2.Visible = obj.ScatterPlot.Visible;
+ end
+
+ end
+
+ function yesno = get.ShowData(obj)
+ if ~isempty(obj.ScatterPlot)
+ yesno = strcmp(obj.ScatterPlot.Visible, 'on');
+ end
+ end
+
+
+ function set.ShowNotches(obj, yesno)
+ 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)
+ if ~isempty(obj.NotchPlots)
+ yesno = strcmp(obj.NotchPlots(1).Visible, 'on');
+ end
+ end
+
+
+ function set.ShowMean(obj, yesno)
+ if ~isempty(obj.MeanPlot)
+ if yesno
+ obj.MeanPlot.Visible = 'on';
+ else
+ obj.MeanPlot.Visible = 'off';
+ end
+ end
+ end
+
+ function yesno = get.ShowMean(obj)
+ if ~isempty(obj.BoxPlot)
+ yesno = strcmp(obj.BoxPlot.Visible, 'on');
+ end
+ end
+
+
+ function set.ShowBox(obj, yesno)
+ if ~isempty(obj.BoxPlot)
+ if yesno
+ obj.BoxPlot.Visible = 'on';
+ else
+ obj.BoxPlot.Visible = 'off';
+ end
+ end
+ end
+
+ function yesno = get.ShowBox(obj)
+ if ~isempty(obj.BoxPlot)
+ yesno = strcmp(obj.BoxPlot.Visible, 'on');
+ end
+ end
+
+
+ function set.ShowMedian(obj, yesno)
+ if ~isempty(obj.MedianPlot)
+ if yesno
+ obj.MedianPlot.Visible = 'on';
+ else
+ obj.MedianPlot.Visible = 'off';
+ end
+ end
+ end
+
+ function yesno = get.ShowMedian(obj)
+ if ~isempty(obj.MedianPlot)
+ yesno = strcmp(obj.MedianPlot.Visible, 'on');
+ end
+ end
+
+
+ function set.ShowWhiskers(obj, yesno)
+ if ~isempty(obj.WhiskerPlot)
+ if yesno
+ obj.WhiskerPlot.Visible = 'on';
+ else
+ obj.WhiskerPlot.Visible = 'off';
+ end
+ end
+ end
+
+ function yesno = get.ShowWhiskers(obj)
+ if ~isempty(obj.WhiskerPlot)
+ yesno = strcmp(obj.WhiskerPlot.Visible, 'on');
+ end
+ end
+
+ end
+
+ methods (Access=private)
+ function results = checkInputs(~, data, pos, varargin)
+ isscalarnumber = @(x) (isnumeric(x) & isscalar(x));
+ p = inputParser();
+ p.addRequired('Data', @(x)isnumeric(vertcat(x{:})));
+ p.addRequired('Pos', isscalarnumber);
+ p.addParameter('Width', 0.3, isscalarnumber);
+ p.addParameter('Bandwidth', [], isscalarnumber);
+ iscolor = @(x) (isnumeric(x) & size(x,2) == 3);
+ p.addParameter('ViolinColor', [], @(x)iscolor(vertcat(x{:})));
+ p.addParameter('MarkerSize', 36, @isnumeric);
+ p.addParameter('LineWidth', 0.75, @isnumeric);
+ p.addParameter('BoxColor', [0.5 0.5 0.5], iscolor);
+ 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,0.15}, @(x)isnumeric(vertcat(x{:})));
+ isscalarlogical = @(x) (islogical(x) & isscalar(x));
+ p.addParameter('ShowData', true, isscalarlogical);
+ p.addParameter('ShowNotches', false, isscalarlogical);
+ p.addParameter('ShowMean', false, isscalarlogical);
+ p.addParameter('ShowBox', true, isscalarlogical);
+ p.addParameter('ShowMedian', true, isscalarlogical);
+ p.addParameter('ShowWhiskers', true, isscalarlogical);
+ validSides={'full', 'right', 'left'};
+ checkSide = @(x) any(validatestring(x, validSides));
+ p.addParameter('HalfViolin', 'full', checkSide);
+ validQuartileStyles={'boxplot', 'shadow', 'none'};
+ checkQuartile = @(x)any(validatestring(x, validQuartileStyles));
+ p.addParameter('QuartileStyle', 'boxplot', checkQuartile);
+ validDataStyles = {'scatter', 'histogram', 'none'};
+ checkStyle = @(x)any(validatestring(x, validDataStyles));
+ p.addParameter('DataStyle', 'scatter', checkStyle);
+
+ p.parse(data, pos, varargin{:});
+ results = p.Results;
+ end
+ end
+
+ methods (Static)
+ function [density, value, width] = calcKernelDensity(data, bandwidth, width)
+ if isempty(data)
+ error('Empty input data');
+ end
+ [density, value] = ksdensity(data, 'bandwidth', bandwidth);
+ density = density(value >= min(data) & value <= max(data));
+ value = value(value >= min(data) & value <= max(data));
+ value(1) = min(data);
+ value(end) = max(data);
+ value = [value(1)*(1-1E-5), value, value(end)*(1+1E-5)];
+ density = [0, density, 0];
+
+ % all data is identical
+ if min(data) == max(data)
+ density = 1;
+ end
+
+ width = width/max(density);
+ end
+ end
+end
+
diff --git a/test_cases/testviolinplot.m b/test_cases/testviolinplot.m
index 51e2a15..ca74b7f 100644
--- a/test_cases/testviolinplot.m
+++ b/test_cases/testviolinplot.m
@@ -1,27 +1,27 @@
+function testviolinplot()
+figure();
+% One could use tiled layout for better plotting them but it would be
+% incompatible with older versions
+subplot(2,4,1);
% TEST CASE 1
disp('Test 1: Violin plot default options');
load carbig MPG Origin
Origin = cellstr(Origin);
-figure
vs = violinplot(MPG, Origin);
-ylabel('Fuel Economy in MPG');
-xlim([0.5, 7.5]);
-
-disp('Test 1 passed ok');
+plotdetails(1);
% TEST CASE 2
disp('Test 2: Test the plot ordering option');
grouporder={'USA','Sweden','Japan','Italy','Germany','France','England'};
-figure;
+subplot(2,4,2);
vs2 = violinplot(MPG,Origin,'GroupOrder',grouporder);
-disp('Test 2 passed ok');
-xlim([0.5, 7.5]);
+plotdetails(2);
% TEST CASE 3
disp('Test 3: Test the numeric input construction mode');
-figure
+subplot(2,4,3);
cats = categorical(Origin);
catnames = (unique(cats)); % this ignores categories without any data
catnames_labels = {};
@@ -32,16 +32,54 @@
thisData(1:length(MPG(cats == thisCat)),n) = MPG(cats == thisCat);
end
vs3 = violinplot(thisData,catnames_labels);
-xlim([0.5, 7.5]);
-disp('Test 3 passed ok');
+plotdetails(3);
% TEST CASE 4
disp('Test 4: Test two sided violin plots. Japan is being compared.');
-figure
+subplot(2,4,4);
C = colororder;
vs4 = violinplot({thisData,repmat(thisData(:,5),1,7)},catnames_labels,'ViolinColor',{C,C(5,:)},'ViolinAlpha',{0.3 0.3});
-xlim([0.5, 7.5]);
-disp('Test 4 passed ok');
+plotdetails(4);
+
+% TEST CASE 5
+disp('Test 5: Test shadow for quartiles.');
+subplot(2,4,5);
+vs5 = violinplot(MPG, Origin, 'QuartileStyle','shadow');
+plotdetails(5);
+
+% TEST CASE 6
+disp('Test 6: Test plotting only right side & histogram plot, with quartiles as boxplot.');
+subplot(2,4,6);
+vs5 = violinplot(MPG, Origin, 'QuartileStyle','boxplot', 'HalfViolin','right',...
+ 'DataStyle', 'histogram');
+plotdetails(6);
+
+% TEST CASE 7
+disp('Test 7: Test plotting only left side & histogram plot, and quartiles as shadow.');
+subplot(2,4,7);
+vs5 = violinplot(MPG, Origin, 'QuartileStyle','shadow', 'HalfViolin','left',...
+ 'DataStyle', 'histogram', 'ShowMean', true);
+plotdetails(7);
+% TEST CASE 8
+disp('Test 8: Same as previous one, just removing the data of half of the violins afterwards.');
+subplot(2,4,8);
+vs5 = violinplot(MPG, Origin, 'QuartileStyle','shadow', 'HalfViolin','left',...
+ 'DataStyle', 'histogram', 'ShowMean', false);
+plotdetails(8);
+for n= 1:round(length(vs5)/2)
+ vs5(1,n).ShowData = 0;
+end
%other test cases could be added here
+end
+
+function plotdetails(n)
+title(sprintf('Test %02.0f \n',n));
+ylabel('Fuel Economy in MPG ');
+xlim([0, 8]); grid minor;
+set(gca, 'color', 'none');
+xtickangle(-30);
+fprintf('Test %02.0f passed ok! \n ',n);
+end
+
diff --git a/test_violn_plots.png b/test_violn_plots.png
new file mode 100644
index 0000000..488d089
Binary files /dev/null and b/test_violn_plots.png differ