This duplicates the prior work with a table instead; the previous still is correct for the one vector case, but having the data as the struct allows one the flexibility to get ahold of the field names and then move the metadata out of the variable names and into the data where it belongs...
N=max(structfun(@numel,S));
S=structfun(@(v)[v;nan(N-numel(v),1)],S,'uni',0);
experiment=contains(fieldnames(S),'c_')+1;
experiment=cell2mat(arrayfun(@(e)repmat(e,N,1),experiment,'uni',0));
type=startsWith(fieldnames(S),'g2');
type=cell2mat(arrayfun(@(t)repmat(t,N,1),type,'uni',0));
type=categorical(type,unique(type),{'labile','stable'});
class=extractAfter(fieldnames(S),'_');
class=arrayfun(@(c)repmat(c,N,1),class,'uni',0);
class=categorical(cat(1,class{:}));
observation=cell2mat(cellfun(@(f)S.(f),fieldnames(S),'uni',0));
tData=table(experiment,type,class,observation);
head(tData)
experiment type class observation
__________ ______ ______ ___________
1 labile acidic 11.597
1 labile acidic 11.94
1 labile acidic 11.028
1 labile acidic 11.753
1 labile acidic 11.864
1 labile acidic 12.079
1 labile acidic 11.26
1 labile acidic 12.181
groupsummary(tData,{'experiment','type','class'},'all')
Removing metadata from variable names and converting to a table makes further analyses much simpler and also is easier to present the data...
As for the boxplots, within the vectors, the previous code would work just fine; with the above table, varfun with the grouping variables would work as well. For the prior result, then
for e=unique(tData.experiment).'
for c=categories(tData.class).'
boxplot(tData.observation(iy),tData.type(iy))
hAx.XAxis.TickLabelRotation=0;
looks about right with the same issue that the online platform does something funky with the first axes on each row.
Again, the above makes the previous presumption that you wanted all of them in one figure...
ADDENDUM
Nota Bene: the orientation of the vectors in the for...end loops; MATLAB iterates over the items in the list by column, so must ensure those are row vectors--hence the transpose.
ADDENDUM SECOND
"the previous still is correct for the one vector case,"
Nota Bene: To use the observation field as the vector, remember it is now augmented to full length so the indexing is over N elements, not the variable number used in prior examples...or pull the data from the struct without the augmentation to same length and the prior logic will work as given if compute the L length vector to coincide with actual data instead of making up something as I did in the example by using a random length...
ADDENDUM THIRD
Forcibly setting the XAxis.TickLabelRotation property back to 0 fixes the issue with the first axes on the two rows.