1

Similar to this question, I would like to set the style (color/shape) of scatter points, depending on the value of a certain column read from a table.

\documentclass[tikz,border=3mm]{standalone}
\usepackage{pgfplots}
\pgfplotsset{width=7cm,compat=1.15}

\begin{document}
    \begin{tikzpicture}
        \begin{axis}[
                scatter, only marks,
                scatter src=explicit symbolic,
                z buffer=sort
            ]
            \addplot3+[
                %scatter/classes={ a={mark=square*, red}, b={mark=square*, blue}}, % this line fails
                visualization depends on={\thisrow{x}\as\x},
                visualization depends on={\thisrow{y}\as\y},
                visualization depends on={\thisrow{z}\as\z},
                visualization depends on={value \thisrowno{4} \as \label},
                scatter/@pre marker code/.code={
                \def\markopts{
                    mark size=(\x+\y)/3+2,%
                    % color=red if \label == a else blue, % I don't know how to handle this
                    opacity=\z
                    }
                    \expandafter\scope\expandafter[\markopts]
                },
                scatter/@post marker code/.code={
                    \endscope
                },
            ] table[x=x, y=y, z=z, meta=label, col sep=space] {
                x   y   z   label
                0   0   0.1 a
                0   1   0.5 b
                1.2 0   1.0 a
                2   5   0.7 a
            };
        \end{axis}
    \end{tikzpicture}
\end{document}

I assume the scatter/classes={} line cannot work because the macros @pre and @post are re-defined, but then, how to handle the string comparison for each classes ?

1

Your \thisrowno{4} is incorrect, because column indexing in pgfplots table input starts from 0. So, \thisrowno{3} would work, but I prefer using \thisrow{label} as this is more readable and allows you to later insert columns in your table without breaking the code.

Other than that, once you have the label in macro \myLabel, you can use an expl3 function such as \str_case_e:nnF to implement a switch-case string comparison with fallback:

\ExplSyntaxOn
% Like \let, but errors out if \strCase is already defined
\cs_new_eq:NN \strCase \str_case_e:nnF
\ExplSyntaxOff

...

\def\markopts{
  mark size=(\x+\y)/3+2,
  color/.expanded={\strCase{\myLabel}{{a}{red}
                                      {b}{blue}}{green}},
  ...
}

Here, green is the fallback value used in case the label of a point is neither a nor b. You can add as many cases as you want. Please note that \strCase works inside color/.expanded={...} because it is an alias for expl3's \str_case_e:nnF function, which is fully expandable (this fact is documented in interface3.pdf, where there is a full star next to the macro name in the margin).

First example

Here is a complete code that programmatically sets the marker color in a label-dependent way. If you have several style elements to set for each label type, I suggest a slightly different technique for more readability—see the second example below.

\documentclass[tikz, border=3mm]{standalone}
\usepackage{expl3}
\usepackage{pgfplots}
\pgfplotsset{width=7cm, compat=1.15}

\ExplSyntaxOn
% Like \let, but errors out if \strCase is already defined
\cs_new_eq:NN \strCase \str_case_e:nnF
\ExplSyntaxOff

\begin{document}
    \begin{tikzpicture}
        \begin{axis}[z buffer=sort]
            \addplot3+[
                scatter, only marks,
                scatter src=explicit symbolic,
                visualization depends on={\thisrow{x}\as\x},
                visualization depends on={\thisrow{y}\as\y},
                visualization depends on={\thisrow{z}\as\z},
                visualization depends on={value \thisrow{label} \as \myLabel},
                scatter/@pre marker code/.code={%
                  \def\markopts{%
                    mark size=(\x+\y)/3+2,
                    color/.expanded={\strCase{\myLabel}{{a}{red}
                                                        {b}{blue}}{green}},
                    opacity=\z,
                  }%
                  \expandafter\scope\expandafter[\markopts]
                },
                scatter/@post marker code/.code={
                  \endscope
                },
            ] table[x=x, y=y, z=z, meta=label, col sep=space] {
                x   y   z   label
                0   0   0.1 a
                0   1   0.5 b
                1.2 0   1.0 a
                2   5   0.7 a
            };
        \end{axis}
    \end{tikzpicture}
\end{document}

screenshot

Note: In this particular case, if the expl3 version on your system is too old to have \str_case_e:nnF, the \cs_new_eq:NN \strCase \str_case_e:nnF line can be replaced with:

\cs_generate_variant:Nn \str_case:nnF { V }
\cs_new_eq:NN \strCase \str_case:VnF

(\str_case:VnF is not equivalent to \str_case_e:nnF, but it is adequate for our needs here).

Second example

In this second example, we want to set several mark style attributes (PGF keys) in a label-dependent way. This is possible using the previous technique, but would lead to a lot of redundancy:

\def\markopts{
    mark size=(\x+\y)/3+2,
    color/.expanded={\strCase{\myLabel}{{a}{red}
                                        {b}{blue}}{green}},
    mark/.expanded={\strCase{\myLabel}{{a}{triangle*}
                                        {b}{square*}}{x}},
    ...,
}

So, I propose a slightly different technique that allows one to set several PGF keys at once when dealing with each case:

\documentclass[tikz, border=3mm]{standalone}
\usepackage{expl3}
\usepackage{pgfplots}
\pgfplotsset{width=7cm, compat=1.15}

\ExplSyntaxOn
\cs_generate_variant:Nn \str_case:nnF { V }
\cs_new_eq:NN \strCase \str_case:VnF
\ExplSyntaxOff

\begin{document}
    \begin{tikzpicture}
        \begin{axis}[z buffer=sort]
            \addplot3+[
                scatter, only marks,
                scatter src=explicit symbolic,
                visualization depends on={\thisrow{x}\as\x},
                visualization depends on={\thisrow{y}\as\y},
                visualization depends on={\thisrow{z}\as\z},
                visualization depends on={value \thisrow{label} \as \myLabel},
                scatter/@pre marker code/.code={%
                  \edef\markopts{%
                    \strCase{\myLabel}
                      {
                        {a}{mark=triangle*, color=red}
                        {b}{mark=square*, color=blue}
                      }
                      {mark=x, color=green}, % fallback
                    mark size=(\x+\y)/3+2,
                    opacity=\z,
                  }%
                  \expandafter\scope\expandafter[\markopts]
                },
                scatter/@post marker code/.code={
                  \endscope
                },
            ] table[x=x, y=y, z=z, meta=label, col sep=space] {
                x   y   z   label
                0   0   0.1 a
                0   1   0.5 b
                1.2 0   1.0 a
                2   5   0.7 a
            };
        \end{axis}
    \end{tikzpicture}
\end{document}

screenshot

In case you have macros (“sensitive material”) in the label-dependent code that musn't be expanded too early (namely, at the time where \strCase is itself expanded), you can protect the sensitive material from the expansion performed by \edef using either \noexpand or \unexpanded, for instance:

\strCase{\myLabel}
  {
    {a}{mark=triangle*, \unexpanded{〈sensitive balanced text〉}}
    {b}{mark=square*, foo=\noexpand〈sensitiveToken〉}
  }
  {mark=x, color=green},
\unexpanded{%
  foo=bar,
  baz=quux,
}%

Here, 〈sensitiveToken〉 could be a macro or an active character, whereas 〈sensitive balanced text〉 can consist of arbitrarily many tokens.

  • Thanks very mutch for your answer. Unfortunately, your code snipped doesn't compile for me: Undefined control sequence. <argument> \strCase. My version of latex: Latexmk: This is Latexmk, John Collins, 1 January 2015, version: 4.41. My version of pdfTex: pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Debian) (preloaded format=pdflatex) – Jav Jan 17 at 13:44
  • 1
    Presumably, you omitted \usepackage{expl3} or \ExplSyntaxOn \cs_new_eq:NN \strCase \str_case_e:nnF \ExplSyntaxOff, or both. This is where \strCase is defined. If your expl3 is too old to have \str_case_e:nnF, you can use this instead (here): \ExplSyntaxOn \cs_generate_variant:Nn \str_case:nnF { V } \cs_new_eq:NN \strCase \str_case:VnF \ExplSyntaxOff. – frougon Jan 17 at 13:46
  • If this fails, post the full LaTeX output, e.g. on pastebin.com. – frougon Jan 17 at 13:52
  • The issue was probably expl3 version because your second strCase definition works. Thanks ! – Jav Jan 17 at 13:53
  • 1
    I added a better way to set several PGF keys in each case for the label. I'd suggest you read the latest version of the answer (I also removed the scatter/classes={a={mark=square*, red}, b={mark=square*, blue}} which wasn't useful anymore). – frougon Jan 17 at 16:08

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.