21

Am hesitant to ask yet one more embarrassing question, but I can't seem to see the problem with the \foreach in the MWE. It should produce several horizontal lines but only produces the one with the yValue as specified in the \newcommand{\yValue}{0.3}:

enter image description here

Code:

\documentclass{article}
\usepackage{pgfplots}

\newcommand{\yValue}{0.3}

\begin{document}
\begin{tikzpicture}
\begin{axis}
[
 ymin=0,
 ymax=1,
 xmin=0,
 xmax=1,
]

\foreach \yValue in {0.00,0.1,...,1.00} {
    \draw [red] (axis cs:0,\yValue) -- (axis cs:1,\yValue);
}

\end{axis}
\end{tikzpicture}
\end{document}
  • \draw [red] foreach \yValue in {0.00,0.1,...,1.00} {(axis cs:0,\yValue) -- (axis cs:1,\yValue)}; does what you want. – Adam Liter Apr 10 '14 at 4:01
  • Take a look at page 910 and 911 of the manual; it looks like you can use \foreach (or foreach) inside of a path, but you can't put a path inside of \foreach. At least, I assume the latter is true, since it doesn't work. The manual doesn't explicitly say that. – Adam Liter Apr 10 '14 at 4:04
  • @Adam: That would be a fine solution to post for the MWE given, but I actually have to do more than one thing in the \foreach so not sure it'll work for my specific case. Also, Page 909 shows an example of what I am trying to use. – Peter Grill Apr 10 '14 at 4:06
  • Ah, the answer seems to be at pages 470 and 471 of the pgfplots documentation. – Adam Liter Apr 10 '14 at 4:27
33

According to pages 470-471 of the pgfplots documentation:

Keep in mind that inside of an axis environment, all loop constructions (including custom loops, \foreach and \pgfplotsforeachungrouped) need to be handled with care: loop arguments can only be used in places where they are immediately evaluated; but pgfplots postpones the evaluation of many macros. For example, to loop over something and to generate axis descriptions of the form \node at (axis cs:\i,0.5)...., the loop macro \i will be evaluated in \end{axis} – but at that time, the loop is over and its value is lost. The correct way to handle such an application is to expand the loop variable explicitly.

Thus, you need to do:

\documentclass{article}
\usepackage{pgfplots}

\begin{document}
\begin{tikzpicture}
\begin{axis}
[
 ymin=0,
 ymax=1,
 xmin=0,
 xmax=1,
]

\foreach \yValue in {0.00,0.1,...,1.00} {
    \edef\temp{\noexpand\draw [red] (axis cs:0,\yValue) -- (axis cs:1,\yValue);}
    \temp
}

\end{axis}
\end{tikzpicture}
\end{document}

enter image description here


Edit

As @percusee points out in the comments, you can also use \pgfplotsinvokeforeach. This is different from \foreach because, according to page 471 of the documentation:

the \x would not be expanded whereas #1 is.

That is to say, it does exactly what is needed to handle the fact that pgfplots doesn't evaluate loop macros until \end{axis}.

Thus, you can also do:

\pgfplotsinvokeforeach{0.00,0.1,...,1.00}{
    \draw [red] (axis cs:0,#1) -- (axis cs:1,#1);
}

Note that the syntax is slightly different. \pgfplotsinvokeforeach uses #1, just like a \newcommand would, instead of allowing you to name your own variable.

  • 2
    Great. Thanks. Am surprised that I have not encountered this problem until today, at least I don't recall that I have -- just in case someone comes up an answer of mine that uses this :-). – Peter Grill Apr 10 '14 at 4:40
  • 8
    \pgfplotsinvokeforeach does this for you automatically. – percusse Apr 10 '14 at 6:12
  • @percusse Thanks for the tip. I've updated my answer accordingly. – Adam Liter Apr 10 '14 at 16:03
  • How can we use \pgfplotsinvokeforeach in a nested way? – bkarpuz Aug 19 '17 at 19:18
  • 2
    @bkarpuz Unfortunately, you can't. From the pgfplots documentation: "you can't nest this command yet (since it does not introduce protection by scopes)". – Adam Liter Aug 22 '17 at 19:40
1

I ran into a similar problem and the answers above didn't work for me. However, I realized that you often want to use foreach inside an axis environment because you want to have access to the axis cs coordinate system. I came up with the following workaround which I show here for the original MWE:

\documentclass{article}
\usepackage{pgfplots}
\usetikzlibrary{calc}

\begin{document}
\begin{tikzpicture}
  \begin{axis}[ymin=0, ymax=1, xmin=0, xmax=1]
    \coordinate (O) at (axis cs:0,0);
    \coordinate (X) at (axis cs:1,0);
    \coordinate (Y) at (axis cs:0,1);
  \end{axis}  
  \begin{scope}[x={($(X)-(O)$)}, y={($(Y)-(O)$)}, shift={(O)}]
    \foreach \y in {0, 0.1, ..., 1} {
      \draw[red] (0,\y) -- (1,\y);
    }
  \end{scope}
\end{tikzpicture}
\end{document}

The idea obviously is that you "recreate" the coordinate system in the scope after leaving the axis environment. This also works with 3D coordinates. (Note that in newer versions of pgfplots you don't need the axis cs: prefix anymore.)

  • Not sure who robust this solution is. Probably some options to tikzpicture or axis environment will break this, bit does seem to work for the given MWE. – Peter Grill Mar 14 at 18:47
  • @PeterGrill I've actually used this trick for quite complicated situations - for example, I mixed 3D mesh plots with blocks inserted by a foreach loop - and it worked fine so far. – Frunobulax Mar 14 at 21:37

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.