How to make a Heatmap Calendar plot

I'm trying to make a Heatmap Calendar for MP2.5 like the one made for R's "Open Air Project", using date and concentration (data attached)
It should look like this:
I know there's a calendar function, but I have no idea of how to plot it. I would really appreciate any help you could give me. Thanks a lot!

1 Comment

Should be possible but probably requires a lot of code unless you can find something on FEX. you may be able to overlay a heatmap on the calender function.

Sign in to comment.

 Accepted Answer

jonas
jonas on 28 Aug 2018
Edited: jonas on 29 Aug 2018
I made something similar to what you were asking for. It's just something I put together for fun, so there are definitely some shortcomings and lots of shortcuts. However, if you don't find anything better on FEX, then you can develop this further. You have to play around with the figure size, markersize and fontsize to make it look good. If you change one you have to adjust the others.
% Load data
data=load('cal.mat')
Date=data.Date;
MP25=data.MP25;
MP25(isnan(MP25))=-100;
% Group data
x=weekday(Date);
y=week(Date);
[G,m]=findgroups(month(Date));
% Figure starts here!
figure;hold on
for ii=1:12
% Extract subgroup of monthly data and name of month
xm=x(G==ii);
ym=y(G==ii);
zm=MP25(G==ii);
Mname = unique(month(Date(G==ii),'name'));
% Initiate subplot ii
ax(ii) = subplot(3,4,ii);hold on
% Write out day of month on grid
str=cellstr(num2str(day(Date(G==ii),'dayofmonth')));
text(xm,ym,ones(size(xm)).*100,str,'fontsize',6,...
'fontweight','bold',...
'verticalalignment','middle',...
'horizontalalignment','center')
zm(zm<0)=NaN;
% Plot color scale
H(ii) = scatter3(xm,ym,zm,150,zm,...
's','filled','markeredgecolor','none');
% Fix some properties that are annoying to change after plotting
title(Mname)
set(gca,'ylim',[min(ym)-.5 min(ym)+5.5],...
'ytick',[min(ym)-.5:max(ym)+.5])
grid on
end
% Add colorbar
colorbar('west','position',[0.93 0.30 0.03 0.4])
% Set up properties for all axes
set(ax,'clim',[0 90],...
'xcolor','k',...
'ycolor','k',...
'xtick',1.5:8,...
'box','on',...
'xticklabels',{'S ','M ','T ','W ','T ','F ','S '},...
'yticklabels',{},...
'ticklength',[0 0],...
'ydir','reverse',...
'xlim',[0.5 7.5],...
'view',[0 90],...
'layer','top');
% Adjust figure size to fit with the font/markersize
set(gcf,'color',[1 1 1],...
'position',[955 140 640 428])
colormap(flipud(autumn))

16 Comments

Note that the calender format is set to US-UK by default where the week starts on sunday (for no apparent reason). It can be changed to EU standard easily, but you need to change the xticklabels manually.
Wow!! you nailed it! I'm totally impressed! Thanks @jonas, I'm very grateful!
I was wandering if it's possible to set discrete colors in the colorbar, in order to make categories, like in this example:
pcolor(3.5*abs(peaks(1024)))
shading interp
colormap([0 1 0; 1 1 0; 1.0000 0.4000 0; 1 0 0; 0.8000 0 1.0000])
colorbar('Ticks',[3,8,14,20,25],...
'TickLabels',{'Good','Regular','Alert','Pre-Emercengy','Emergency'});
Categories should be: -Good (values between 0-50) -Regular (between 51-79) -Alert (between 80-109) -Pre-Emergency (between 110-169) -Emergency (170 and above)
I made some small updates to the code so it looks a bit more like the figure you attached.
Sure, you can always specify the number of segments you want in the colormap. In the example above I used this line
colormap(flipud(autumn))
By default, you get a rather continuous colormap, but you can also specify the number of colors. For example:
colormap(flipud(autumn(5)))
Gives you five distinct colors. Then you can move the colorbar ticks so that they match the color segments.
Note that flipud just changes the gradient of the colormap so that it goes from yellow to red instead of the default red to yellow
There is however a small problem with your specific case. Your colormap is not linear, i.e. the segments have different lengths. I will see if I can fix that.
Replace the previous code after the for-loop with this, and it should give you what you want.
I would try a different colormap with more distinct colors, perhaps 'jet'.
% Add colorbar
cb=colorbar('west','position',[0.85 0.30 0.02 0.4])
colors=flipud(jet(5));
cmap=[repmat(colors(1,:),5,1);repmat(colors(2,:),3,1);repmat(colors(3,:),3,1);repmat(colors(4,:),6,1);repmat(colors(5,:),3,1)]
colormap(cmap)
cb.Ticks=[50/2 50+30/2 80+30/2 110+60/2 180+20/2];
cb.TickLabels={'Good','Regular','Alert','Pre-Emergency','Emergency'};
% Set up properties for all axes
set(ax,'clim',[0 200],...
'xcolor',[.7 .7 .7],...
'ycolor',[.7 .7 .7],...
'xtick',1.5:8,...
'xticklabels',{'S ','M ','T ','W ','T ','F ','S '},...
'yticklabels',{},...
'ticklength',[0 0],...
'ydir','reverse',...
'xlim',[0.5 7.5],...
'view',[0 90],...
'layer','top',...
'box','on');
% Adjust figure size to fit with the font/markersize
set(gcf,'color',[1 1 1],...
'position',[955 140 640 428])
for i=1:12;
old_pos=ax(i).Position;
ax(i).Position=old_pos-[0.1 0 0 0];
end
For segments of the colorbar are not equal in length, as you may want. This is more difficult to achieve. If it's important, I would probably make a 'fake' colorbar with patch objects, and hide the real one.
I think it's more a inssue about how to set the intervals for each color of the colormap.
I've been trying to apply this example to a new dataset (cal2.mat): https://www.mathworks.com/matlabcentral/answers/275085-how-to-have-a-discrete-color-bar-for-scatter3
yet I'm having some problems...
% Load data
data=load('cal2.mat')
Date=data.Date;
MP25=data.MP25;
MP25(isnan(MP25))=-100;
% Group data
x=weekday(Date);
y=week(Date);
[G,m]=findgroups(month(Date));
% Figure starts here!
figure;hold on
for ii=1:12
% Extract subgroup of monthly data and name of month
xm=x(G==ii);
ym=y(G==ii);
zm=MP25(G==ii);
Mname = unique(month(Date(G==ii),'name'));
% Initiate subplot ii
ax(ii) = subplot(3,4,ii);hold on
% Write out day of month on grid
str=cellstr(num2str(day(Date(G==ii),'dayofmonth')));
text(xm,ym,ones(size(xm)).*100,str,'fontsize',6,...
'fontweight','bold',...
'verticalalignment','middle',...
'horizontalalignment','center')
zm(zm<0)=NaN;
% Plot color scale
H(ii) = scatter3(xm,ym,zm,150,zm,...
's','filled','markeredgecolor','none');
% Fix some properties that are annoying to change after plotting
title(Mname)
set(gca,'ylim',[min(ym)-.5 min(ym)+5.5],...
'ytick',[min(ym)-.5:max(ym)+.5])
grid on
end
% Add colorbar
colorbar('west','position',[0.93 0.30 0.03 0.4],...
'Ticks',[30,60,90,120,180],...
'TickLabels',{'Good','Regular','Alert','Pre- Emercengy','Emergency'})
% Set up properties for all axes
set(ax,'clim',[0 90],...
'xcolor','k',...
'ycolor','k',...
'xtick',1.5:8,...
'box','on',...
'xticklabels',{'M ','T ','W ','T ','F ','S
','S '},...
'yticklabels',{},...
'ticklength',[0 0],...
'ydir','reverse',...
'xlim',[0.5 7.5],...
'view',[0 90],...
'layer','top');
% Adjust figure size to fit with the font/markersize
set(gcf,'color',[1 1 1],...
'position',[955 140 640 428]);
%%colormap([0 1 0; 1 1 0; 1.0000 0.6000 0; 1 0 0; 0.8000 0 1.0000])
crange = caxis;
t = linspace(crange(1),crange(2),64);
cmap = zeros(64,3);
%Good
mask = t<=50;
cmap(mask,:) = repmat([0 1 0],[sum(mask), 1]);
%Regular
mask = t>=51 & t<=79;
cmap(mask,:) = repmat([1 1 0],[sum(mask), 1]);
%Alert
mask = t>=80 & t<=109;
cmap(mask,:) = repmat([1.0000 0.6000 0],[sum(mask), 1]);
%Pre-Emergency
mask = t>=110 & t<=169;
cmap(mask,:) = repmat([1 0 0],[sum(mask), 1]);
%Emergency
mask = t>=170;
cmap(mask,:) = repmat([0.8000 0 1.0000],[sum(mask), 1]);
colormap(cmap);
clear all
Other than the fact that some numbers seem to be missing, I don't see any major problem. You have to change the 'clim' of the axes (currently [0 90]) if you want to see the entire range. I will take a look.
Also, it seems you are still using US/UK week days, but you changed the labels. This makes the labels wrong.
EDIT: Okay, the fact that I some numbers are not being written is really weird. I can't wrap my head around it... There is no error, warning or anything. Now my old code is not working either....!?
Edit2: The code works for me if I put the following line after the scatterplot:
plot3(1,1,1000)
This additional plot prompts matlab to draw the text. Again, I cannot wrap my head around why this happens and I've tested the usual drawnow, refreshdata, pause... The text is there but invisible for some reason. I have checked the properties and everything looks normal. I have attached the m-file to this comment so that you can try it yourself.
You are right, 'clim',[0 210] gives the entire Colorbar.
I have no idea why some numbers dissapear from the calendar. The data have the same format, so I don't know why it's not displaying the date numbers.
Do you know why is not printing the entire colorbar labels? it's seems to be cutting the names
plot3(1,1,1000) work's for me aswell, but only for "green days" red day's, purple day's and some orange day's are still missing.
Could be something related with the values? maybe it's not plotting below and above some kind of limit
I think I figured out what the problem is. Remove the plot3 and add this after the scatter3 instead
set(gca,'zlim',[0 1000])
also change the z-value of the text so that it's a bit below the max z (1000) and above the maximum z-value of the data (around 200). For example:
t=text(xm,ym,ones(size(xm)).*250,str,'fontsize',6,...
'fontweight','bold',...
'verticalalignment','middle',...
'horizontalalignment','center')
It makes sense that it didn't work because the 'clippingstyle', is set to '3dbox', which means that everything outside of the z-axes is invisible. Now I'm just confused why it seemed to work before...
Hope this fix also works for you. There is still something fishy with this problem..
To answer your previous question. The entire labels were not shown because they were outside of the figure window. I fixed this previously. So if you use my m-file you should be alright.
For me, it seems perfect!! thank you so much @jonas for taking the time to help me!
jonas
jonas on 29 Aug 2018
Edited: jonas on 29 Aug 2018
Happy to help! Maybe I will improve this and make it a FEX function. It looks quite nice! Remember that you have to change the week format to EUROPEAN standard if you want your week in MTWTFSS format. The default one is UK/US!! You cannot just change the labels, because the weekdays will have the wrong days! Compare with your windows calender to make sure it's correct
Just a final thing. If you print the figure with export_fig from FileExchange, the font will look more smooth like the one I posted before. Yours is a bit fatter than what it looks like when you preview in MATLAB.
Thanks again @jonas!! Please let me know if you make it a FEX function, I think it could be very usefull for people working on Air Quality. About the week format it's perfect just the way it is right now. Could be possible to set the month names to 'local' somehow?
No problemo. What do you mean month name local?
This is where the month name is extracted and stored in a cell array:
Mname = unique(month(Date(G==ii),'name'));
you can change the 'name' to 'shortname' ( Jan,Feb ... ) or 'monthofyear' ( 1,2,...), but that's it. You can of course also remove this line and specify any strings manually:
Mname={'Janu','Febr'...,'Dece');
CdC
CdC on 26 Apr 2021
Edited: CdC on 26 Apr 2021
Note: the cal.mat file requested in the code is based on the datafile in the original question. Thanks for creating this lovely and helpful code.
Thanks very much for this wonderful code. Can you kindly suggest how do I fix it to plot the figure correctly. Kindly see attached whats its doing
What is the version/OS? Are there any changes you made to the code?
On one hand, I would suspect that it's a graphics driver issue. On the other hand, that doesn't explain why there are so many repeated dates at a very specifically wrong y-position.
I can't replicate that behavior in any version I can access.

Sign in to comment.

More Answers (0)

Categories

Find more on Creating, Deleting, and Querying Graphics Objects in Help Center and File Exchange

Products

Asked:

on 28 Aug 2018

Commented:

DGM
on 21 Oct 2024

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!