8

How to define and plot a piecewise defined function in tikz or tkz-fct? For example consider the function

f(x) = 1 for x < 0, x for 0 <= x < 1, cos(x) for x >= 1

Edit:

After Torbjørn T.'s comment I tried the following. I should mention that I included three different versions of plotting the function because I want that all three versions should work.

\documentclass{article}
\usepackage{amsmath}
\usepackage{pgfplots}
\usepackage{tkz-fct}

\pgfmathdeclarefunction{p}{3}{%
  \pgfmathparse{(and(#1>#2, #1<#3))}%
}

\pgfmathdeclarefunction{f}{1}{%
  \pgfmathparse{p(#1,-100,0)*1 + p(#1,0,1)*#1 + p(#1,1,100)*cos(#1r)}
}

\begin{document}

\begin{tikzpicture}
   \tkzInit[xmin=-1,xmax=5,ymax=4] %
   \tkzGrid %
   \tkzAxeXY %
   \tkzFct{f(x)} %
   \draw plot function{f(x)};%
      \begin{scope}[xshift=4cm]
      \begin{axis}
         \addplot[domain=-2:4,samples=100]{f(x)};
      \end{axis}
   \end{scope}
\end{tikzpicture}


\end{document}

Which gives the following output:

output

4
  • 4
    Does this help? tex.stackexchange.com/questions/19510/… Nov 3, 2011 at 18:50
  • Thanks, I tried something with it, see edit above, but it doesn't work...
    – student
    Nov 3, 2011 at 19:38
  • 1
    I can't test here (windows), but I presume you would want your r inside of the cos. Also this does not cover the cases where x=0 and x=1, although that should not cause too much problems. Nov 3, 2011 at 20:28
  • @wh1t3: Thanks, I put the r inside of the cos now
    – student
    Nov 3, 2011 at 21:40

4 Answers 4

6

There are a few problems with the code:

  1. PGF uses degrees for trig functions
  2. Seems that the r at the end of the function f was intended to convert from radians to degree, but I changed it so that it is more obvious.
  3. Need to determine what happens at the end points of the piecewise domain, so note the slight tweaks for that.
  4. Not sure what the tkz portion of the code has to do with the problem of defining a piecewise function, so have commented that out.
  5. Be careful using single letter function names as documented in: Why do 2 identical function definitions with different names produce two different plots?

So, with slight modifications to your code I can produce the following. Note that there still is a problem around x=1 as pgf does not know what to do.

enter image description here

\documentclass[border=2pt]{standalone}
\usepackage{amsmath}
\usepackage{pgfplots}
\usepackage{tkz-fct}

\pgfmathdeclarefunction{p}{3}{%
  \pgfmathparse{(and(#1>#2, #1<#3))}%
}

\pgfmathdeclarefunction{f}{1}{%
  \pgfmathparse{p(#1,-100,-0.001)*1 + p(#1,0,1)*#1 + p(#1,1.01,100)*cos(deg(#1))}%
}

\begin{document}
\begin{tikzpicture}
%   \tkzInit[xmin=-1,xmax=5,ymax=4] %
%   \tkzGrid %
%   \tkzAxeXY %
%   \tkzFct{f(x)} %
%   \draw plot function{f(x)};%
      \begin{scope}[xshift=6cm]
      \begin{axis}
         \addplot[ultra thick, blue,domain=-2:4,samples=100]{f(x)};
      \end{axis}
   \end{scope}
\end{tikzpicture}
\end{document}

What I would recommend is that you draw the three separate portions individually and avoid the problem areas via a fixed value of \Tolerance:

enter image description here

\documentclass[border=2pt]{standalone}
\usepackage{amsmath}
\usepackage{pgfplots}
\usepackage{tkz-fct}

\pgfmathdeclarefunction{p}{3}{%
  \pgfmathparse{(and(#1>#2, #1<#3))}%
}

\newcommand{\Tolerance}{0.0001}%
\pgfmathdeclarefunction{f}{1}{%
  \pgfmathparse{%
    p(#1,-\maxdimen,-\Tolerance)*1.0 +%
    p(#1,0,1-\Tolerance)*#1 +%
    p(#1,1,\maxdimen)*cos(deg(#1))}%
}

\begin{document}
\begin{tikzpicture}
%   \tkzInit[xmin=-1,xmax=5,ymax=4] %
%   \tkzGrid %
%   \tkzAxeXY %
%   \tkzFct{f(x)} %
%   \draw plot function{f(x)};%
      \begin{scope}[xshift=6cm]
      \begin{axis}
         \addplot[ultra thick, blue,domain=-2:-\Tolerance,samples=100]{f(x)};
         \addplot[ultra thick, green,domain=\Tolerance:1-\Tolerance,samples=100]{f(x)};
         \addplot[ultra thick, red,domain=1+\Tolerance:4,samples=100]{f(x)};
      \end{axis}
   \end{scope}
\end{tikzpicture}
\end{document}

To clarify, the \addplot calls above are really just:

\addplot[ultra thick, blue, domain=-2.0000:-0.0001, samples=100]{f(x)};
\addplot[ultra thick, green,domain= 0.0001: 0.9999, samples=100]{f(x)};
\addplot[ultra thick, red,  domain= 1.0001: 4.0000, samples=100]{f(x)};
3
  • The tkz portion is because I want also be able to plot those functions using tkz-fct.
    – student
    Nov 3, 2011 at 21:33
  • @Peter Grill Thanks, your second picture looks fine, however the source code looks pretty complicated. I want a solution where I have a simple syntax to plot much more complicated piecewise defined functions, too.
    – student
    Nov 3, 2011 at 21:45
  • I too am looking for that since I posted the question you referred to in your posting. But I think the second one looks more complicated as I defined \Tolerance so it was easy to adjust. Nov 3, 2011 at 21:48
4

I would use the ifthenelse structure (available in tikz and in gnuplot).

\documentclass{standalone}
\usepackage{tikz}
\usepackage{pgfplots}
\begin{document}

% plain tikz + ifthenelse:
\begin{tikzpicture}[scale=3]
  \draw[blue,thick] plot[samples=200,domain=-2:4] (\x,{ifthenelse(\x <
    0,1,ifthenelse(and(\x >= 0,\x < 1),\x, cos(deg(\x))))});  
  \draw[red,thick,semitransparent] plot[samples=200,domain=-2:4]
    (\x,{cos(deg(\x))});   
\end{tikzpicture}

% pgfplots + gnuplot:
\begin{tikzpicture}
  \begin{axis}
    \addplot+[samples=150,domain=-2:4] function {x < 0 ? 1 : ((x >=0)
      && (x<1)) ? x : cos(x)}; 
    \addplot+[samples=150,domain=-2:4,semitransparent] function {cos(x)}; 
  \end{axis}
\end{tikzpicture}

% plain tikz with '?' operator: 
\begin{tikzpicture}[scale=3]
  \draw plot[samples=200,domain=-2:4] (\x,{\x < 0 ? 1 : (((\x >=0)
      && (\x<1)) ? \x : cos(deg(\x)))});
\end{tikzpicture}

% pgfplots without gnuplot: (requires developer version of pgf or pgfplots):
\begin{tikzpicture}
  \begin{axis}
    \addplot[blue,samples=150,domain=-2:4] {x < 0 ? 1 : (((x >=0)
      && (x<1)) ? x : cos(deg(x)))}; 
  \end{axis}
\end{tikzpicture}
\end{document}

An example

7
  • This does not appear to be using the cos function for x>1. Nov 3, 2011 at 20:39
  • @PeterGrill It does (see my updated answer), but I did not believe it either!
    – cjorssen
    Nov 3, 2011 at 20:53
  • The OP used domain=-2:4 so probably best to stick to that domain so that the results are easy to compare. Nov 3, 2011 at 21:20
  • @cjorssen I like your answer. I took the freedom to add variants using the ? operator as well. And by the way: I found that \usetikzlibrary{fpu} (which is used by pgfplots), did not properly support it. I have just comitted support for ==, !=, <=, >=, ? to pgf CVS. The last example will only compile with this commit. Nov 3, 2011 at 21:24
  • @ChristianFeuersänger Thanks! I was investigating to understand why it didn't work for me. I changed the domain to stick to the OP one.
    – cjorssen
    Nov 3, 2011 at 21:37
3

Run with xelatex

%f(x) = 1 for x < 0, x for 0 <= x < 1, cos(x) for x >= 1

\documentclass{article}
\usepackage{pstricks-add}
\pagestyle{empty}
\begin{document}    
\begin{pspicture}(-1,-1)(7,1)
\psaxes[trigLabels,xunit=\pstRadUnit,trigLabelBase=3]{->}(0,0)(-1,-1)(7,1.2)
\psplot[algebraic,linecolor=red,plotpoints=1000,
        linewidth=1pt]{-1}{7}{IfTE(x<0,1,IfTE(x<1,x,cos(x)))}
\end{pspicture}
\end{document}

enter image description here

2

I slightly modified @Herbert's code to make the graph of f (visually) "a graph of a function". It still has few quirks.

\documentclass{article}
\usepackage{pstricks-add}
\pagestyle{empty}
\begin{document}    
\begin{pspicture}(-1,-1)(7,1)
\psplot[algebraic,linecolor=red,plotpoints=1000,
        linewidth=1pt]{-1}{7}{IfTE(x<0,1,IfTE(x<1,x,cos(x)))}
\psline[linecolor=white,linewidth=1pt](0,1)(0,0)
\psline[linecolor=white,linewidth=2pt](1,.96)(1,0.54)
\psaxes[trigLabels,xunit=\pstRadUnit,trigLabelBase=3]{->}(0,0)(-1,-1)(7,1.2)
\end{pspicture}
\end{document}

enter image description here

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.