-
Notifications
You must be signed in to change notification settings - Fork 103
/
violinplot.m
216 lines (205 loc) · 8.29 KB
/
violinplot.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
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
% <a href="matlab:help('Violin')">Violin</a> 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
% 'Orientation' Orientation of the violin plot.
% Defaults to 'vertical'.
% 'Parent' The parent axis of the violin plot.
% 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
if strcmp(violins(1).Orientation,'vertical')
set(violins(1).Parent, 'XTick', 1:length(catnames), 'XTickLabels', catnames);
else
set(violins(1).Parent, 'YTick', 1:length(catnames), 'YTickLabels', catnames);
end
set(violins(1).Parent,'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
if strcmp(violins(1).Orientation,'vertical')
set(violins(1).Parent, 'XTick', 1:length(catnames), 'XTickLabels', catnames);
else
set(violins(1).Parent, 'YTick', 1:length(catnames), 'YTickLabels', catnames);
end
set(violins(1).Parent,'Box','on');
return
else
data = {data};
end
end
% 1D data, no categories
if not(hascategories) && isvector(data{1})
violins = Violin(data, 1, varargin{:});
if strcmp(violins(1).Orientation,'vertical')
set(violins(1).Parent, 'XTick', 1);
else
set(violins(1).Parent, 'yTick', 1);
end
% 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
if strcmp(violins(1).Orientation,'vertical')
set(violins(1).Parent, 'XTick', 1:size(data{1}, 2));
else
set(violins(1).Parent, 'YTick', 1:size(data{1}, 2));
end
if hascategories && length(cats) == size(data{1}, 2)
if strcmp(violins(1).Orientation,'vertical')
set(violins(1).Parent, 'XTickLabels', cats);
else
set(violins(1).Parent, 'YTickLabels', cats);
end
end
end
set(violins(1).Parent,'Box','on');
end