How to plot weeknumbers (integers) in a graph without leaving gaps for weeks that do not exist
7 views (last 30 days)
Show older comments
In my project it is customary to plot certain observed events as a function of week number, where the week number is registered in datafiles as integers using the syntax YYWW (YY = last two digits of the year, WW = week number); for example, week 3 for year 2023 is stored as 2303.
Reading this data from the source datafile and plotting it is easy. However, since every year has 52 weeks, there is obviously no data for week 53 to 00. This means that there will be large gaps in the plot. See attachment for an example.
Aside from the fact this simply does not look nice, it is also a problem when using the plot to perform a trend analysis and/or fitting the data.
As such I am looking for a way to exclude non-existing weeks from the graph (while keeping weeks that do exist but for which no data has been accumulated) so that the gaps disappear and I have a continuous plot, but which still shows the weeknumbers on the x-axis.
4 Comments
Stephen23
on 13 Jun 2023
Question: do you require the plots to also use exactly that format or is it acceptable for the plots to use e.g. Y:M:D (or any other format currently supported by DATETIME). Because the simplest general solution would be to convert those dates to DATETIME, and then simply plot with those. This has the benefit that it will scale, adjust the ticks, etc automatically with your data, i.e. the plotting would then be trivially easy.
But only you can tell us, if that would be acceptable.
Otherwise, if that exact format must be used, then there will be quite a bit of fiddling about with tickmarks...
Answers (2)
Les Beckham
on 13 Jun 2023
Maybe you can adapt this approach to your situation.
dt = datetime(2023,1,[1 8 15 22 4*22+3*[7 14 21]]) % example datetime values
y = rand(size(dt)); % example data
plot(dt, y, 'o'); % plot data against actual datetime -- results in a gap in the x axis
grid on
wk = week(dt); % find the week numbers
figure
plot(1:(numel(dt)), y, 'o'); % plot against index -- without a gap
grid on
xticklabels(string(100*(year(dt)-2000) + wk)); % label the x axis with the actual week in YYMM format
xlabel('week (YYWW)')
5 Comments
Stephen23
on 14 Jun 2023
"Seems odd that Jan-01 is considered week 52"
At first sight, yes.
But ISO 8601 makes it clear that week numbering years are not the same as calender years.
Different week definitions for different years... thus my question here:
Only the OP can advise, which definition their project uses.
Les Beckham
on 14 Jun 2023
"Do I understand correctly that this code excludes all the non-exisiting weeks, but also the weeks that exist but for which I have no data?"
It creates the plot such that there is no gap for weeks that have no data. In the example above it skips the missing weeks between 22-Jan-2023 and 19-Apr-2023 -- weeks 2304 through 2315 (in the modified example using 'iso-weekofyear').
Stephen23
on 15 Jun 2023
Edited: Stephen23
on 16 Jun 2023
This turns out to be surprisingly difficult without DATETIME et al supporting ISO 8601 weeknumbers.
The rules for ISO 8601 week numbers and the corresponding years are not a trivial thing to implement:
Here is an alternative approach using the CATEGORICAL class:
First lets create some random fake data (I selected these years to match some examples on that wiki page):
[Y0,M0] = meshgrid(80:81,1:52); % some dates 1980W01 - 1981W52
YW = double(compose("%02d%02d",Y0(:),M0(:))); % integers YYWW
YW([3:6,17:32]) = []; % remove some random dates, because data is never complete
YW = [7952;YW;8201] % add 1979W52 and 1982W01
M = rand(numel(YW),3); % random data to plot
Now we can convert to categorical and try some different plots.
The simplest approach only plots the dates that exist in your data. If your data contains all dates between the start and end dates or you are happy to miss the dates that your data does not contain, then this might be sufficient for your needs:
X0 = categorical(YW);
plot(X0,M)
Hmm, it appears that by default the axes show all categories. We could explicitly specify a subset of those ticks:
plot(X0,M)
axh = gca;
idx = mod(double(string(axh.XTick)),10)~=0;
axh.XTick(idx) = [];
However the spacing is not even due to the missing dates (except between YY50 and YY10, where a larger step is expected!)
So we need to filll in the gaps. We can do this by creating every category for every week between the end dates... the complication is that we have to consider the fact that ISO 8601 week-numbering years may have either 52 or 53 weeks in them. Hmmm, not so trivial either.
Here is one approach. We generate every Thursday for those years:
YY = 1900+fix(YW/100); % you need to handle centuries, pivot years, etc.
DW = dateshift(datetime(min(YY),1,1),'dayofweek','Thursday'):calweeks(1):datetime(max(YY),12,31);
Y2 = mod(year(DW),100);
W2 = week(DW,'iso-weekofyear');
Now we can generate all categories, with the correct number of weeks for each year:
C2 = compose("%02d%02d",Y2(:),W2(:))
Then add those categories to the existing data using SETCATS:
X2 = setcats(X0,C2);
plot(X2,M)
axh = gca;
idx = mod(double(string(axh.XTick)),10)~=0;
axh.XTick(idx) = [];
axh.XLim = X0([1,end]);
Done. The plot shows all data with chronologically correctly spaced weeks, and all expected tick marks.
Hopefully this inspires you to experiment with the CATEGORICAL class... and get something working for you.
0 Comments
See Also
Categories
Find more on Discrete Data Plots in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!