1
votes

How do I vertically offset a stem plot so that the stems emanate from say y == 0.5 instead of from the x-axis?

I know I could change the x-tick-marks but it would be better to rather just change the plot.

stem(X+0.5) doesn't work as it will just make the stems longer.

Also I have both positive and negative data. And also I have other plots on the same axis which I don't want to offset.


Based on Luis Mendo's answer below, I have written a function for this (however see my answer below as MATLAB actually has a built-in property for this anyway):

function stem_offset(x_data, y_data, offset, offset_mode, varargin)
%STEM_OFFSET stem plot in which the stems begin at a position vertically
%offset from the x-axis.
%
%   STEM_OFFSET(Y, offset) is the same as stem(Y) but offsets all the lines
%   by the amount in offset
%
%   STEM_OFFSET(X, Y, offset) is the same as stem(X,Y) but offsets all the
%   lines by the amount in offset
%
%   STEM_OFFSET(X, Y, offset, offset_mode) offset_mode is a string
%   specifying if the offset should effect only the base of the stems or
%   also the ends. 'base' for just the base, 'all' for the baseand the
%   ends. 'all' is set by default
%
%   STEM_OFFSET(X, Y, offset, offset_mode, ...) lets call all the stem()
%   options like colour and linewidth etc as you normally would with
%   stem().

    if nargin < 3
        offset = 1:length(y_data);
        y_data = x_data;
    end
    if nargin < 4
        offset_mode = 'all';
    end

    h = stem(x_data, y_data, varargin{:});
    ch = get(h,'Children');

    %Offset the lines
    y_lines = get(ch(1),'YData'); %// this contains y values of the lines

    %Offset the ends
    if strcmp(offset_mode, 'all')
        set(ch(1),'YData',y_lines+offset)
        y_ends = get(ch(2),'YData'); %// this contains y values of the ends
        set(ch(2),'YData',y_ends+offset) 
    else
        set(ch(1),'YData',y_lines+offset*(y_lines==0)) %// replace 0 (i.e. only the start of the lines) by offset
    end

end

Which I have now uploaded to the file exchange (http://www.mathworks.com/matlabcentral/fileexchange/45643-stem-plot-with-offset)

3

3 Answers

3
votes

The following seems to work. Apparently, the stem object's first child contains the vertical lines, so you just have to change all 0 values in their YData property to the desired offset:

delta = .5; %// desired offset
h = stem(1:10); %// plot to be offset. Get a handle

ch = get(h,'Children');
yy = get(ch(1),'YData'); %// this contains y values of the lines
set(ch(1),'YData',yy+delta*(yy==0)) %// replace 0 by delta

An example with both positive and negative data, and with other plots on the same axis:

stem(.5:4.5,ones(1,5),'g') %// not to be offset
hold on
h = stem(1:5,[-2 3 -4 1 -1]); %// to be offset
axis([0 5.5 -5 4])

ch = get(h,'Children');
yy = get(ch(1),'YData'); %// this contains y values of the lines
set(ch(1),'YData',yy+delta*(yy==0)) %// replace 0 by delta

enter image description here

2
votes

You can draw a white rectangle over the stems right after you call stem.

x = 1:10

stem(x)

fudge=0.05
rectangle('Position', [min(x) * range(x)*fudge, 0.5*fudge, range(x)*(1+2*fudge), 0.5-fudge], 'FaceColor', 'w', 'EdgeColor', 'w')

The fudge is there to avoid painting over the axis, and to make sure the leftmost and rightmost stems are covered.

enter image description here

2
votes

It appears that there actually is a property in stem for this that I somehow missed!

http://www.mathworks.com/help/matlab/ref/stem.html#btrw_xi-87

e.g. from the docs:

figure
X = linspace(0,2*pi,50)';
Y = (exp(0.3*X).*sin(3*X));
h = stem(X,Y);
set(h,'BaseValue',2);