How do I change the number of axis ticks while using 'datetick' (Object Oriented Programming, Creating an Application)

I am working on a project that takes the data for the number of COVID-19 Deaths and Cases in all countries/state/regions of the globe, and puts them into a application that shows this information graphically (complete with widgets and all) using MATLAB's App Designer, an acompanying data file, and a classdef .m file to prepare the data. The project and all the widgets work perfectly, however the way I have set up the x-axis lable seems to be in error. The final plot should look something like this:
with monthly x-axis tick lables in the form of (mm/dd). However, when I run my appliation, my output is this (data has changed slightly, so only focus on number of x-axis ticks):
with a quarterly tick seperation instead of a monthly one. I dont want to send the entire code, so I know you wont be able to run the application, but I know that all the code leading to this point is correct I am just strugling with finding a way to alter the number of axis ticks. Below is what I beleive is the necessary code for the situation, if more is needed, please dont heasitate to ask for it and I can send through a less public medium.
This code is at the end of my 'Plot Data' method section (after all of the data has been plotted
xlim(app.UIAxes,[app.xDATA(1) app.xDATA(end)]);
xlim(app.UIAxes,'manual');
app.UIAxes.YLimMode = 'auto';
app.UIAxes.YTickMode = 'auto';
datetick(app.UIAxes,'x','mm/dd',"keeplimits");
grid(app.UIAxes,'on');
set(app.UIAxes,'YTickLabel',num2str(get(app.UIAxes,'YTick').','%.f'));
hold(app.UIAxes,'off');
This is the code in my start-up function that takes the data from my classdef and initiates it.
globalDATA = DATA('Global'); % DATA import
app.CountryListBox.Items = (unique(globalDATA.COUNTRY,'stable')); % Settings for items listed in the Country list box
app.StateorRegionListBox.Items = (unique(globalDATA.STATE,'stable')); % Settings for items listed in the State/Region List box
app.cases_deaths_MATRIX = globalDATA.Cases_Deaths_GLOBAL;
app.casesVEC = app.cases_deaths_MATRIX(1,:);
app.deathsVEC = app.cases_deaths_MATRIX(2,:);
startDATE = datenum(globalDATA.DATE{1});
endDATE = datenum(globalDATA.DATE{end});
app.xDATA = linspace(startDATE,endDATE,length(globalDATA.DATE));
app.averagingWINDOW = app.AveragedofDaysSlider.Value;
app.StateorRegionListBoxValueChanged;
I appreciate any help that is able to be offered, thank you in advance for your time and assistance.

 Accepted Answer

Cool looking app!
One strategy is to set the ticks and then use the keepticks flag:
xdata=datenum(2020,1:4,1);
ydata=rand(1,4);
ax=nexttile;plot(xdata,ydata)
nticks = 4;
xticks(ax,linspace(xdata(1),xdata(end),nticks))
datetick(ax,'x','mm/dd',"keepticks");
ax=nexttile;plot(xdata,ydata)
nticks = 8;
xticks(ax,linspace(xdata(1),xdata(end),nticks))
datetick(ax,'x','mm/dd',"keepticks");
ax=nexttile;plot(xdata,ydata)
nticks = 12;
xticks(ax,linspace(xdata(1),xdata(end),nticks))
datetick(ax,'x','mm/dd',"keepticks");
But if you can use datetime instead of datenum, you might find that options are more flexible (with xtickformat) and you get better ticks:
figure
xdata=datetime(xdata,'ConvertFrom','datenum');
ax=nexttile;plot(xdata,ydata)
nticks = 4;
xticks(ax,linspace(xdata(1),xdata(end),nticks))
ax=nexttile;plot(xdata,ydata)
nticks = 8;
xticks(ax,linspace(xdata(1),xdata(end),nticks))
ax=nexttile;plot(xdata,ydata)
nticks = 12;
xticks(ax,linspace(xdata(1),xdata(end),nticks))
ax=nexttile;plot(xdata,ydata)
nticks = 12;
xticks(ax,linspace(xdata(1),xdata(end),nticks))
xtickformat('MMM dd')

11 Comments

Thank you very much for this answer! I am running into issues with changing the linspace to the number of ticks. Using your suggested methods, using the number of ticks in the linspace function causes the total number of points to be altered. the data file gives data for each day, and I need to keep the data points as daily to show the information correctly. is there a way to change the number of visible ticks without changing the data plotted?
I think you're saying that my approach set the number of ticks, but you want to make sure that you have one tick per day?
xdata=datenum(2020,1,1:20);
ydata=rand(1,20);
ax=nexttile;plot(xdata,ydata)
tickspacing = 1;% because datenum is fractions of a day, you can just increment by a value of 1 for days
xticks(ax,xdata(1):tickspacing:xdata(end))
datetick(ax,'x','mm/dd',"keepticks");
xdata=datetime(xdata,'ConvertFrom','datenum');
ax=nexttile;plot(xdata,ydata)
tickspacing = days(1); % For datetimes, you can be much more explicit about what the spacing is
xticks(ax,xdata(1):tickspacing:xdata(end))
My apologies for not being more clear. The data that is being plotted is about 330 days long. Each of those days needs to have an individual data point, which my origional code does, however what I am trying to accomplish is to have the visual tick marks showe in monthly incriments. The first of the origional figures I sent does this. the tick marks are labled in a monthly succession, with the actual data being plotted as daily. This needs to happen because in the application built there is a slider bar widget that can change the average number of days plotted (1-15 days). Due to this, I need to keep the total number of daily data points, but I would like to change the number of visual ticks on the plot to be monthly, instead of quarterly as shown in the second figure in my origional post. The data points are daily, but I would like the tick marks to be monthly. Does this make more sense?
To be clear, when you set the ticks manually, they are totally unrelated to the number of data points in your chart. I picked some random data just to show something on the plot, but it was irrelevant.
If you want monthly ticks, set the increment to months. The increment you use for the ticks can be anything you want: days, months, 14 minutes, etc.
Again, all of this is much easier with datetime, because it frees you from concerns about the number of days in each month or leap years or daylight savings time etc.:
datetime(2010,1,1):calmonths(1):datetime(2010,10,1)
ans = 1×10 datetime array
01-Jan-2010 01-Feb-2010 01-Mar-2010 01-Apr-2010 01-May-2010 01-Jun-2010 01-Jul-2010 01-Aug-2010 01-Sep-2010 01-Oct-2010
Of course, you'll need to decide if you want to place them at the first of each month or the middle or the last. Ticks that are not included in the axes limits are ignored, so it's pretty easy to use the limits or the data itself to decide where the ticks would go:
xdata=datetime(2020,9,13):datetime(2021,5,15); % note that the data are not in monthly increments
ydata=rand(size(xdata));
ax=axes;
plot(ax, xdata, ydata)
lim = ax.XLim;
% Lim will probably be values at the first of the month, but we should make
% sure
first_tick=datetime(lim(1).Year, lim(1).Month, 1);
last_tick=datetime(lim(2).Year, lim(1).Month, 1);
xticks(ax, first_tick:calmonths(1):last_tick)
This makes more sense, and thank you for the clarification! I have attempted to alter your given code using my data, and I have gotten it to work except for 2 slight issues. I end up with two seperate sets of overlapping xtick marks. For the data that I am given, the first date is 01/22/20 (making the first month be Jan 2020) and the end date is 01/30/2021 (making the desired last month be Jan 2021). For the lim=ax.XLim, the end limit becomes April 2021, and the the first set of tick marks is, again, a quarterly split. Once it runs through the section of code that ensures the values be the first of each month, I get a second set of overlapping tixk marks that are monthly, and stop at the correct Jan 2021. Any thoughts on how to deal with this issue? Code and figure shown below:
load('covid_data.mat')
date = covid_data(1,3:end);
startdate = date{1};
enddate = date{end};
xdata = datetime(startdate):datetime(enddate);
% xdata=datetime(2020,9,13):datetime(2021,5,15); % note that the data are not in monthly increments
ydata=rand(size(xdata));
ax=axes;
plot(ax, xdata, ydata)
lim = ax.XLim;
% Lim will probably be values at the first of the month, but we should make
% sure
first_tick=datetime(lim(1).Year, lim(1).Month, 1);
last_tick=datetime(lim(2).Year, lim(1).Month, 1);
xticks(ax, first_tick:calmonths(1):last_tick)
Sorry I should have made that more clear! When you call axes it creates a new one even if you already have one. In your app, you won't call axes, and instead of ax you'll just have app.UIAxes (I couldn't make an example app when running code in the MATLAB answers so I used axes as a proxy).
My guess is you're seeing two axes in your test case because you already had one up before you started plotting. You could do a clf before the call to axes to test it out or just try in your app with app.UIAxes
So when I implimented this into the code of my application, your guess was correct and I only have one set of axis ticks! However, instead of starting on the first of the month, it starts on the 20th of the month (20th of february to be exact, rather than january) instead of the first. Also, then I look at the "Deaths cases" the uniformity of the plot breaks and I get this random extra break at the end limit (shown below)
in this image, for some reason it does start on January 20th, but for the other two plots (cases and both) the first tick is Feb 20.
@Giacomo Guida - if you look above you'll see the code for starting them at the beginning of the month. If you want more ticks at the right side, just add more ticks to the vector that you set ticks with. If you want different axes limits just set the XLim property.
you know how to set the ticks to whatever you want
you know how to set the limits to whatever you want
you just have to tell MATLAB what you want.
Thank you very much for all of your help. I will finish from here. You have been extremly helpful.
I was able to successfuly fix the issue. Thank you very much @Dave B! Your assistance was impecable!
@Giacomo Guida - I'm so glad, hope to see your finished app in file exchange!

Sign in to comment.

More Answers (0)

Categories

Find more on MATLAB in Help Center and File Exchange

Asked:

on 4 Nov 2021

Commented:

on 18 Nov 2021

Community Treasure Hunt

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

Start Hunting!