Reputation: 1637
I am trying to plot a hypnogram (graph that shows sleep cycles) and am currently using stairstep function to plot it. Below is a sample data since the one I am working with is huge:
X = linspace(0,4*pi,10);
Y = sin(X);
stairs(X,Y)
How do I make the lines of every ticks/score on the y-axis have a unique color? Which looks something like this:
Upvotes: 2
Views: 1325
Reputation: 11802
One way to do it would be to segregate your data into as many dataset as your have flat levels, then plot all these data sets with the required properties.
There is however a way to keep the original dataset into one piece. If we consider your initial example data:
X = linspace(0,4*pi,10);
Y = sin(X);
step 1: recreate a "stair" like data set
Then by recombining the elements of X
and Y
we can obtain the exact same output than with the stairs
function:
x = reshape( [X;X], 1,[] ); x(1) = [] ; % duplicate each element, remove the first one
y = reshape( [Y;Y], 1,[] ); y(end) = [] ; % duplicate each element, remove the lastone
hp = plot(x,y) ;
step 2: Use patch
to be able to specify level colors
The patch
object has many option for colouring faces, vertex and edges. The default patch
object will try to close any profile given in coordinate by joining the first and last point. To override this behaviour, you just need to add a NaN
element to the end of the coordinate set and patch
will produce a simple line (but all the colouring options remain !).
To determine how many levels and how many colors we will need, we use the function unique
. This will tell us how many unique levels exist in the data, and also we can associate each level with an index which will point to the color map.
%% Basic level colored line patch
% create profile for patch object
x = reshape([X;X],1,[]); x(1) = [] ; % same as above to get a "stairs" shape
y = reshape([Y;Y],1,[]); y(end) = [] ; % idem
xp = [x,NaN] ; % add NaN in last position so the patch does not close the profile
yp = [y,NaN]; % idem
% prepare colour informations
[uy,~,colidx] = unique(Y) ;
ncolor = length(uy) ; % Number of unique level
colormap(hsv(ncolor)) % assign a colormap with this number of color
% create the color matrix wich will be sent to the patch object
% same method of interleaving than for the X and Y coordinates
cd = reshape([colidx.';colidx.'],1,[]);
hp = patch(xp,yp,cd,'EdgeColor','interp','LineWidth',2) ;
colorbar
Yes! ... now our flat levels have a colour corresponding to them ... but wait, those pesky vertical lines are still there and polluting the graph. Could we colour them in a different way? Unfortunately no. No worries however, there is still a way to make them completely disappear ...
step 3: Use NaN
to disable some segments
Those NaN
will come to the rescue again. Any segment defined with a NaN
will not be plotted by graphic functions (be it plot
, patch
, surf
or any other ...). So what we can do is again interleave some NaN
in the original coordinate set so only the horizontal lines will be rendered. Once the patch is created, we can build a second, "opposite", coordinate set where only the vertical lines are visible. For this second set, since we do not need fancy colouring, we can simply render them with plot
(but you could also build a specific patch for that too if you wanted to colour them differently).
%% invisible vertical line patch + dashed vertical lines
% prepare profile points, interleaving NaN between each pair
vnan = NaN(size(X)) ;
xp = reshape([X;vnan;X],1,[]); xp([1:2 end]) = [] ;
yp = reshape([Y;Y;vnan],1,[]); yp(end-2:end) = [] ;
% prepare the vertical lines, same method but we interleave the NaN at one
% element offset
xv = reshape([X;X;vnan],1,[]); xv([1:3 end]) = [] ;
yv = reshape([Y;vnan;Y],1,[]); yv([1:2 end-1:end]) = [] ;
% prepare colormap and color matrix (same method than above)
[uy,~,colidx] = unique(Y) ;
ncolor = length(uy) ; % Number of unique level
colormap(hsv(ncolor)) % assign a colormap with this number of color
% create the color matrix wich will be sent to the patch object
% same method of interleaving than for the X and Y coordinates
cd = reshape([colidx.';colidx.';vnan],1,[]); cd(end-2:end) = [] ;
% draw the patch (without vertical lines)
hp = patch(xp,yp,cd,'EdgeColor','flat','LineWidth',2) ;
% add the vertical dotted lines
hold on
hv = plot(xv,yv,':k') ;
% add a label centered colorbar
colorbar('Ticks',((1:ncolor)+.5)*ncolor/(ncolor+1),'TickLabels',sprintf('level %02d\n',1:ncolor))
I have used the hsv
colormap in the last example because your example seems to indicate that you do not need gradually progressing colors. You could also define a custom colormap with the exact color you want for each level (but that would be another topic, already covered many time if you search for it on Stack Overflow).
Happy R.E.M. sleeping !
Upvotes: 1
Reputation: 642
Below code is not that efficient, but works well.
Basically, it draws line by line from left to right.
Firstly, generate sample data
num_stage = 6;
% generate sample point
x = linspace(0,1,1000)';
% generate its stage
y = round((sin(pi*x)+1)*(num_stage-1)/2)/(num_stage-1);
stage = unique(y); % find value of each stage
color_sample = rand(num_stage,3); % set color sample
Then we can draw like this
idx = find([1;diff(y)]); % find stage change
idx(end+1) = length(x)+1; % add last point
% display routine
figure;
% left end stage
k = 1;
% find current stage level
c = find(stage == y(idx(k)));
% plot bold line
plot(x([idx(k),idx(k+1)-1]),y(idx(k))*ones(2,1),'color',color_sample(c,:),'linewidth',5);
hold on;
for k = 2 : length(idx)-1
% find current stage level
c = find(stage == y(idx(k)));
% plot dashed line from left stage to current stage
plot(x([idx(k)-1,idx(k)]),[y(idx(k-1));y(idx(k))],'--','color',[0.7,0.7,0.7]);
% plot bold line for current stage with specified color
plot(x([idx(k),idx(k+1)-1]),y(idx(k))*ones(2,1),'color',color_sample(c,:),'linewidth',5);
end
% set x-axis
set(gca,'xlim',[x(1),x(end)]);
Upvotes: 1
Reputation: 388
Use if statement and divide it blocks. Check the criteria of Y-axis to be in a certain range and if it falls in that range, plot it there using the colors you want. For example if (y>1) plot(x,y,'r') else if (y some range) plot(x,y,'b'). Hope it helps
Upvotes: 1