Ok... the first step is to build up a function that can detect the season in which a given date falls. Fortunately, I already developed one long time ago that can also handle seasons in the southern hemisphere (which are reversed).
The function doesn't implement any sanity check, because I was using it with already sanitized datasets, but it should not be hard for you to eventually implement some (unless you decide to sanitize your datasets before using it). It works in a vectorized way to maximize the performance of calculations within Matlab.
Here is it:
function season = GetSeason(date,southern_hemisphere)
if (nargin == 1)
southern_hemisphere = false;
end
[~,month,day] = datevec(date);
offset = month + (day / 100);
winter = (offset < 3.21) | (offset >= 12.22);
spring = ~winter & (offset < 6.21);
summer = ~winter & ~spring & (offset < 9.23);
autumn = ~winter & ~spring & ~summer;
offset(spring) = 0;
offset(summer) = 1;
offset(autumn) = 2;
offset(winter) = 3;
if (southern_hemisphere)
offset = offset + 2;
end
season = mod(offset,4) + 1;
end
Now, the first step, within your script, is to extract your observations from a dataset file. In order to create a fully working demo for you, I created an Excel dataset. But you could also use a CSV dataset with almost no changes in the code or other file formats handled by Matlab:
% detect the dataset columns format
opts = detectImportOptions('data.xlsx');
% impose a specific format for the dataset columns
opts = setvartype(opts,{'datetime' 'double' 'double' 'double'});
% extract data in a table variable
data = readtable('data.xlsx',opts);
% sanitize the table variable removing the rows with missing or invalid values
data = rmmissing(data);
% sort the table variable rows by date (default first rows, default ascending)
data = sortrows(data);
The second test consists in getting the corresponding season for the observation dates:
seasons = GetSeason(data.Date);
The third step, assuming we are performing all this process only for the first column of observations called Obs1:
spring_1 = data.Obs1(seasons == 1);
summer_1 = data.Obs1(seasons == 2);
autumn_1 = data.Obs1(seasons == 3);
winter_1 = data.Obs1(seasons == 4);
The fourth and final step consists in plotting one boxplot for each season in a single graph (the grouping variable groups must be passed as argument into the boxplot function in order to aknowledge the latter of how many boxes it has to draw and using which values):
groups = [
ones(size(spring_1));
2 * ones(size(summer_1));
3 * ones(size(autumn_1));
4 * ones(size(winter_1));
];
figure();
boxplot([spring_1; summer_1; autumn_1; winter_1],groups);
set(gca,'XTickLabel',{'Spring' 'Summer' 'Autumn' 'Winter'});
And here is the result:

UPDATE WITH FULL WORKING CODE FOR ALL OBSERVATIONS
opts = detectImportOptions('data.xlsx');
opts = setvartype(opts,{'datetime' 'double' 'double' 'double'});
data = readtable('data.xlsx',opts);
data = rmmissing(data);
data = sortrows(data);
seasons = GetSeason(data.Date);
spring_1 = data.Obs1(seasons == 1);
summer_1 = data.Obs1(seasons == 2);
autumn_1 = data.Obs1(seasons == 3);
winter_1 = data.Obs1(seasons == 4);
spring_2 = data.Obs2(seasons == 1);
summer_2 = data.Obs2(seasons == 2);
autumn_2 = data.Obs2(seasons == 3);
winter_2 = data.Obs2(seasons == 4);
spring_3 = data.Obs3(seasons == 1);
summer_3 = data.Obs3(seasons == 2);
autumn_3 = data.Obs3(seasons == 3);
winter_3 = data.Obs3(seasons == 4);
plot_data = [
spring_1;
summer_1;
autumn_1;
winter_1;
spring_2;
summer_2;
autumn_2;
winter_2;
spring_3;
summer_3;
autumn_3;
winter_3
];
plot_groups = [
(1 * ones(size(spring_1))) (1 * ones(size(spring_1)));
(1 * ones(size(summer_1))) (2 * ones(size(summer_1)));
(1 * ones(size(autumn_1))) (3 * ones(size(autumn_1)));
(1 * ones(size(winter_1))) (4 * ones(size(winter_1)));
(2 * ones(size(spring_2))) (5 * ones(size(spring_2)));
(2 * ones(size(summer_2))) (6 * ones(size(summer_2)));
(2 * ones(size(autumn_2))) (7 * ones(size(autumn_2)));
(2 * ones(size(winter_2))) (8 * ones(size(winter_2)));
(3 * ones(size(spring_3))) (9 * ones(size(spring_3)));
(3 * ones(size(summer_3))) (10 * ones(size(summer_3)));
(3 * ones(size(autumn_3))) (11 * ones(size(autumn_3)));
(3 * ones(size(winter_3))) (12 * ones(size(winter_3)))
];
labels_obs = {'' '' '' '' '' '' '' '' '' '' '' ''};
labels_season = repmat({'Spring' 'Summer' 'Autumn' 'Winter'},1,3);
figure('Units','normalized','Position',[0.05 0.1 0.9 0.8]);
boxplot(plot_data,plot_groups, ...
'BoxStyle','outline', ...
'FactorGap',[5 1], ...
'Labels',{labels_obs; labels_season}, ...
'Notch','on');
colors = repmat('wcyg',1,3);
h = findobj(gca,'Tag','Box');
for i = 1:numel(h)
patch(get(h(i),'XData'),get(h(i),'YData'),colors(i),'FaceAlpha',0.5);
end
h = findall(allchild(findall(gca,'Type','hggroup')),'Type','text','String','');
positions = cell2mat(get(h,'pos'));
positions_new = num2cell([mean(reshape(positions(:,1),4,[]))' positions(1:4:end,2:end)],2);
set(h(1:4:end),{'Position'},positions_new,{'String'},{'Observations 3'; 'Observations 2'; 'Observations 1'})
h = findall(allchild(findall(gca,'Type','hggroup')),'Type','text','String','');
delete(h);
Result:
