Plot n series on same plot not overlapping - matlab

I have a time series of prices, 2000 entries.
I have created 12 vectors where each one contains only the data for one month. They don't have the same length, vary about 20 values between 160 and 180 values.
So now I need to plot all these vectors in the same plot, in sequence of course, starting with January data, and a little space in between, and on the x-axis put the month names (which I have in an array ['jan' 'feb' etc]
For an example click on the link and scroll down to seasonal subseries plot
http://www.itl.nist.gov/div898/handbook/pmc/section4/pmc443.htm

To obtain a similar graph to the above, you can insert a row of NaNs after each month. Since, each month has a different number of rows, you cannot simply reshape, concatenate NaNs, and reshape back.
Suppose you have a timestamps in the first column and some data in the second column:
data = [(now-11:now+13)' rand(25,1)];
% Count in 'idx' when each year-month pair ends
[y,m] = datevec(data(:,1));
[~, idx] = unique([y,m],'rows','last');
% Preallocate expanded Out with NaN separations between each month
szData = size(data);
Out = NaN(szData(1) + numel(idx)-1,2);
% Reposition 'data' within 'Out'
pos = ones(szData(1),1);
pos(idx(1:end-1)+1) = 2;
Out(cumsum(pos),:) = data;
% Example plot
plot(Out(:,1),Out(:,2))
set(gca,'Xtick',data([1 11 12 25],1))
datetick('x','dd-mmm','keepticks')

Related

Creating plots with disconnected/discrete lines

Here is my code:
for p = 1:length(id_unique)
h=figure;
hold on
j = 1;
for i = 1:1:length(id)
if id_unique(p)==id(i)
h=plot([startdate(i),enddate(i)],[j,j],'ok-');
hold on
j = j + 1;
end
end
grid on
hold off
savefig([plotname,'.fig'])
print(plotname,'-djpeg','-r300')
close
end
% id: integer vector containing 50000 values
% id_unique: sorted unique values from vector 'id'
% startdate: datetime vector containing 50000 dates
% enddate: datetime vector containing 50000 dates
Each elements/values in 'id' vector implies an event where the startdate and the enddate of the event is available in the corresponding position in 'startdate' and 'enddate' vectors.
Thus an event id(1) has the start date in startdate(i) and end date in enddate(i).
The program takes a value from the 'id_unique' vector and for each matched value found in 'id', it draws a line in the plot, denoting the beginning and ending time of the event.
For example, suppose 55 is an id-value from vector 'id_unique' and we have this value 1000 times in id. So for 55 a plot is created depicting 1000 separate lines with a marker 'o' at the beginning of the event, a marker 'o' at the end of the event and a line connecting both markers.
Please look at the attached plot that is generated from this code block. If id_unique has 70 values, 70 such plots will be created from this code. In the image, many lines are too small because of small difference between start and end date, so the markers overlap each other and look like a single point.
Now the problem comes when for an id-value in 'id_unique', we have a lot of instances of it in the 'id' vector. When the program plots individual lines unto 100 it works quite fast, but after plotting 300 lines in the same figure the program gets slow. When the program plots 1000 lines in the same figure, it takes about 5-7 seconds for each line. So it takes many hours to generate a plot with many lines.
Is there a way to improve my code to make these plot generation faster.
You don't need a loop:
you can use something like:
a = 1:0.1:4*pi;
b = sin(a); %vector that represent the start of each line
c = sin(a)+1; %vector that represent the end of each line
plot([b;c],[a;a],'-ko','markersize',2)
Result:
The function plot need 2 arguments: x and y, but, and this is why the world is beautiful, the function plot can manage several lines. If x and y are matrices, matlab interpret each column as a new line.
Having thought about this more, I now would suggest doing the following - rather than creating multiple lines in a plot, which generates a lot of objects in the background, it is better to produce one line per plot, that is broken up into multiple segments. If startdate and enddate are vectors of 1xlength(id), then the following code will be much faster as it concatenates all of the lines together, with NaN values in between. This makes the plot break the line at that point. All very quick, without the need to mess about with xlim and ylim as I previously suggested...
Here's my code:
for p = 1:length(id_unique)
h=figure;
block=[startdate(id==id_unique(p));enddate(id==id_unique(p))];
x_vals=reshape([block;nan(1,size(block,2))],1,[]);
y_vals=reshape([(1:size(block,2));(1:size(block,2));nan(1,size(block,2))],1,[]);
plot(x_vals,y_vals,'ok-');
grid on
savefig([plotname,'.fig'])
print(plotname,'-djpeg','-r300')
close
end
I hope this works better for you. (Of course, if your startdate and enddate vectors are length(id) by 1, then you should transpose them using .' in the 3rd line above).
I'm not sure what the purpose of the separate plots is, but it would be possible to plot them all on the same graph if that were useful. The code would then be:
h=figure;
hold on;
for p = 1:length(id_unique)
block=[startdate(id==id_unique(p));enddate(id==id_unique(p))];
x_vals=reshape([block;nan(1,size(block,2))],1,[]);
y_vals=reshape([(1:size(block,2));(1:size(block,2));nan(1,size(block,2))],1,[]);
plot(x_vals,y_vals,'Marker','o');
grid on
end
savefig([plotname,'.fig'])
print(plotname,'-djpeg','-r300')
close
This lets Matlab set the line type and colour using its standard series. It also allows you to use the legend function to add labels. Of course, if the position within id were important, rather than using a sequence for y_vals, you could use the position information obtained from find, changing the 4th and 6th lines to be:
[~,index]=find(id==id_unique(p));
block=[startdate(index);enddate(index)];
y_vals=reshape(index;index;nan(1,size(block,2))],1,[]);
Then you see all of id on one plot, with the different values of id distinguished by colour and linetype. You can then use the legend function to generate a legend:
legend(num2str(id_unique.'));
(This assumes that id_unique is a row vector. If it is a column vector, remove the .').
Here is way to also plot 2 different markers:
% some random data:
N = 50;
id = randi(5,N,1);
startdate = sort(randi(100,N,1));
enddate = startdate+randi(10,N,1);
% plotting:
ax = plot([startdate(:).'; enddate(:).'],[1:N; 1:N],'-k',...
startdate,1:N,'k<',enddate,1:N,'k>')
The (:).' after the vectors is not really needed, it's just to make sure they will be given in the right direction to plot - 2 rows, startdate above enddate.
which gives (randomly):
If you want to divide the data to groups by id, and color them this way, you can do the following:
N = 30;
id = randi(5,N,1);
startdate = datetime(sort(736000+randi(100,N,1)),'ConvertFrom','datenum');
enddate = startdate+randi(20,N,1);
% plotting:
ax = plot([startdate(:).'; enddate(:).'],[1:N; 1:N],'-',...
startdate,1:N,'k<',enddate,1:N,'k>');
% coloring:
cmap = colormap('jet');
col = cmap(floor(linspace(1,64,numel(unique(id)))),:);
for k = 1:N
ax(k).Color = col(id(k),:);
ax(k).LineWidth = 2;
end
% set the legend:
c = 1;
leg_ent = zeros(numel(unique(id)),1);
for k = unique(id).'
leg_ent(c) = find(id==k,1,'first');
c = c+1;
end
legend(ax(leg_ent),num2str(unique(id)),'Location','SouthEast')
and you will get:

Linear fit of data in a cell array

I have a cell array of data. The array is a single column with something between 500 and 3000 elements. Each element consists of a matrix with four columns. The first column are the x-, the second the y-values, whereas the third, and fourth column are irrelevant.
What I would like to do know is to fit these x- and y-values to a linear function (y=a*x+b), but not all the values, only a fraction - e.g. the first 10, 20, or 50%, the rest should not be considered. I have problems to access the relevant data in the cell array properly and to find a way to fit only a fraction of the data. What makes the task for me even more challenging is that the number of x- and y-values is different for every element of the array.
Although the elements of the array all have the size 500x4, some of them only exhibit several x- and y-paris and almost only NaN at the end, some of them almost 500 and only several NaN.
% cell array of data...
% single column, 500 to 3000 elements..
mycell = cell(1000,1);
for k = 1 : 1000
% each element is a matrix with 4 columns.. 500x4
mycell{k} = rand(500,4);
end
% first 10% means 500/10 or 50 elements
% first 50% means 500/2 or 250 elements
% lets fit the first 10% (50 elements)
for k = 1 : 1000
% get elements 1:50, or 10%
% x is col 1
x = mycell{k}(1:50, 1);
% y is col 2
y = mycell{k}(1:50, 2);
% will leave it to you to figure out the fitting. see:
% http://www.mathworks.com/help/matlab/data_analysis/linear-regression.html
polyfit(x, y, 1);
end

Matlab bar3 plot

I have a problem with MATLAB bar3 plots: Here is what I have:
m x n Array Values containing values of a measurement.
Another m x n Array Angles Represents the angle at which a value was measured (e.g. the 3rd value was measured at an angle of 90°). The angular values for each measurement value are stored in another variable.
I need a range for my x-axis from -180° to +180°. This alone is no problem. But how do I hand over my measurement values? I have to somehow link them to the angular values. So that each value in Values is somehow linked to it's angular value in Angles. For my y-axis, I can simply count from 0 to the amount of rows of my Values Array.
EXAMPLE:
Valueslooks like:
3 5 6
2 1 7
5 8 2
Angles looks like:
37° 38° 39°
36° 37° 38°
34° 35° 36°
Values(1,1) = 3 was measured at Angles(1,1) = 37° for example.
At each angle, the number of bars varies depending on how many measurements exist for that angle. bar3 needs a matrix input. In order to build a matrix, missing values are filled with NaN.
Warning: NaNs are usually ignored by plotting commands, but bar3 apparently breaks this convention. It seems to replace NaNs by zeros! So at missing values you'll get a zero-height bar (instead of no bar at all).
[uAngles, ~, uAngleLabels] = unique(Angles); %// get unique values and
%// corresponding labels
valuesPerAngle = accumarray(uAngleLabels(:), Values(:), [], #(v) {v});
%// cell array where each cell contains all values corresponding to an angle
N = max(cellfun(#numel, valuesPerAngle));
valuesPerAngle = cellfun(#(c) {[c; NaN(N-numel(c),1)]}, valuesPerAngle);
%// fill with NaNs to make all cells of equal lenght, so that they can be
%// concatenated into a matrix
valuesPerAngle = cat(2, valuesPerAngle{:}); %// matrix of values for each angle,
%// filled with NaNs where needed
bar3(uAngles, valuesPerAngle.'); %'// finally, the matrix can be plotted
ylabel('Angles')
xlabel('Measurement')
With your example Values and Angles this gives:

MATLAB Plotting a monthly data on a daily axis

I have a plot that has 528 points on the x-axis. The x-axis is labelled by mmm yyyy. I want to plot data over it, but the data is in monthly form. I want to take each of the monthly data points and plot it at the beginning of the month as a dot.
% Axis and plot
t = 731:1258; % 20120101 to 20130611
y = reshape(dataPoint_Anom1x1(:,:,731:end),[],1); % Size 528x1
x = datenum(2009, 12, 31) + t; % Convert t into serial numbers
plot(x, y); % Plot data
hold on
The part below is what I'm having trouble with. The dataPoint_Clim1x1 is size 12x1. (1,1) corresponds to January, (2,1) corresponds to February, etc. I need to plot the corresponding month's climatology point as a dot at the beginning of each month between January 2012 and June 2013.
%%%% Plot climatology on the same graph
dataClim_1x1 = dataClim(u,v,:); % Array that only contains points 1 degree away from 72.5E and 67.25S
B = mean(dataClim_1x1); % Average along the column
dataPoint_Clim1x1 = mean(B,2); % Average along the row
x_dataClim = ???
y_dataClim = reshape(dataPoint_Clim1x1, [],1); % Change dataPoint_Clim1x1 into a 1 column matrix
plot(x_dataClim,y_dataClim) % y_dataClim is only 12x1.
So the plot command right above is wrong. Do I just need to somehow set up the x-axis so that it plots every month with datenum somehow? I don't want to use a secondary axis though.
I think you just need to define your x coordinates of the points with
x_dataClim = datenum(2011, 1:12, 1);
This generates "the first of the month":
>> datestr(x_dataClim)
ans =
01-Jan-2011
01-Feb-2011
01-Mar-2011
01-Apr-2011
01-May-2011
01-Jun-2011
01-Jul-2011
01-Aug-2011
01-Sep-2011
01-Oct-2011
01-Nov-2011
01-Dec-2011
The cool thing is that you can actually "go into next year" - so
>> datestr(datenum(2011, 11:14, 1))
ans =
01-Nov-2011
01-Dec-2011
01-Jan-2012
01-Feb-2012
Here's what I ended up doing:
x = datenum(2011,1:30,1); % 30 months of data (2 and 1/2 years)
y_dataClim = reshape(dataPoint_Clim1x1, [],1); % Change dataPoint_Clim1x1 into a 1 column matrix
y = cat(1, y_dataClim, y_dataClim, y_dataClim(1:6,:));
scatter(x,y, 50,'fill'); % Plot scatter plot

Merge two (or more) time stamped data and plot showing the gap(s)

what is the best way to merge and plot 2 (or more) time stamped data so that the plot includes the gaps between the known data?
For example, I have a cell with time and heart rate values for Monday, and another for Friday. I would like to plot all data from Mon to Friday which includesthe gaps showing nothing was recorded from Tues-Thurs?
So far if I merge the data
data = [mon;fri]
% get the time serial numbers
dateInd = 1;
dateString = data(dateIndex);
dateFormat = 'dd/mm/yyyy HH:MM:SS';
tNum = datenum(dateString{1},dateFormat);
t = linspace(tNum(1),tNum(end),length(tNum));
% get heart rates,
HRIndex = 2;
HR = data(HRIndex);
and plot
plot(t,HR)
datetick('x','ddd');
I obviously get Monday and Fridays data merged into a single plot 2 days long. but I would like to have a plot 5 days long with data only showing on monday and Friday. What is the best way to achieve this?
I hope this makes sense,
Many thanks,
Jon
To achieve such effect I usually fill missing data with NaNs, like here:
x = linspace(0,2*pi,100);
y = sin(x);
y(20:30) = NaN; % there will be a gap from point#20 to point#30
plot(x,y);
The reason is that MatLab does not draw plot points where either x or y data are NaNs.
In your case you may add missing time points to your time data (to have corect gap) and NaNs to corresponding Y-values.
By the way, why don't you plot two separate plots with X-data of the second one properly shifted?
EDIT
Case 1: your x-data is time relative to the start of the day (in 0-24 interval). If you plot them directly they will overlap. You have to add some offset manually, like this:
% generate test data
x1 = linspace(0,1,25); % 25 points per first day
y1 = rand(25,1);
x2 = linspace(0,1,25); % 25 points per second day
y2 = rand(25,1);
% plot them as two separate plots
% so line style, color, markers may be set separately
XOffset = 3;
figure;
plot(x1,y1,'*k-', x2+XOffset,y2,'*r-');
% plot them as single separate plot
% so line style, color, markers are the same
figure;
plot([x1(:); NaN; x2(:)+XOffset],[y1(:); NaN; y2(:)],'*k-');
% One NaN is enough to insert a gap.
Case 2: your x-data have full time information including the date (like MatLab's serial date number, see help on now function, for example). Then just plot them, they will be offset automatically.
% generate test data
XOffset = 3;
x1 = linspace(0,1,25); % 25 points per first day
y1 = rand(25,1);
x2 = linspace(0,1,25)+XOffset; % 25 points per second day, with offset
y2 = rand(25,1);
% plot them as two separate plots
% so line style, color, markers may be set separately
figure;
plot(x1,y1,'*k-', x2,y2,'*r-');
% plot them as single separate plot
% so line style, color, markers are the same
figure;
plot([x1(:); NaN; x2(:)],[y1(:); NaN; y2(:)],'*k-');
% One NaN is enough to insert a gap.
Also instead of
plot(x1,y1,'*k-', x2,y2,'*r-');
you may do like this (number of plots is not limited):
hold on;
plot(x1,y1,'*k-');
plot(x2,y2,'*r-');
hold off;