beanPlot.m 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. function hBeanAx = beanPlot(groupData, groupLabels, colors, offsetScale, hAx, varargin)
  2. if ~exist('hAx', 'var')
  3. hBeanFig = createFullScreenFig;
  4. hBeanFig.Position(4) = hBeanFig.Position(3) / 2;
  5. hAx = gca;
  6. end
  7. switch nargin
  8. case 6
  9. support = varargin{1};
  10. plotOrientation = 'horizontal'; % Can change to plotHorizontalBool.
  11. halveBean = false;
  12. useAlpha = true;
  13. bwOption = true;
  14. case 7
  15. support = varargin{1};
  16. plotOrientation = varargin{2};
  17. halveBean = false;
  18. useAlpha = true;
  19. bwOption = true;
  20. case 8
  21. support = varargin{1};
  22. plotOrientation = varargin{2};
  23. halveBean = varargin{3};
  24. useAlpha = true;
  25. bwOption = true;
  26. case 9
  27. support = varargin{1};
  28. plotOrientation = varargin{2};
  29. halveBean = varargin{3};
  30. useAlpha = varargin{4};
  31. bwOption = true;
  32. case 10
  33. support = varargin{1};
  34. plotOrientation = varargin{2};
  35. halveBean = varargin{3};
  36. useAlpha = varargin{4};
  37. bwOption = varargin{5};
  38. otherwise
  39. support = 'unbounded';
  40. plotOrientation = 'horizontal'; % Can change to plotHorizontalBool.
  41. halveBean = false;
  42. useAlpha = true;
  43. bwOption = true;
  44. end
  45. hold(hAx, 'on');
  46. nGroups = numel(groupLabels);
  47. zeroLineX = [0 0];
  48. if nGroups == 1
  49. zeroLineColor = [1 1 1] * 0.5;
  50. zeroLineY = offsetScale * [1 1.5];
  51. else
  52. zeroLineColor = mean(rgb2gray(colors));
  53. zeroLineY = offsetScale * [1 / 2 (nGroups + 1 / 2)];
  54. end
  55. % Flip variables for vertical plotting orientation.
  56. if strcmp(plotOrientation, 'vertical')
  57. [zeroLineX, zeroLineY] = deal(zeroLineY, zeroLineX);
  58. end
  59. hZeroLine = plot(zeroLineX, zeroLineY, 'LineStyle', '--', 'Color', zeroLineColor);
  60. bandWidth = nan(1, nGroups);
  61. for iGroup = 1: nGroups
  62. data = groupData{iGroup};
  63. if numel(data) > 1 && range(data) ~=0
  64. if ~strcmp(support, 'unbounded')
  65. [bw, f, xi, cdf] = kde(data, 2^10, support(1), support(2));
  66. else
  67. [bw, f, xi, cdf] = kde(data, 2^10);
  68. end
  69. bandWidth(iGroup) = bw;
  70. end
  71. end
  72. bwMean = nanmedian(bandWidth);
  73. % 1/2 for the half bean and 2 / 15 for two times the data ticks length.
  74. if halveBean
  75. halfOffsetFactor = (1 / 2 + 2 / 15);
  76. else
  77. halfOffsetFactor = 1 + 3 / 15;
  78. end
  79. for iGroup = 1: nGroups
  80. % Define data and plot format.
  81. data = groupData{iGroup};
  82. iColor = colors(iGroup, :);
  83. offset = iGroup * offsetScale * halfOffsetFactor;
  84. % 1-D strip plot of individual data points.
  85. dataPointAlpha = 0.1;
  86. if nGroups == 1
  87. dataPointColor = [1 1 1] * 0.5;
  88. else
  89. dataPointColor = mean(rgb2gray(colors));
  90. end
  91. if useAlpha
  92. dataPointColor = [dataPointColor dataPointAlpha];
  93. end
  94. for iDataPoint = 1: numel(data)
  95. dataPointX = data(iDataPoint) * [1 1];
  96. if halveBean
  97. dataPointY = offset + offsetScale / 15 * [-1 0];
  98. else
  99. dataPointY = offset + offsetScale / 20 * [-1 1];
  100. end
  101. % Flip variables for vertical plotting orientation.
  102. if strcmp(plotOrientation, 'vertical')
  103. [dataPointX, dataPointY] = deal(dataPointY, dataPointX);
  104. end
  105. plot(dataPointX, dataPointY, 'Color', dataPointColor, 'LineWidth', 0.5);
  106. end
  107. % Get kernel density estimate.
  108. if bwOption
  109. bw = bwMean; % Dirty way fo homogeneizing the bandwidth.
  110. if numel(data) <=10
  111. bw = numel(data) / 10 * bw;
  112. end
  113. else
  114. bw = bandWidth(iGroup);
  115. end
  116. % end
  117. if numel(data) > 1 && range(data) ~=0
  118. % if ~strcmp(support, 'unbounded')
  119. % [bandwidth, f, xi, cdf] = kde(data, 2^10, support(1), support(2));
  120. [f, xi] = ksdensity(data, 'Support', support, 'BoundaryCorrection', 'reflection', 'BandWidth', bw);
  121. % else
  122. % [bandWidth, f, xi, cdf] = kde(data, 2^10);
  123. % end
  124. f = f(:)';
  125. xi = xi(:)';
  126. else
  127. f = 1;
  128. xi = data(1);
  129. end
  130. % [f, xi] = ksdensity(data, 'Support', support, 'BoundaryCorrection', 'reflection', 'BandWidth', bw);
  131. % Normalize the area for plots with different value ranges.
  132. maxF = max(f);
  133. f = f / maxF * offsetScale / 2;
  134. % Half bean.
  135. if halveBean
  136. areaXCoords = [xi fliplr(xi)]; % This is the variable value.
  137. areaYCoords = [f zeros(size(f))] + offset; % This is the variable frequency.
  138. else
  139. % Concatenate to make the bean area.
  140. areaXCoords = [xi xi(end: -1: 1)]; % This is the variable value.
  141. areaYCoords = [-f f(end: -1: 1)] + offset; % This is the variable frequency.
  142. end
  143. % Flip variables for vertical plotting orientation.
  144. if strcmp(plotOrientation, 'vertical')
  145. [areaXCoords, areaYCoords] = deal(areaYCoords, areaXCoords);
  146. end
  147. fill(areaXCoords, areaYCoords, iColor, ...
  148. 'FaceAlpha', 0.7, 'EdgeColor', 'none')
  149. % Now plot quantiles
  150. % quantileValue = 0.25;
  151. lineFormatCell = {'Color', iColor, 'LineWidth', 1};
  152. % plotQuantileBean(data, quantileValue, offsetX, lineFormatCell)
  153. % quantileValue = 0.5;
  154. % lineFormatCell{end} = 2;
  155. % plotQuantileBean(data, quantileValue, offsetX, lineFormatCell)
  156. % quantileValue = 0.75;
  157. % lineFormatCell{end} = 1;
  158. % plotQuantileBean(data, quantileValue, offsetX, lineFormatCell)
  159. lineFormatCell{end} = 2;
  160. plotBeanMean(data, offsetScale, offset, lineFormatCell, plotOrientation, halveBean)
  161. end
  162. hBeanAx = gca;
  163. prettifyAxes(hBeanAx)
  164. hBeanAx.XMinorTick = 'off';
  165. hBeanAx.YMinorTick = 'off';
  166. % Flip variables for vertical plotting orientation.
  167. if strcmp(plotOrientation, 'vertical')
  168. hBeanAx.XTick = offsetScale * (1: nGroups) * halfOffsetFactor;
  169. hBeanAx.XTickLabel = groupLabels;
  170. hBeanAx.XTickLabelRotation = 0;
  171. % if nGroups > 1
  172. % hBeanAx.PlotBoxAspectRatio = [2 1 1];
  173. % end
  174. if halveBean
  175. hBeanAx.XLim = offsetScale * [(halfOffsetFactor - 1 / 15) ;
  176. ( 1 / 2 + nGroups * halfOffsetFactor + 1 / 15)];
  177. else
  178. hBeanAx.XLim = offsetScale * [(- 1 / 2 + halfOffsetFactor - 2 / 15) ...
  179. (+ 1 / 2 + halfOffsetFactor * nGroups + 2 / 15)];
  180. end
  181. else
  182. hBeanAx.YTick = offsetScale * (1: nGroups) * halfOffsetFactor;
  183. hBeanAx.YTickLabel = groupLabels;
  184. hBeanAx.YTickLabelRotation = 0;
  185. % if nGroups > 1
  186. % hBeanAx.PlotBoxAspectRatio = [1 1 1];
  187. % end
  188. % Add option for half bean
  189. if halveBean
  190. hBeanAx.YLim = offsetScale * [(halfOffsetFactor - 1.2 / 15) ;
  191. ( 1 / 2 + nGroups * halfOffsetFactor + 1 / 15)];
  192. else
  193. hBeanAx.YLim = offsetScale * [(- 1 / 2 + halfOffsetFactor - 2 / 15) ...
  194. (+ 1 / 2 + halfOffsetFactor * nGroups + 2 / 15)];
  195. end
  196. if nGroups == 1; axis tight; end
  197. end
  198. % Flip variables for vertical plotting orientation.
  199. if strcmp(plotOrientation, 'vertical')
  200. hZeroLine.XData = get(gca, 'XLim');
  201. else
  202. hZeroLine.YData = get(gca, 'YLim');
  203. end
  204. end
  205. function plotQuantileBean(data, quantileValue, offset, lineFormatCell)
  206. [f, xi] = ksdensity(data);
  207. quantileYCoords = quantile(data, quantileValue) * [1 1];
  208. [~, quantileInd] = min(abs(quantileYCoords(1) - xi));
  209. quantileXCoords = [-1 1] * f(quantileInd);
  210. hold on
  211. plot(quantileXCoords + offset, quantileYCoords, lineFormatCell{:})
  212. end
  213. function plotBeanMean(data, offsetScale, offset, lineFormatCell, plotOrientation, halveBean)
  214. [f, xi] = ksdensity(data);
  215. meanXCoords = nanmean(data) * [1 1];
  216. if halveBean
  217. meanYCoords = [0 1] * offsetScale / 2 + offset;
  218. else
  219. meanYCoords = [-1 1] * offsetScale / 2 + offset;
  220. end
  221. [~, meanInd] = min(abs(meanYCoords(1) - xi));
  222. if strcmp(plotOrientation, 'vertical')
  223. [meanXCoords, meanYCoords] = deal(meanYCoords, meanXCoords);
  224. end
  225. hold on
  226. plot(meanXCoords, meanYCoords, lineFormatCell{:})
  227. end