Set exact time limits in Matlab plot - matlab

When facing data in the time domain i seem to run into problems setting the exact limits on the x-axis with matlab.
Thus i plot my data and give the xlim in matlab time and set the limits:
minTT = datenum(2008,10,31,17,12,00);
maxTT = datenum(2008,10,31,17,19,00);
xlim = ([minTT maxTT]);
Then i use the date tick option to convert the matlab timing to "real clock time".
datetick('x',13,'keepticks');
The 'keep ticks' option is still better than none and i tried with both.
But what i get out is a plot that is going from: 17:12:28 to 17:17:58.
I have tried editing the ticks on my own as suggested from another post at StackOverflow like this:
ticks = get(gca, 'xtick')
newTicks = linspace(ticks(1), ticks(end), 8);
set(gca,'Xtick', newTicks)
but even that doesn't work out and gives me limits from 17:12:28 to 17:17:31.
Is there any way to really force matlab to use specific times on the axis whether or not the data doesn't exactly start there?

You can use the 'keeplimits' flag to datetick() - it does just what it says, much like 'keepticks'

Related

How do I get lines to stop extending beyond plot border? (Matlab)

I am writing a report for a class and am having some issues with the lines of an unstable plot going beyond the boundary of the graph and overlapping the title and xlabel. This is despite specifying a ylim from -2 to 2. Is there a good way to solve this issue?
Thanks!
plot(X,u(:,v0),X,u(:,v1),X,u(:,v2),X,u(:,v3),X,u(:,v4))
titlestr = sprintf('Velocity vs. Distance of %s function using %s: C=%g, imax=%g, dx=%gm, dt=%gsec',ICFType,SDType,C,imax,dx,dt);
ttl=title(titlestr);
ylabl=ylabel("u (m/s)");
xlabl=xlabel("x (m)");
ylim([-2 2])
lgnd=legend('t=0','t=1','t=2','t=3','t=4');
ttl.FontSize=18;
ylabl.FontSize=18;
xlabl.FontSize=18;
lgnd.FontSize=18;
EDIT: Minimum reproducible example
mgc=randi([-900*10^10,900*10^10], [1000,2]);
mgc=mgc*1000000;
plot(mgc(:,1),mgc(:,2))
ylim([-1,1])
This is odd. It really looks like a Bug... partly
The reason is probably that the angle of the lines are so narrow that MATLAB runs into rounding errors when calculating the points to draw for your limits very small limits given very large numbers. (You see that you don't run into this problem when you don't scale the matrix mgc.
mgc = randi([-900*10^10,900*10^10], [1000,2]);
plot(mgc(:,1),mgc(:,2))
ylim([-1,1])
but if you scale it further, you run into this problem...
mgc = randi([-900*10^10,900*10^10], [1000,2]);
plot(mgc(:,1)*1e6,mgc(:,2)*1e6)
ylim([-1,1])
While those numbers are nowhere near the maximum number a double can represent (type realmax in the command window to see that this is a number with 308 zeros!); limiting the plot to [-1,1] on one of the axes -- note that you obtain the same phenom on the x-axis -- let MATLAB run into precision problems.
First of all, you see that it plots much less lines than before (in my case)... although, I just said to zoom on the y-axis. The thing is, that MATLAB does not recalculate the lines for the section but it really zooms into it (I guess that this may cause resolution errors with regard to pixels?)
Well, lets have a look at the data (Pro-tip, you can get the data of a line from a MATLAB figure by calling this snippet
datObj = findobj(gcf,'-property','YData','-property','XData');
X = datObj.XData;
Y = datObj.YData;
xlm = get(gca,'XLim'); % get the current x-limits
) We see that it represents the original data set, which is not surprising as you can also zoom out again.
Note that his only occurs if you have such a chaotic, jagged line. If you sort it, it does not happen.
quick fix:
Now, what happens, if we calculate the exact points for this section?
m = diff(Y)./diff(X); % slope
n = Y(1:end-1)-m.*X(1:end-1); % offset
x = [(-1-n); (1-n)]./m;
y = ones(size(x))./[-1 1].';
% plot
plot([xMinus1;xPlus1],(ones(length(xMinus1),2).*[-1 1]).')
xlim(xlm); % limit to exact same scale as before
The different colors indicate that they are now individual lines and not a single wild chaos;)
It seems Max pretty much hit the nail on the head as it pertains to the reason for this error is occurring. Per Enrico's advice I went ahead and submitted a bug report. MathWorks responded saying they were unsure it was "unexpected behavior" and would look into it more shortly. They also did suggest a temporary workaround (which, in my case, may be permanent).
This workaround is to put
set(gca,'ClippingStyle','rectangle');
directly after the plotting line.
Below is a modified version of the minimum reproducible example with this modification.
mgc=randi([-900*10^10,900*10^10], [1000,2]);
mgc=mgc*1000000;
plot(mgc(:,1),mgc(:,2))
set(gca,'ClippingStyle','rectangle');
ylim([-1,1])

Converting from datenum back to actual time

Ok, so I've been messing with what should be a simple problem. I'm trying to plot an numerical array (double) vs. the associated time stamp (cell) with the format of the DD-MM-YY HH:MM:SS (for example: 13-Mar-15 07:23:10).
I'm able to plot a single set using datetime(time stamp). Due to the data set it outputs the nice HH:MM on the x-axis. Very nice.
Now in order to plot 2 sets of values on the same graph, I've found that Matlab doesn't like to use the date_time twice for the x-axis, so then I go to the infamous datenum function, which is able to plot both on the same graph. However, it's in the serial value of time and it jacks with my plot sizing (i.e. the x-axis doesn't autosize).
With what should be a simple problem has actually caused me days scouring the internet trying to reconvert it back to my beloved HH:MM after converting the "time stamp" into the serial time.
I don't think that a code sample or data set should be necessary for example purposes. (but can provide if needed)
I've tried to use the datetick function, but can't get seem to get it working.
The trick with datenum and datetick is to set limits and tick positions before you call datetick, then make sure it doesn't redo them.
So, after plotting your two sets of data against the datenums it would go something like:
step = 1/24; % for hourly - adjust to preference
ticks = datenum('mystarttime'):step:datenum('myendtime')
set(gca,'XTick',ticks)
datetick('x','HH:MM','keepticks','keeplimits')
I work a lot with time series and most often I have to plot my data versus time/date.
Since Matlab never gave me a definitively convenient solution, for many years now I work this way:
I defined once and for all a Matlab shortcut (in the matlab shortcut toolbar):
containing the following code:
xticktemp = get(gca,'Xtick') ;
ticklabel = {datestr(xticktemp(1)) datestr(xticktemp(2:end),15) } ;
set(gca,'Xticklabel',ticklabel)
clear xticktemp ticklabel
I convert all my times with datenum and use this format for working (or posix time when more convenient, but it's another story), calculating and plotting. When I need to now exactly the time of an event in a human readable format, I press the shortcut and I obtain something like:
Of course there are 2 major limitations with this method:
This does not control the steps of the ticks (it just replicate what Matlab initially set)
You have to re-click your shortcut to refresh the tick labels everytime you zoom or pan the figure
For these reasons, when I need to finalize figure for presentation to others I won't use this trick and define my ticks exactly like I want them, but when you are simply working away, this shortcut has saved me hours (may be even days?) of fiddling around over the last 10 years ....

Matlab - Dual category X-axis (Day and Hour)

For a plot, like the one attached, is there a way to introduce a dual category axis? A (poorly drawn) example can be seen attached also.
The report ideally should be auto generated, so setting static day names are not ideal..
EDIT: I should also mention it would be great to have the hours start again each day, 0-24,0-24,0-24 etc
Not sure about the days, but I have a suggestion for the hours. Imagine the following:
hours = 0:70;
data=rand(size(hours));
plot(hours,data,'*')
xlabel('Hours of day')
This gives the following plot:
Now you need to edit the labels used by the x-axis. Here's one way to do it (not necessarily the most elegant because you are dealing with cell arrays and have to convert things back and forth between string and numerical data type, but it seems to work, at least in Octave):
temp = get(gca,'xticklabel');
for k=1:length(temp)
temp{k} = num2str(mod(str2num(temp{k}),24));
end
set(gca,'xticklabel',temp)
Which gives the following graph:
Good luck with adding the days below that!! (maybe using the text command in a clever way)
Here is a very custom way using text annotations. You might need to add some minor changes to fit your data.
clc
clear
x = 1:80;
HourValues = repmat([0 10 20],1,3);
DaysString = {'Mon' 'Tue' 'Wed'};
NumDays = numel(DaysString);
plot(x,rand(1,80))
set(gca,'XTickLabel',HourValues) % Set xtick labels
xlimit = get(gca,'XLim'); % Get x and y- limits
ylimit = get(gca,'YLim');
% May need some adaptation to fit your data
for k = 1:NumDays
text((NumDays*k-2)*10,ylimit(1)-.06,DaysString{k},'FontSize',14,'HorizontalAlignment','Center')
end
text(xlimit(2)/2,ylimit(1)-0.1,'Hour of day','FontSize',16,'HorizontalAlignment','Center')
Giving something like this:

Ordinary Differential Equations in Matlab

Question -
I am working on some matlab code that solves 2 first degree differentials and 2 second order differentials. I am ok with dsolve() but when I want to plot I am currently using ezplot and it is not giving me what I want. I want to produce 1 window with four graphs. I know I would use subplot but I dont know how, an example would be nice. Also I dont know how to make my plots show the importiant area not just a large area. my code is below:
close all % close all figure windows that are open
clear all % clear all the variables currently stored in memory
clc % clear the commands in the command window
%%Problem 1%%%%%
a = (dsolve('Dv = -500*v+5000','v(0)=5'));
display (a)
b = (dsolve('Dx = -2000*x+100','x(0)=-.02'));
display (b)
%%Problem 2%%%%%
c = (dsolve('D2y+2000*Dy+26000000*y-520000000=0','Dy(0)=0','y(0)=5'));
display(c)
d = (dsolve('D2y+100*Dy+2500*y-520000000=0','Dy(0)=20','y(0)=0'));
display (d)
figure
ezplot(a);
axis([0,.01,4,10])
figure
ezplot(b);
axis([0,.01,0,10])
figure
ezplot(c);
axis([0,.01,4,10])
figure
ezplot(d);
axis([0,.01,4,10])
I didn't know until now, but it seems that ezplot only generates data points for "interesting part" of your plot. So if you specify the x-limit that ezplot does not use, you don't see anything. What you need to do is to specify the x-limits in its second argument of ezplot. Then, you can create subplots with standard suplot function, get axis handle, and specify the axis. The plotting part of your code should be like this.
figure
h1=subplot(2,2,1);
ezplot(a, [0,0.01]);
axis(h1,[0,0.01,4,10])
h2=subplot(2,2,2);
ezplot(b, [0,0.01]);
axis(h2,[0,.01,0,10])
h3=subplot(2,2,3);
ezplot(c, [0,0.01]);
axis(h3,[0,.01,4,10])
h4=subplot(2,2,4);
ezplot(d, [0,0.01]);
axis(h4,[0,.01,4,10])

MATLAB query about for loop, reading in data and plotting

I am a complete novice at using matlab and am trying to work out if there is a way of optimising my code. Essentially I have data from model outputs and I need to plot them using matlab. In addition I have reference data (with 95% confidence intervals) which I plot on the same graph to get a visual idea on how close the model outputs and reference data is.
In terms of the model outputs I have several thousand files (number sequentially) which I open in a loop and plot. The problem/question I have is whether I can preprocess the data and then plot later - to save time. The issue I seem to be having when I try this is that I have a legend which either does not appear or is inaccurate.
My code (apolgies if it not elegant):
fn= xlsread(['tbobserved' '.xls']);
time= fn(:,1);
totalreference=fn(:,4);
totalreferencelowerci=fn(:,6);
totalreferenceupperci=fn(:,7);
figure
plot(time,totalrefrence,'-', time, totalreferencelowerci,'--', time, totalreferenceupperci,'--');
xlabel('Year');
ylabel('Reference incidence per 100,000 population');
title ('Total');
clickableLegend('Observed reference data', 'Totalreferencelowerci', 'Totalreferenceupperci','Location','BestOutside');
xlim([1910 1970]);
hold on
start_sim=10000;
end_sim=10005;
h = zeros (1,1000);
for i=start_sim:end_sim %is there any way of doing this earlier to save time?
a=int2str(i);
incidenceFile =strcat('result_', 'Sim', '_', a, 'I_byCal_total.xls');
est_tot=importdata(incidenceFile, '\t', 1);
cal_tot=est_tot.data;
magnitude=1;
t1=cal_tot(:,1)+1750;
totalmodel=cal_tot(:,3)+cal_tot(:,5);
h(a)=plot(t1,totalmodel);
xlim([1910 1970]);
ylim([0 500]);
hold all
clickableLegend(h(a),a,'Location','BestOutside')
end
Essentially I was hoping to have a way of reading in the data and then plot later - ie. optimise the code.
I hope you might be able to help.
Thanks.
mp
Regarding your issue concerning
I have a legend which either does not
appear or is inaccurate.
have a look at the following extracts from your code.
...
h = zeros (1,1000);
...
a=int2str(i);
...
h(a)=plot(t1,totalmodel);
...
You are using a character array as index. Instead of h(a) you should use h(i). MATLAB seems to cast the character array a to double as shown in the following example with a = 10;.
>> double(int2str(10))
ans = 49 48
Instead of h(10) the plot handle will be assigned to h([49 48]) which is not your intention.