第一个例子

第一个例子

如同这个问题,我想根据从表中读取的某一列的值来设置散点的样式(颜色/形状)。

\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}

我认为该scatter/classes={}行无法工作是因为宏@pre@post被重新定义,但是,如何处理每个类的字符串比较?

答案1

你的\thisrowno{4}做法是错误的,因为表输入中的列索引pgfplots从 0 开始。因此,\thisrowno{3}可以工作,但我更喜欢使用,\thisrow{label}因为它更具可读性,并且允许您稍后在表中插入列而不会破坏代码。

除此之外,一旦在宏中有了标签\myLabel,您就可以使用expl3诸如\str_case_e:nnF实现带回退的 switch-case字符串比较的函数:

\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}},
  ...
}

这里,green是当点的标签既不是a也不是时使用的后备值b。您可以根据需要添加任意数量的案例。请注意,\strCase在里面起作用,因为它是函数的color/.expanded={...}别名,即expl3\str_case_e:nnF完全可扩展(这一事实记录在界面3.pdf,其中在边缘处的宏名称旁边有一颗完整的星号)。

第一个例子

以下是以标签相关方式通过编程设置标记颜色的完整代码。如果您需要为每种标签类型设置多个样式元素,我建议使用略有不同的技术以提高可读性 - 请参阅下面的第二个示例。

\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}

截屏

注意:在这种特殊情况下,如果expl3您的系统上的版本太旧\str_case_e:nnF,则\cs_new_eq:NN \strCase \str_case_e:nnF可以将该行替换为:

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

\str_case:VnF不是相当于\str_case_e:nnF,但它已经足以满足我们的需要)。

第二个例子

在第二个示例中,我们想要以标签相关的方式设置多个标记样式属性(PGF 键)。使用之前的技术可以实现这一点,但会导致大量冗余:

\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}},
    ...,
}

因此,我提出了一种略有不同的技术,允许人们在处理每种情况时一次设置多个 PGF 键:

\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}

截屏

如果标签相关代码中存在不能过早扩展的宏(“敏感材料”)(即,在\strCase扩展时),则可以\edef使用或\noexpand来保护敏感材料免受扩展的\unexpanded影响,例如:

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

这里,〈sensitiveToken〉 可以是一个宏或者一个活动字符,而 〈sensitive equippedtext〉 可以由任意多个标记组成。

相关内容