5

I'm using a lot of step functions in my master's thesis and would like to automate the plotting. I can get them to look just as I want by hand using this method, but when I try to automate it like this there are two problems (see picture).

  1. The vertical jump is sometimes plotted instead of just being a jump.
  2. The dotted lines to indicate vertical jumps are only drawn the last time I call the command.

How can I fix this?

In the \stepfunction command I define the function to be plotted locally in the for-loop. Like this:

  \foreach \xStart/\xEnd  in {\minx/\s, \s/\e, \e/\maxx} {
    \addplot[domain=\xStart:\xEnd, blue, ultra thick,
        declare function={
            pj(\x) = and(\x >= \s, \x < \e)*(\val) + 
                and(\x >= \e, 1)*(\b) + 
                and(1, \x < \s)*(\b);}] 
        {pj(x)};

Is this a good idea? I want to be able to draw many functions in a single picture so I can't put the whole axis environment in the newcommand, right?

Left: automated plot, right: plot by hand.

Code

\documentclass[border=3pt]{standalone}

\usepackage{tikz}
\usepackage{pgfplots}
\pgfplotsset{compat=1.9}


\pgfkeys{
  stepfunction/.is family,
  stepfunction,
  plotstart/.initial=0,
  plotend/.initial=5,
  start/.initial=1,
  end/.initial=2,
  value/.initial=1,
  base/.initial=0
}
\newcommand\stepfunctionset[1]{\pgfkeys{stepfunction,#1}}
\newcommand\stepfunction[1][]{
  \stepfunctionset{#1,
    plotstart/.get=\minx,
    plotend/.get=\maxx,
    start/.get=\s,
    end/.get=\e,
    value/.get=\val,
    base/.get=\b
  }
  % plot from \minx to \maxx
  \foreach \xStart/\xEnd  in {\minx/\s, \s/\e, \e/\maxx} {
    \addplot[domain=\xStart:\xEnd, blue, ultra thick,
        declare function={
            pj(\x) = and(\x >= \s, \x < \e)*(\val) + 
                and(\x >= \e, 1)*(\b) + 
                and(1, \x < \s)*(\b);}] 
        {pj(x)};
  };
  \draw[dotted] (axis cs:\s,\b) -- (axis cs:\s,\val);
  \draw[dotted] (axis cs:\e,\b) -- (axis cs:\e,\val);
  \addplot[color=blue,fill=white,only marks,mark=*] 
    coordinates{(\s,\b)(\e,\val)};
  \addplot[color=blue,only marks,mark=*] 
    coordinates{(\s,\val)(\e,\b)};
}

\begin{document}
\begin{tikzpicture}[scale=1.5, thick]
  \begin{axis}[xmin=-1, xmax=6, ymax=3.5]
    % p_{j-1,1}
    \stepfunction[plotstart=-1, plotend=2.5, 
        start=0, end=2, value=1, base=0]
    % p_{j+1,6}
    \stepfunction[plotstart=2.5, plotend=3.75, 
        start=3, end=3.5, value=3, base=0]
    % p_{j,4}
    \stepfunction[plotstart=3.75, plotend=6, start=4, end=5, value=2]
  \end{axis}
\end{tikzpicture}

% Plot by hand
\begin{tikzpicture}[
  scale=1.5,
  thick,
  soldot/.style={color=blue,only marks,mark=*},
  holdot/.style={color=blue,fill=white,only marks,mark=*},
  declare function={
    pj(\x)=and(\x >= 0, \x < 1)*(1) + 
        and(\x >= 1, 1)*(0) + 
        and(1, \x < 0)*(0);
    }]
  \begin{axis}[xmin=-1, xmax=6, ymax=3.5]

    % p_{j-1,1}
    \foreach \xStart/\xEnd  in {-1/0, 0/2, 2/3} {
        \addplot[domain=\xStart:\xEnd, blue, ultra thick] {pj(0.5*x)};
    }
    \draw[dotted] (axis cs:0,0) -- (axis cs:0,1);
    \draw[dotted] (axis cs:2,0) -- (axis cs:2,1);
    \addplot[holdot] coordinates{(0,0)(2,1)};
    \addplot[soldot] coordinates{(0,1)(2,0)};


    % p_{j+1,6}
    \foreach \xStart/\xEnd  in {3/3.5, 3.5/4} {
        \addplot[domain=\xStart:\xEnd, blue, ultra thick]
            {pj(2*(x-3))*3};
    }
    \draw[dotted] (axis cs:3,0) -- (axis cs:3,3);
    \draw[dotted] (axis cs:3.5,0) -- (axis cs:3.5,3);
    \addplot[holdot] coordinates{(3,0)(3.5,3)};
    \addplot[soldot] coordinates{(3,3)(3.5,0)};

    % p_{j,4}
    \foreach \xStart/\xEnd  in {4/5, 5/6} {
        \addplot[domain=\xStart:\xEnd, blue, ultra thick] 
            {pj((x-4))*2};
    }
    \draw[dotted] (axis cs:4,0) -- (axis cs:4,2);
    \draw[dotted] (axis cs:5,0) -- (axis cs:5,2);
    \addplot[holdot] coordinates{(4,0)(5,2)};
    \addplot[soldot] coordinates{(4,2)(5,0)};

  \end{axis}
\end{tikzpicture}

\end{document}

Edit

Using the option jump mark left for plotting solves the first problem and makes the foor-loop unnecessary:

% plot from \minx to \maxx
\addplot[domain=\minx:\maxx, blue, ultra thick, jump mark left,
        declare function={
            pj(\x) = and(\x >= \s, \x < \e)*(\val) + 
                and(\x >= \e, 1)*(\b) + 
                and(1, \x < \s)*(\b);}] 
        {pj(x)};

Thanks Paul Stiverson!

  • Have you looked at the jump mark left options for plot in TikZ? – Paul Stiverson Jun 24 '15 at 17:32
  • Nice! I was not aware of that option! Indeed jump mark left solves the first problem. I'll add that to the question. – Lennart Jern Jun 24 '15 at 17:41
3

rather than specify the step function as a sum of booleans, seems easier to specify it as a list of start/end/value triplets and then just loop through:

enter image description here

\documentclass[border=3pt]{standalone}

\usepackage{tikz}
\usepackage{pgfplots}
\pgfplotsset{compat=1.14}

\begin{document}
\def\lasty{}
\begin{tikzpicture}[
  scale=1.5,
  thick,
  soldot/.style={color=blue,only marks,mark=*},
  holdot/.style={color=blue,fill=white,only marks,mark=*}
]
  \begin{axis}[xmin=-1, xmax=6, ymax=3.5]
    \foreach \xStart/\xEnd/\yVal  in 
    {-1/0/0, 0/2/1, 2/3/0, 3/3.5/3, 3.5/4/0, 4/5/2, 5/6/0} {
        \addplot[domain=\xStart:\xEnd, blue, ultra thick] {\yVal}; 
        \ifx\lasty\empty\else
         \addplot[holdot](\xStart,\lasty);
         \addplot[soldot](\xStart,\yVal);
         \edef\tmp{\noexpand\draw[dotted]
           (axis cs: \xStart,\lasty) -- (axis cs: \xStart,\yVal);}
         \tmp
        \fi
        \global\let\lasty\yVal
}
  \end{axis}
\end{tikzpicture}

\end{document}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.