function violins = violinplot(data, cats, varargin) %Violinplots plots violin plots of some data and categories % VIOLINPLOT(DATA) plots a violin of a double vector DATA % % VIOLINPLOT(DATAMATRIX) plots violins for each column in % DATAMATRIX. % % VIOLINPLOT(DATAMATRIX, CATEGORYNAMES) plots violins for each % column in DATAMATRIX and labels them according to the names in the % cell-of-strings CATEGORYNAMES. % % In the cases above DATA and DATAMATRIX can be a vector or a matrix, % respectively, either as is or wrapped in a cell. % To produce violins which have one distribution on one half and another % one on the other half, DATA and DATAMATRIX have to be cell arrays % with two elements, each containing a vector or a matrix. The number of % columns of the two data sets has to be the same. % % VIOLINPLOT(DATA, CATEGORIES) where double vector DATA and vector % CATEGORIES are of equal length; plots violins for each category in % DATA. % % VIOLINPLOT(TABLE), VIOLINPLOT(STRUCT), VIOLINPLOT(DATASET) % plots violins for each column in TABLE, each field in STRUCT, and % each variable in DATASET. The violins are labeled according to % the table/dataset variable name or the struct field name. % % violins = VIOLINPLOT(...) returns an object array of % Violin objects. % % VIOLINPLOT(..., 'PARAM1', val1, 'PARAM2', val2, ...) % specifies optional name/value pairs for all violins: % '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. Accepts % 1x3 color vector or nx3 color vector where n = num % groups. In case of two data sets being compared it can % be 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. % 'MarkerSize' Size of the data points, if shown. % Defaults to 24 % 'MedianMarkerSize' Size of the median indicator, if shown. % Defaults to 36 % '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 % 'GroupOrder' Cell of category names in order to be plotted. % Defaults to alphabetical ordering % Copyright (c) 2016, Bastian Bechtold % This code is released under the terms of the BSD 3-clause license hascategories = exist('cats','var') && not(isempty(cats)); %parse the optional grouporder argument %if it exists parse the categories order % but also delete it from the arguments passed to Violin grouporder = {}; idx=find(strcmp(varargin, 'GroupOrder')); if ~isempty(idx) && numel(varargin)>idx if iscell(varargin{idx+1}) grouporder = varargin{idx+1}; varargin(idx:idx+1)=[]; else error('Second argument of ''GroupOrder'' optional arg must be a cell of category names') end end % check and correct the structure of ViolinColor input idx=find(strcmp(varargin, 'ViolinColor')); if ~isempty(idx) && iscell(varargin{idx+1}) if length(varargin{idx+1}(:))>2 error('ViolinColor input can be at most a two element cell array'); end elseif ~isempty(idx) && isnumeric(varargin{idx+1}) varargin{idx+1} = varargin(idx+1); end % check and correct the structure of ViolinAlpha input idx=find(strcmp(varargin, 'ViolinAlpha')); if ~isempty(idx) && iscell(varargin{idx+1}) if length(varargin{idx+1}(:))>2 error('ViolinAlpha input can be at most a two element cell array'); end elseif ~isempty(idx) && isnumeric(varargin{idx+1}) varargin{idx+1} = varargin(idx+1); end % tabular data if isa(data, 'dataset') || isstruct(data) || istable(data) if isa(data, 'dataset') colnames = data.Properties.VarNames; elseif istable(data) colnames = data.Properties.VariableNames; elseif isstruct(data) colnames = fieldnames(data); end catnames = {}; if isempty(grouporder) for n=1:length(colnames) if isnumeric(data.(colnames{n})) catnames = [catnames colnames{n}]; %#ok<*AGROW> end end catnames = sort(catnames); else for n=1:length(grouporder) if isnumeric(data.(grouporder{n})) catnames = [catnames grouporder{n}]; end end end for n=1:length(catnames) thisData = data.(catnames{n}); violins(n) = Violin({thisData}, n, varargin{:}); end set(gca, 'XTick', 1:length(catnames), 'XTickLabels', catnames); set(gca,'Box','on'); return elseif iscell(data) && length(data(:))==2 % cell input if not(size(data{1},2)==size(data{2},2)) error('The two input data matrices have to have the same number of columns'); end elseif iscell(data) && length(data(:))>2 % cell input error('Up to two datasets can be compared'); elseif isnumeric(data) % numeric input % 1D data, one category for each data point if hascategories && numel(data) == numel(cats) if isempty(grouporder) cats = categorical(cats); else cats = categorical(cats, grouporder); end catnames = (unique(cats)); % this ignores categories without any data catnames_labels = {}; for n = 1:length(catnames) thisCat = catnames(n); catnames_labels{n} = char(thisCat); thisData = data(cats == thisCat); violins(n) = Violin({thisData}, n, varargin{:}); end set(gca, 'XTick', 1:length(catnames), 'XTickLabels', catnames_labels); set(gca,'Box','on'); return else data = {data}; end end % 1D data, no categories if not(hascategories) && isvector(data{1}) violins = Violin(data, 1, varargin{:}); set(gca, 'XTick', 1); % 2D data with or without categories elseif ismatrix(data{1}) for n=1:size(data{1}, 2) thisData = cellfun(@(x)x(:,n),data,'UniformOutput',false); violins(n) = Violin(thisData, n, varargin{:}); end set(gca, 'XTick', 1:size(data{1}, 2)); if hascategories && length(cats) == size(data{1}, 2) set(gca, 'XTickLabels', cats); end end set(gca,'Box','on'); end