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

student
  • 29,003

4 Answers4

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)};
Peter Grill
  • 223,288
  • The tkz portion is because I want also be able to plot those functions using tkz-fct. – student Nov 03 '11 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 03 '11 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. – Peter Grill Nov 03 '11 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

cjorssen
  • 10,032
  • 4
  • 36
  • 126
  • This does not appear to be using the cos function for x>1. – Peter Grill Nov 03 '11 at 20:39
  • @PeterGrill It does (see my updated answer), but I did not believe it either! – cjorssen Nov 03 '11 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. – Peter Grill Nov 03 '11 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. – Christian Feuersänger Nov 03 '11 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 03 '11 at 21:37
  • @cjorssen FYI: it seems as if you have resized your pictures, causing decreased quality. If you sample them immediately at a suitable resolution, you get much better quality. With imagemagick, you can use convert -density 100 <file>.pdf <file>.png – Christian Feuersänger Nov 03 '11 at 21:47
  • @ChristianFeuersänger Thanks for the command line, I changed the image accordingly. However, it is still in lower quality than the image posted by the OP. The previous image was converted by imgur as I uploaded the pdf. – cjorssen Nov 03 '11 at 22:04
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

Sony
  • 3,920