--- boxplot_v3.m	2020-06-06 20:36:39.901273288 +0300
+++ boxplot_v4.m	2020-06-13 20:18:41.609426905 +0300
@@ -3,6 +3,7 @@
 ## Copyright (C) 2011 Pascal Dupuis <Pascal.Dupuis@worldonline.be>
 ## Copyright (C) 2012 Juan Pablo Carbajal <carbajal@ifi.uzh.ch>
 ## Copyright (C) 2016 Pascal Dupuis <cdemills@gmail.com>
+## Copyright (C) 2020 Philip Nienhuis <prnienhuis@users.sf.net>
 ## Copyright (C) 2020 Andreas Bertsatos <abertsatos@biol.uoa.gr>
 ##
 ## This program is free software; you can redistribute it and/or modify it under
@@ -94,7 +95,9 @@
 ## @item @tab scalar @tab Proportional width of the notch.
 ##
 ## @item 'Symbol' @tab '.' @tab Defines only outliers between 1.5 and 3 IQR.
-## @item @tab ['x','*'] @tab 2nd character defines outliers > 3 IQR
+## @item @tab ['x','*'] @tab 2nd character defines outliers > 3 IQR.  Valid
+## characters are: '+', 'o', '*', '.', 'x', 's', 'd', '^', 'v', '<', '>',
+## 'p', and 'h'.  See @function{plot} markers for more info.
 ##
 ## @item 'Orientation' @tab 'vertical' @tab Default value, can also be defined
 ## with numerical 1.
@@ -212,7 +215,8 @@
           case 5
             maxwhisker = dummy;
           otherwise
-            error("No positional argument allowed at position %d", --indopt);
+            error("Boxplot: no positional argument allowed at position %d", ...
+                  --indopt);
         endswitch
       endif
       numarg--;
@@ -231,34 +235,55 @@
             ## Check for string input: "on" or "off"
             if strcmpi (notched, "on")
               notched = 1;
-            endif
-            if strcmpi (notched, "off")
+            elseif strcmpi (notched, "off")
               notched = 0;
+            else
+              msg = ["Boxplot: 'Notch' input argument takes only 'on'", ...
+                    " or 'off' as input parameter"];
+              error (msg);
             endif
           case 2
             symbol = varargin{indopt};
           case 3
             orientation = varargin{indopt};
             ## Check for string input: "vertical" or "horizontal"
-            if strcmpi (notched, "vertical")
+            if strcmpi (orientation, "vertical")
               notched = 1;
-            endif
-            if strcmpi (notched, "horizontal")
+            elseif strcmpi (orientation, "horizontal")
               notched = 0;
+            else
+              msg = ["Boxplot: 'Orientation' input argument takes only", ...
+                    " 'vertical' or 'horizontal' as input parameter"];
+              error (msg);
             endif
           case 4
             maxwhisker = varargin{indopt};
+            ## Check for scalar value
+            if (! isscalar (maxwhisker))
+              msg = ["Boxplot: 'Whisker' input argument takes only", ...
+                    " a real scalar value as input parameter"];
+              error (msg);
+            endif
           case 5
             outlier_tags = varargin{indopt};
             ## Check for string input: "on" or "off"
             if strcmpi (outlier_tags, "on")
               outlier_tags = 1;
-            endif
-            if strcmpi (outlier_tags, "off")
+            elseif strcmpi (outlier_tags, "off")
               outlier_tags = 0;
+            else
+              msg = ["Boxplot: 'OutlierTags' input argument takes only", ...
+                    " 'on' or 'off' as input parameter"];
+              error (msg);
             endif
           case 6
             sample_IDs = varargin{indopt};
+            ## Check for cell array
+            if (! iscell (sample_IDs))
+              msg = ["Boxplot: 'Labels' input argument takes only", ...
+                    " a cell array as input parameter"];
+              error (msg);
+            endif
             outlier_tags = 1;
           case 7
             box_width = varargin{indopt};
@@ -266,24 +291,49 @@
             ## if mis-spelled)
             if strcmpi (box_width, "fixed")
               box_width = "fixed";
-            else
+            elseif strcmpi (box_width, "proportional")
               box_width = "proportional";
+            else
+              msg = ["Boxplot: 'BoxWidth' input argument takes only", ...
+                    " 'fixed' or 'proportional' as input parameter"];
+              error (msg);
             endif
           case 8
             widths = varargin{indopt};
+            ## Check for scalar value
+            if (! isscalar (widths))
+              msg = ["Boxplot: 'Widths' input argument takes only", ...
+                    " a real scalar value as input parameter"];
+              error (msg);
+            endif
           case 9
             box_style = varargin{indopt};
             ## Check for string input: "outline" or "filled"
             if strcmpi (box_style, "outline")
               box_style = 0;
-            endif
-            if strcmpi (box_style, "filled")
+            elseif strcmpi (box_style, "filled")
               box_style = 1;
+            else
+              msg = ["Boxplot: 'BoxStyle' input argument takes only", ...
+                    " 'outline' or 'filled' as input parameter"];
+              error (msg);
             endif
           case 10
             positions = varargin{indopt};
+            ## Check for numeric vector
+            if (! isvector (positions) || ! isnumeric (positions))
+              msg = ["Boxplot: 'Positions' input argument takes only", ...
+                    " a numeric vector as input parameter"];
+              error (msg);
+            endif
           case 11
             labels = varargin{indopt};
+            ## Check for cell array
+            if (! iscell (labels))
+              msg = ["Boxplot: 'Labels' input argument takes only", ...
+                    " a cell array as input parameter"];
+              error (msg);
+            endif
           otherwise
             ## Take two args and append them to plot_opts
             plot_opts(1, end+1:end+2) = {dummy, varargin{indopt}};
@@ -334,10 +384,18 @@
     elseif (! isempty (sample_IDs) && length (sample_IDs) != nc)
       error ("Boxplot: Sample_IDs must match the data");
     endif
+    ## Create labels according to number of datasets as ordered in data
+    ## in case they are not provided by the user as optional argument
+    if (isempty (labels))
+      for i = 1:nc
+        column_label = num2str (groups(i));
+        labels(i) = {column_label};
+      endfor
+    endif
   else
     if (! isvector (data))
       error ("Boxplot: with the formalism (data, group), both must be vectors");
-    end
+    endif
     ## Check if sample IDs exist that they have same size with data
     if (! isempty (sample_IDs))
       if (length (sample_IDs) != 1 || length (sample_IDs{1}) != length (data))
@@ -346,22 +404,70 @@
       nc = unique (groups);
       dummy_data = cell (1, length (nc));
       dummy_sIDs = cell (1, length (nc));
-      for ind_c = (1:length (nc))
-        dummy_data(ind_c) = data(groups == nc(ind_c));
-        dummy_sIDs(ind_c) = {sample_IDs{1}(groups == nc(ind_c))};
-      endfor
+      ## Check if groups are parsed as a numeric vector
+      if isnumeric (groups)
+        for ind_c = (1:length (nc))
+          dummy_data(ind_c) = data(groups == nc(ind_c));
+          dummy_sIDs(ind_c) = {sample_IDs{1}(groups == nc(ind_c))};
+        endfor
+        ## Create labels according to unique numeric groups in case
+        ## they are not provided by the user as optional argument
+        if (isempty (labels))
+          for i = 1:nc
+            column_label = num2str (groups(i));
+            labels(i) = {column_label};
+          endfor
+        endif
+      ## Check if groups are parsed as a cell string vector
+      elseif iscellstr (groups)
+        for ind_c = (1:length (nc))
+          dummy_data(ind_c) = data(ismember (group, nc(ind_c)));
+          dummy_sIDs(ind_c) = {sample_IDs{1}(ismember (group, nc(ind_c)))};
+        endfor
+        ## Create labels according to unique cell string groups in case
+        ## they are not provided by the user as optional argument
+        if (isempty (labels))
+          labels = nc;
+        endif
+      else
+        error ("Boxplot: group argument must be numeric or cell string vector");
+      endif
       data = dummy_data;
-      groups = nc(:).';
-      nc = length (nc);
       sample_IDs = dummy_sIDs;
+      nc = length (nc);
+      groups = (1:nc);
     else
       nc = unique (groups);
-      dummy = cell (1, length (nc));
-      for indopt = (1:length (nc))
-        dummy(indopt) = data(groups == nc(indopt));
-      endfor
-      data = dummy; groups = nc(:).';
+      dummy_data = cell (1, length (nc));
+      ## Check if groups are parsed as a numeric vector
+      if isnumeric (groups)
+        for ind_c = (1:length (nc))
+          dummy_data(ind_c) = data(groups == nc(ind_c));
+        endfor
+        ## Create labels according to unique numeric groups in case
+        ## they are not provided by the user as optional argument
+        if (isempty (labels))
+          for i = 1:nc
+            column_label = num2str (groups(i));
+            labels(i) = {column_label};
+          endfor
+        endif
+      ## Check if groups are parsed as a cell string vector  
+      elseif iscellstr (groups)
+        for ind_c = (1:length (nc))
+          dummy_data(ind_c) = data(ismember (group, nc(ind_c)));
+        endfor
+        ## Create labels according to unique cell string groups in case
+        ## they are not provided by the user as optional argument
+        if (isempty (labels))
+          labels = nc;
+        endif
+      else
+        error ("Boxplot: group argument must be numeric vector or cell string");
+      endif
+      data = dummy_data;
       nc = length (nc);
+      groups = (1:nc);
     endif
   endif
 
@@ -372,14 +478,6 @@
   ##    6,7    lower and upper confidence intervals for median
   s = zeros (7, nc);
   box = zeros (1, nc);
-  ## Create labels according to number of datasets as ordered in parsed data
-  ## in case they are not provided by the user as optional argument
-  if (isempty (labels))
-    for i=1:nc
-      column_label = num2str (groups(i));
-      labels(i) = {column_label};
-    endfor
-  endif
   ## Arrange the boxes into desired positions (if requested, otherwise leave
   ## default 1:nc)
   if (! isempty (positions))
@@ -480,7 +578,7 @@
   ## Draw a box around the quartiles, with box width being fixed or proportional
   ## to the number of items in the box.
   if (strcmpi (box_width, "proportional"))
-    box = box .* (widths ./ max(box));
+    box = box .* (widths ./ max (box));
   else
     box = box .* (widths ./ box);
   endif
@@ -500,11 +598,17 @@
   whisker_y(:, [chop, chop + nc]) = [];
   median_x(:, chop) = [];
   median_y(:, chop) = [];
-
+  box(chop) = [];
+  
   ## Add caps to the remaining whiskers
   cap_x = whisker_x;
-  cap_x(1, :) -= repmat ((widths / 1.5), 1, 2);
-  cap_x(2, :) += repmat ((widths / 1.5), 1, 2);
+  if (strcmpi (box_width, "proportional"))
+    cap_x(1, :) -= repmat (((box .* (widths ./ max (box))) / 8), 1, 2);
+    cap_x(2, :) += repmat (((box .* (widths ./ max (box))) / 8), 1, 2);
+  else
+    cap_x(1, :) -= repmat ((widths / 8), 1, 2);
+    cap_x(2, :) += repmat ((widths / 8), 1, 2);
+  endif
   cap_y = whisker_y([1, 1], :);
 
   ## Calculate coordinates for outlier tags
@@ -559,7 +663,7 @@
       endif
     endif
   else
-   ## Define outlier_tags' horizontal alignment
+    ## Define outlier_tags' horizontal alignment
     outlier_tags_alignment = {"horizontalalignment", "left", "rotation", 90};
     if (isempty (plot_opts))
       h = plot (quartile_y, quartile_x, "b;;",
@@ -711,7 +815,6 @@
 %! groups = [(ones (10, 1)); (ones (25, 1) * 2); (ones (20, 1) * 3)];
 %! labels = {"Team A", "Team B", "Team C"};
 %! pos = [2, 1, 3];
-%! boxplot (data, groups, "Notch", "on", "Labels", labels, "Positions", pos, "OutlierTags", ...
-%!          "on", "BoxStyle", "filled");
+%! boxplot (data, groups, "Notch", "on", "Labels", labels, "Positions", pos, ...
+%!          "OutlierTags", "on", "BoxStyle", "filled");
 %! title ("Example of Group splitting with paired vectors");
-
