TYL
TYL

Reputation: 1637

Adding colors to lines of a stairstep plot

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: enter image description here

Upvotes: 2

Views: 1325

Answers (3)

Hoki
Hoki

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

simple stair plot


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

colored patch line

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

flat coloured levels


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

Dohyun
Dohyun

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

Following is resultenter image description here

Upvotes: 1

shaifali Gupta
shaifali Gupta

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

Related Questions