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

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:

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])

For a time series plot, how to set the x axis to calendar time without weekends

I want to plot a time series (pricing data for a forex future contract). Time is given in yyyymmdd HH:MM:SS string format. Price is in double.
The time series spans over a week. However, the timestamp for the data points are always on weekdays. Is there a built-in method to display the appropriate x-axis labels and scaling but without weekends?
I can currently display the time series by converting the timestamp in string to datenum and arrange x-axis automatically with datetick. But I would like to know how to exclude weekends on the x-axis. Or exclude any extended interval of time from the grid where there is no data.
For a visual, please see example at the end. If one can exclude the large empty space due to including the weekend (Apr 10 was a US holiday), the plot can become more legible by focusing on where there is actually data.
The data points themselves are exclusive to weekdays. But between plot and datetick, grid points on weekends are created whenever the time series spans over a week. datetick is helpful because it detects the appropriate scaling for x-axis grid and applies the corresponding grid labels all in one go. If you see datetick as a part of the solution, is there a way to remove particular grid points after using datetick?
Example:
T=[7.378903958333334e+05;7.378967076388889e+05]; % converted from "20200409 09:30:00" and "20200415 16:59:00"
C=[0.7166;0.7090];
grid on
plot(T,C,'.')
xlim([min(T),max(T)])
datetick
If you are interested in tinkering with the same plot, please retrieve the data by c&p into Matlab cmd from pastebin. The same lines above will produce the following plot.
There is no such features. Probably, because this essentially makes the x-axis discontinuous, which is (usually) not desirable.
You will have to create a synthetic x-axis to achieve this by sorting out the the weekends (or rather the not business days).
This is the standard plot (the business days are blue)
% create data
T = 7.3789e+05+(0:5:100)/10.';
C = rand(size(T));
% is a busniess day?
lg = isbusday(T);
% standard date plot
plot(T(lg),C(lg),'o', T(~lg),C(~lg),'o')
datetick('x','ddd')
Now let us get rid of the other days and set the xTickLabel manually:
T = T(lg);
C = C(lg);
% plot without time-axis
plot(C,'o')
% --- get ticks
xTick = get(gca,'XTick');
% get which ticks are integers
lg = isreal(xTick) & rem(xTick,1)==0;
XTick_new = xTick(lg);
% --- create new tick label
xTickLbl_new_num = NaN(size(XTick_new));
% make sure they are within the limits
lg = XTick_new >= 1 & XTick_new <= length(T);
xTickLbl_new_num(lg) = T(XTick_new(lg));
% convert to string (arbitrary format)
xTickLbl_new_str = strings(size(xTickLbl_new_num));
xTickLbl_new_str(lg) = datestr(xTickLbl_new_num(lg),'ddd');
% set new label
set(gca,'XTick',XTick_new,'XTickLabel',xTickLbl_new_str);
Have a look on the results: left the standard version and on the right the version with manually set tick-labels.

Interactive curve fitting with MATLAB using custom GUI?

I find examples the best way to demonstrate my question. I generate some data, add some random noise, and fit it to get back my chosen "generator" value...
x = linspace(0.01,1,50);
value = 3.82;
y = exp(-value.*x);
y = awgn(y,30);
options = optimset('MaxFunEvals',1000,'MaxIter',1000,'TolFun',1e-10,'Display','off');
model = #(p,x) exp(-p(1).*x);
startingVals = [5];
lb = [1];
ub = [10];
[fittedValue] = lsqcurvefit(model,startingVals,x,y,lb,ub,options)
fittedGraph = exp(-fittedValue.*x);
plot(x,y,'o');
hold on
plot(x,fittedGraph,'r-');
In this new example, I have generated the same data but this time added much more noise to the first 15 points. Because it is random sometimes it works out okay, but after a few runs I get a good example that illustrates my problem. Same code, except for these lines added under value = 3.82
y = exp(-value.*x);
y(1:15) = awgn(y(1:15),5);
y(15:end) = awgn(y(15:end),30);
As you can see, it has clearly not given a good fit to where the data seems reliable, because it is fitting from points 1-50. What I want to do is say, okay MATLAB, I can see we have some noisy data but it seems decent over a range, only fit your exponential from points 15 to the end. I could go back to my code and update it to do this, but I will be batch fitting graphs like this where each one will have different ranges of 'good' data.
So what I am after is a GUI callback mechanisms that allows me to click on two circles from the data and have them change color or something, which indicates the lsqcurvefit will only fit over that range. Internally all it has to change is inside the lsqcurvefit call e.g.
x(16:end),y(16:end)
But the range should update depending on the starting and ending circles I have clicked.
I hope my question is clear. Thanks.
You could use ginput to select the two points for your min and max in the plot.
[x,y]=ginput(2);
%this returns you the x and y coordinates of two points clicked after each other
%the min point is assumed to be clicked first
min=[x(1) y(1)];
max=[x(2) y(2)];
then you could fit your curve with the coordinates for min and max I guess.
You could also switch between a rightclick for the min and a leftclick for the max etc.
Hope this helps you.

Set exact time limits in Matlab plot

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'

Organize Points and Draw the Line

In Matlab, I've got several points in the 3D space. Those points represent a rope and I would like to draw a line linking all those points. Here is my problem: How the oraganization of those points should be done to have a "simple" and "more or less straigth line". In other words I would like to draw a line linking all the points from the first to the last one but not "going back". Maybe with a simple image i can explain better my problem:
This is what the code should do:
This is what the code shouldn't do:
Some idea of how can I achieve the intended result? How can I organize the points? I'm working with Matlab but if you know any paper where I can read how to do this it will be fine. Thank you.
If you just don't want to go back in the upper direction, the solution that #Dan suggested should be fine. Sort them in that direction before connecting.
However, if that is not a requirement but you are actually looking for a solution without ugly crossings that is as short as possible, you may want to look into solutions for the travelling salesman problem.
If you define the distance between 1 and 9 to be zero, the solution you find to the travelling salesman problem should give you a nice and short rope.
If you want to go for the TSP approach but get a little lost, just try a 'furthest insertion' start position or '2 opt' improvements as that is likely going to give a big improvement over random path selection already with minimal effort.
OK. I've found the right answer (by Gunther Struyf): Sort Coordinates Points in Matlab
In order to visualize the final result just add to Gunther's code the following lines:
row=data(:,1);
col=data(:,2);
figure
scatter(row,col,'r');
hold on
line(row(result),col(result));
The following code shows the same algorithm but for a 3D implementation:
close all;
data = [2,2, 2; 2,3,2 ; 1,2,3 ; 1,3,3 ; 2,1,3 ; 1,1,3 ; 3,2,4 ; 3,3,4 ; 3,1,5; 5,4,6; 7,3,8; 8,9,7; 9,4,7; 6,2,5; 5,8,6; 9,3,8;6,9,2];
row=data(:,1);
col=data(:,2);
frame=data(:,3);
dist = pdist2(data,data);
N = size(data,1);
result = NaN(1,N);
result(1) = 1; % first point is first row in data matrix
for ii=2:N
dist(:,result(ii-1)) = Inf;
[~, closest_idx] = min(dist(result(ii-1),:));
result(ii) = closest_idx;
end
figure
%Ploting the points directly (without sorting)
subplot(2,1,1);
scatter3(row,col,frame,'r');
hold on;
line(row,col,frame);
%Ploting after sorting
subplot(2,1,2);
scatter3(row,col,frame,'r');
hold on
line(row(result),col(result),frame(result));