4

I am doing a group plot of related curves. Since they are related by a parameter, I want to color them according to (a function of) that parameter. For that purpose, I used the mesh option, and then use point meta to indicate what color it should have. However, the legend is then a mesh grid, and I want it to be a colored line. What's the cleanest way to do this?

I found the related post Legend with Markers for Mesh Plot but I couldn't adapt it to my case.

 \begin{tikzpicture}
    \begin{groupplot}[group style={group size={2 by 1},
                      xlabels at=edge bottom,
                      xticklabels at=edge bottom},
                    axis lines=left, domain=-5:5,
                    xlabel=$x$,
                    width=0.5\textwidth,colormap/hot,
                    legend columns=-1,
                    legend entries={ $\beta=0.1$, $\beta=0.5$,
                    $\beta=0.8$, $\beta=1.0$, $\beta=2.0$, $\beta=5.0$},
                    legend to name={fig:mm_temp_legend}]
                    ]
      \nextgroupplot[ylabel={$p(x)$}]
      \pgfplotsforeachungrouped \be/\zedd in {
                0.1/12.4595,
                0.5/18.3263,
                0.8/29.6888,
                1.0/43.0372,
                2.0/403.539,
                5.0/1.07923e6 } {
        \addplot+[line legend,samples=200,no marks,solid,mesh,point meta={ln(\be)}]
        gnuplot {exp(\be*(0.4*(x-0.3)**2-0.02*x**4))/\zedd};
      }
      %
      \nextgroupplot[ylabel={$-\log p(x)$},ymin=0]
      \pgfplotsforeachungrouped \be/\zedd in {
                0.1/12.4595,
                0.5/18.3263,
                0.8/29.6888,
                1.0/43.0372,
                2.0/403.539,
                5.0/1.07923e6 } {
        \addplot+[samples=200,no marks,solid,mesh,point meta={ln(\be)}]
        gnuplot {\be*(-0.4*(x-0.3)**2+0.02*x**4)+log(\zedd)};
      }
    \end{groupplot}
  \end{tikzpicture}

here's how it looks like

yannick
  • 813
  • If I am not mistaken, the mesh key automatically activates mesh legend as part of its every mesh style. A cure here could be to place line legend after mesh. However, your approach to color the plot by means of the macro \be might pose a challenge for the legend (which is supposed to be a relatively simple image; it does not inherit point meta). It would inherit the color if you could provide the color explicitly... – Christian Feuersänger Apr 03 '13 at 19:54
  • I would only know to do that by expanding the loop by hand. Do you know a method that is more compact? – yannick Apr 04 '13 at 08:36

1 Answers1

5

What you are trying to achieve is possible. However, it is actually more of a feature request (which explains why it poses difficulties).

The problem arises because a legend does not have the same color data (aka "point meta") values as its associated plot. And from my point of view, this is actually what one expects. In your example, you have used point meta to define a color, and I see that it is useful for you. However, the legend cannot know the resulting color.

The lack of color mapping in a legend is an inherent problem of the approach here. However, you can place line legend after mesh to generate a line legend (with the wrong color because of the reason explained above).

Note that your example actually generates two legends because the options after \begin{groupplot} apply to both following axes.

That said, one can fulfill your feature request with a relatively simple API call. Because pgfplots comes without a public basic level API, this command is undocumented. It maps a scalar value into some color map.

The idea is to define one color for each possible value of ln(\be) in advance and to use these predefined colors later-on:

\documentclass{article}

\usepackage{pgfplots}
\pgfplotsset{compat=1.7}
\usepgfplotslibrary{groupplots}


\begin{document}
\thispagestyle{empty}

\pgfplotsset{
    colormap/hot,
}%
\pgfmathparse{ln(0.1)}%
\let\BEMIN=\pgfmathresult

\pgfmathparse{ln(5)}%
\let\BEMAX=\pgfmathresult

% FIRST: define one color for every value of \be :
\pgfplotsforeachungrouped \be in {
        0.1,
        0.5,
        0.8,
        1.0,
        2.0,
        5.0} {
    \pgfmathparse{ln(\be)}
    \let\value=\pgfmathresult
    %
    \pgfplotscolormapaccess
            [\BEMIN:\BEMAX]% IMPORTANT for the mapping.
            {\value}
            {\pgfkeysvalueof{/pgfplots/colormap name}}
    % now, \pgfmathresult contains {<R>,<G>,<B>}
    % 
    \edef\temp{\noexpand\definecolor{be color \be}{rgb}{\pgfmathresult}}%
    %
    % now, \temp contains '\definecolor{be color 0.1}{rgb}{<R>,<G>,<B>}'
    \temp
}

\begin{tikzpicture}
    \begin{groupplot}[group style={group size={2 by 1},
                      xlabels at=edge bottom,
                      xticklabels at=edge bottom},
                    axis lines=left, domain=-5:5,
                    xlabel=$x$,
                    width=0.5\textwidth,%colormap/hot,
                    legend columns=-1,
                    ]
                    ]
      \nextgroupplot[ylabel={$p(x)$},
        % HINT: I added 'legend entries here as opposed to the environment:
        % only ONE sub-plot should have legend, not both
        legend entries={ $\beta=0.1$, $\beta=0.5$,
            $\beta=0.8$, $\beta=1.0$, $\beta=2.0$, $\beta=5.0$},
        legend to name={fig:mm_temp_legend},
    ]

      \pgfplotsforeachungrouped \be/\zedd in {
                0.1/12.4595,
                0.5/18.3263,
                0.8/29.6888,
                1.0/43.0372,
                2.0/403.539,
                5.0/1.07923e6 } {
        % NOTE: I replaced 'no marks' by 'mark=none'. Seems as if 'no
        % marks' does not make it into the legend (!?)
        %
        % this \edef\temp is required to expand 'draw=be color \be' to
        % 'draw=be color 0.1' etc:
        \edef\temp{%
            \noexpand\addplot+[draw=be color \be,id=\be,samples=200,mark=none,solid,]
                gnuplot {exp(\be*(0.4*(x-0.3)**2-0.02*x**4))/\zedd};
        }%
        \temp
      }
      %
      \nextgroupplot[ylabel={$-\log p(x)$},ymin=0]
      \pgfplotsforeachungrouped \be/\zedd in {
                0.1/12.4595,
                0.5/18.3263,
                0.8/29.6888,
                1.0/43.0372,
                2.0/403.539,
                5.0/1.07923e6 } {
        \addplot+[id=2_\be,samples=200,no marks,solid,mesh,point meta={ln(\be)}]
        gnuplot {\be*(-0.4*(x-0.3)**2+0.02*x**4)+log(\zedd)};
      }
    \end{groupplot}
  \end{tikzpicture}

  \ref{fig:mm_temp_legend}
\end{document}

enter image description here

An important step is to compute the lower and upper limit of the ln(\be) values, otherwise mapping is impossible. The actual loop involves a little bit of basic expansion control; compare Where do I start LaTeX programming? for details.

The \addplot+ command also needs to be expanded; otherwise the \be value would be lost. This is because draw=... is evaluated in the "visualization phase" which is started in \end{groupplot} (not before). And draw=... would just remember the unexpanded value which depends on \be.

I kept the second plot intact to allow a simple comparison. You may want to adopt it if this answer suits your needs.

Note that I added id=... to avoid repeated calls to gnuplot on my machine and that I had to replace no marks by mark=none (the latter appears to be a bug or usability issue in pgfplots, I will take care of it).

See also a related application of custom lookups in the colormap in Drawing heatmaps using TikZ


Now that I have implemented all that stuff; I leaned backward and realized that it might have been much more helpful to reimplement your use-case by means of cycle lists. These lists are how pgfplots typically handles varying colors; they are simple to use and fully supported when it comes to legends. If you feel that cycle lists are better (perhaps you really want a colormap, then they are not), you can find more information in the pgfplots manual.

  • Thank you so much for this detailed answer! I want the colormap, that's why I didn't use the cycle list key. – yannick Apr 04 '13 at 11:41
  • For those who use babel with the frenchb option, don't forget to wrap the figure in \shorthandoff{:}\shorthandoff{;} and \shorthandon{;}\shorthandon{:}. (taken from http://forum.mathematex.net/latex-f6/compatibilite-entre-french-babel-et-tikz-t11865.html) – yannick Apr 04 '13 at 12:50