12

I want to add a bunch of evenly spaced vertical lines to a plot using \foreach.

I can do it like this:

\documentclass{standalone}
\usepackage{tikz}
\usepackage{pgfplots}
\pgfplotsset{compat=1.10}

\begin{document}

\begin{tikzpicture}

    \begin{axis}[
        axis lines=left,
        scaled ticks=false,
        xtick=\empty,
        ytick=\empty,
        ymin=0,
    ]

    \addplot[samples=100,domain=0:10] {(x-5)^2 + 20};

    \addplot coordinates {\foreach \x in {1,...,9}  (\x,0) (\x,40)};

    \end{axis}

\end{tikzpicture}

\end{document}  

And I get the following plot:

enter image description here

However, I want all of the lines to be part of the same plot; something like this:

\addplot coordinates {\foreach \x in {1,...,9}  (\x,0) (\x,40)};

or this:

\addplot {\foreach \x in {1,...,9} coordinates (\x,0) (\x,40)};

But neither of these compiles. Is it possible to use \foreach inside \addplot, and if so, what's the correct syntax?

Edit: I want the plot to be something like this:

enter image description here

which I generated with the following snippet:

\addplot coordinates {
    (1,0) (1,40)

    (2,0) (2,40)

    (3,0) (3,40)

    (4,0) (4,40)
};

But I want to do it using a for-type loop.

By the way, the reason I'm doing this is to be able to drop lines from the curve to the x-axis and label them. I plan to make the vertical lines invisible, then find the intersections of the curve the vertical lines, and then use the intersection points as coordinates for visible lines that start at the curve. If there's a more efficient way to do that (which seems like a pretty common thing to do), I'm all ears. Edit: I'm reading the function from a file, not generating it using a formula.

  • 2
    Not sure to understand it, but if you know the plotted function, a coordinate on it is just (x, f(x)). Why do you need to find an intersection point? Take a look at "Accessing Axis Coordinates in Graphical Elements" in pgfplots documentation – Ignasi Sep 01 '15 at 13:34
  • I'm actually reading the function from a file -- see my comment below. – LarrySnyder610 Sep 01 '15 at 16:41

3 Answers3

13

To avoids expansion issues with \foreach, pgfplots offers \pgfplotsinvokeforeach which can be used without problems; also, you can easily draw your lines from the curve to the axis using the function (no need to find intersections points); declaring the function from the beginning also simplifies the code:

enter image description here

The code:

\documentclass[border=5]{standalone}
\usepackage{pgfplots}
%\pgfplotsset{compat=1.12}

\pgfmathdeclarefunction{myfunct}{1}{%
  \pgfmathparse{(#1-5)^2 + 20}%
}

\begin{document}    
\begin{tikzpicture} 
    \begin{axis}[%
        ,axis lines=left
        ,xtick=\empty
        ,ytick=\empty
        ]
        \addplot[samples=100,domain=0:10] {myfunct(x)};
        \pgfplotsinvokeforeach{1,...,9}{
            \addplot coordinates { (#1,0) (#1,40) };}
    \end{axis}  
\end{tikzpicture}\qquad
\begin{tikzpicture} 
    \begin{axis}[%
        ,axis lines=left
        ,xtick=\empty
        ,ytick=\empty
        ]
        \addplot[samples=100,domain=0:10] {myfunct(x)};
        \pgfplotsinvokeforeach{1,...,9}{
            \addplot coordinates { (#1,0) (#1,{myfunct(#1)}) };
            \node[above=5pt] at (axis cs:#1,{myfunct(#1)}) {\pgfmathprint{myfunct(#1)}};
            }
    \end{axis}  
\end{tikzpicture}   

\end{document} 

Update

Using the same idea, name the path for the curve and inside \pgfplotsinvokeforeach draw the lines and name them; find the intersection points and draw the desired lines with the required labels:

\documentclass[border=5pt]{standalone}
\usepackage{pgfplots}
\usepgfplotslibrary{fillbetween}
\pgfplotsset{compat=1.10}

\usepackage{filecontents}
\begin{filecontents*}{mydata.dat}
-6 36
-5 25
-4 16
-3 9
-2 4
-1 1
0 0
1 1
2 4
3 9
4 16
5 25
6 36
\end{filecontents*}


\begin{document}

\begin{tikzpicture}

\begin{axis}[
  axis lines=left,
  scaled ticks=false,
  xtick=\empty,
  ytick=\empty,
  ymin=0,
]
\addplot[no marks,smooth,name path=curve] table {mydata.dat};
\pgfplotsinvokeforeach{-6,...,6}{
  \addplot[draw=none,name path={line#1}] coordinates  { (#1,0) (#1,40) };
  \path[name intersections={of=curve and line#1,by={point#1}}];
  \draw[red]
    (axis cs:#1,0) -- (point#1) node[above,black] {#1};
}
\end{axis}

\end{tikzpicture}

\end{document} 

enter image description here

Gonzalo Medina
  • 505,128
  • Actually I want the foreach inside the addplot because I want all the vertical lines to be part of the same plot (to make indexing easier later on). Also, let me clarify: I don't actually know the function, I'm reading data from a file. I only used x^2 here to build a compilable MWE. So, if I am reading the function points from a file, is there a way to do something like your second example? – LarrySnyder610 Sep 01 '15 at 16:37
  • @grendelsdad Sounds like a new question to me. Feel free to open it, providing there a little complete document and a simple data file showing your relevant settings. – Gonzalo Medina Sep 01 '15 at 16:38
  • Sorry, @GonzaloMedina, I edited my response to your answer -- see above. – LarrySnyder610 Sep 01 '15 at 16:55
  • @grendelsdad I suggested a new question, not editing this one. Now my answer seems partly useless. Please, in the future, state your actual settings and intent from the beginning. – Gonzalo Medina Sep 01 '15 at 16:58
  • @grendelsdad Now your question is confusing. You have some data in a file and are plotting those data and what do you want to do? At least, add to your question some simple data file showing us what you are really doing! – Gonzalo Medina Sep 01 '15 at 17:06
  • Sorry for the confusion. There are two issues. One is using a foreach loop (or something like it) inside an addplot -- I want to name that (single) path and then find intersections between that path and the curve. The second issue only arose based on your (and others') responses, and that is, whether there's an easy way to find f(x) for different x values when f(.) is read from a file instead of specified explicitly. That, I agree, should be a new question and I will post one later. – LarrySnyder610 Sep 01 '15 at 17:09
  • @grendelsdad Please see my updated answer. – Gonzalo Medina Sep 01 '15 at 17:20
  • Thanks @GonzaloMedina, this is perfect. Sorry about the confusion. – LarrySnyder610 Sep 01 '15 at 19:10
3

The correct syntax (or at least a syntax which works) can be seen below:

% arara: pdflatex

\documentclass[border=5]{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.12}

\begin{document}    
\begin{tikzpicture} 
    \begin{axis}[%
        ,axis lines=left
        ,xtick=\empty
        ,ytick=\empty
        ]
        \addplot[samples=100,domain=0:10] {(x-5)^2 + 20};
        \foreach \x in {1,...,9}
        {\edef\temp{\noexpand\addplot coordinates { (\x,0) (\x,40)};}\temp}
    \end{axis}  
\end{tikzpicture}   
\end{document} 

enter image description here

LaRiFaRi
  • 43,807
1

From last paragraph on your question, I understand you want something like this:

\documentclass[border=2mm]{standalone}
\usepackage{tikz}
\usepackage{pgfplots}
\pgfplotsset{compat=1.10}

\begin{document}

\begin{tikzpicture}

    \begin{axis}[
        axis lines=left,
        scaled ticks=false,
        xtick=\empty,
        ytick=\empty,
        ymin=0,
        declare function={myfunction(\t)=(\t-5)^2+20;}
    ]

    \addplot[samples=100,domain=0:10] {myfunction(x)};

    \foreach \x in {1,...,9}
        {\edef\temp{\noexpand\addplot coordinates { (\x,{myfunction(\x)})};}\temp}

     \draw (axis cs:3,{myfunction(3)})--++(90:2cm);
     \draw[red] (axis cs:2,{myfunction(2)})--(axis cs:7,{myfunction(7)});
    \end{axis}

\end{tikzpicture}

\end{document}

enter image description here

Ignasi
  • 136,588