Para pessoas que não têm\regex_replace_all:nnN

Para pessoas que não têm\regex_replace_all:nnN

Tenho algumas tabelas que quero ler e plotar, cada uma delas com seu estilo. Por uma questão de conveniência, gosto de usar as mesmas tags. Aqui está um MWE:

\documentclass{minimal}

\usepackage{pgfplotstable}

\begin{filecontents}{tabA.dat}
x   y
0   0
1   1
2   2
3   3
\end{filecontents}
\begin{filecontents}{tabB.dat}
x   y
0   1
1   2
2   3
3   4
\end{filecontents}

\pgfplotsset{compat=newest,
tabA/.style={color=red,mark=*},
tabB/.style={color=black,mark=o},
}

\pgfplotstableread{tabA.dat}\tabA
\pgfplotstableread{tabB.dat}\tabB

\begin{document}
    \begin{tikzpicture}
        \begin{axis}[legend,legend pos=south east]
            \addplot[tabA] table[x=x,y=y] {\tabA};\label{pgf:tabA}\addlegendentry{tabA}
            \addplot[tabB] table[x=x,y=y] {\tabB};\label{pgf:tabB}\addlegendentry{tabB}
        \end{axis}
    \end{tikzpicture}

\end{document}

Posso obter o mesmo resultado usando \pgfplotsforeachinvokeou \foreach? Algo como

\begin{tikzpicture}
    \begin{axis}[legend,legend pos=south east]
        \pgfplotsinvokeforeach{tabA,tabB}{%
            % The following doesn't work, of course
            \addplot[#1] table[x=x,y=y] {\#1}; % <- Magic goes here 
        }
    \end{axis}
\end{tikzpicture}

É claro que neste caso simples eu poderia usar de forma simples

\addplot[#1] table[x=x,y=y] {#1.dat};

mas às vezes o nome do arquivo não segue um padrão ou outras vezes quero apenas ler e armazenar uma tabela para modificá-la ou reutilizá-la várias vezes.

Responder1

Bem-vindo ao TeX.SE! Uma coisa é sempre possível:

  1. escrever uma macro que monte uma lista de tokens do jeito que você quiser (aqui, concatena uma série de \addplotcomandos com opções apropriadas);

  2. em seguida, usando uma segunda macro (no meu código, construída a partir do segundo argumento de \foreachTable) que gera algo como \begin{axis}[...]#1\end{axis}, #1substituída pela lista de tokens montada anteriormente que contém todos os \addplotcomandos.

Esta técnicasempre funciona(depois que a segunda macro for expandida, o fluxo de entrada do TeX estará exatamente no mesmo estado como se você tivesse inserido todo o código manualmente). Você pode, portanto, usá-lo para gerar programaticamente tabelas, imagens, o que quiser.

Código completo:

\begin{filecontents}{tabA.dat}
x   y
0   0
1   1
2   2
3   3
\end{filecontents}

\begin{filecontents}{tabB.dat}
x   y
0   1
1   2
2   3
3   4
\end{filecontents}

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

\ExplSyntaxOn
\seq_new:N \l__millo_plot_cmds_tl

\cs_new_protected:Npn \millo_foreach_table_do_axis:nNn #1#2#3
  {
    \tl_clear:N \l__millo_plot_cmds_tl
    \clist_map_inline:nn {#1}
      {
        \tl_set:Nn \l_tmpa_tl {#3}
        \regex_replace_all:nnN { \c{myTable} } { \c{##1} } \l_tmpa_tl
        \tl_put_right:NV \l__millo_plot_cmds_tl \l_tmpa_tl
      }

    \exp_args:No #2 \l__millo_plot_cmds_tl
  }

\NewDocumentCommand \foreachTable { m m m }
  {
    \cs_set_protected:Npn \__millo_axis_func:n ##1 {#2}
    \millo_foreach_table_do_axis:nNn {#1} \__millo_axis_func:n {#3}
  }
\ExplSyntaxOff

\pgfplotsset{
  tabA/.style={color=red,mark=*},
  tabB/.style={color=black,mark=o},
}

\pgfplotstableread{tabA.dat}\tabA
\pgfplotstableread{tabB.dat}\tabB

\begin{document}

\begin{tikzpicture}
  \foreachTable{tabA, tabB}
    {
      \begin{axis}[legend, legend pos=south east]
        #1
      \end{axis}
    }
    { \addplot[#1] table[x=x,y=y] {\myTable}; \addlegendentry{#1} }
\end{tikzpicture}

\end{document}

captura de tela

Explicação da chamada:

\foreachTable{tabA, tabB}
  {
    \begin{axis}[legend, legend pos=south east]
      #1
    \end{axis}
  }
  { \addplot[#1] table[x=x,y=y] {\myTable}; \addlegendentry{#1} }

O primeiro argumento é a lista de entradas (cada entrada leva a um \addplotcomando).

O segundo argumento é o que será inserido após o #1interior dele ter sido substituído pelos \addplotcomandos gerados automaticamente.

O terceiro argumento especifica o código para cada gráfico gerado automaticamente, com algumas substituições convenientes:

  • #1substituído pelo nome da entrada (aqui: tabAthen tabB);

  • \myTablesubstituído pelo token de sequência de controle construído a partir do nome da entrada (aqui: \tabApara a primeira entrada, \tabBpara a segunda).

Se quiser adicionar manualmente mais gráficos (aqui, um antes e outro depois dos gerados automaticamente), você pode, por exemplo, fazer:

\foreachTable{tabA, tabB}
  {
    \begin{axis}[legend, legend pos=south east]
      \addplot {sin(deg(\x))}; \addlegendentry{$\sin$}
      #1
      \addplot {sqrt(\x)};     \addlegendentry{$x\mapsto \sqrt{x}$}
    \end{axis}
  }
  { \addplot[#1] table[x=x,y=y] {\myTable}; \addlegendentry{#1} }

insira a descrição da imagem aqui

Para pessoas que não têm\regex_replace_all:nnN

Se você l3kernelfor muito velho para ter \regex_replace_all:nnN, você pode:

  • acrescentar \cs_generate_variant:Nn \tl_replace_all:Nnn { Nno }antes \cs_new_protected:Npn \millo_foreach_table_do_axis:nNn #1#2#3;

  • substitua a linha

    \regex_replace_all:nnN { \c{myTable} } { \c{##1} } \l_tmpa_tl
    

    com

    \exp_args:NNno
    \tl_replace_all:Nno \l_tmpa_tl { \myTable } { \use:c {##1} }
    

Então deve funcionarna condiçãoque você não usa \myTableaparelho interno. Por exemplo, use

\foreachTable{tabA, tabB}
  {
    ...
  }
  { \addplot[#1] table[x=x,y=y] \myTable; \addlegendentry{#1} }

em vez de:

\foreachTable{tabA, tabB}
  {
    ...
  }
  { \addplot[#1] table[x=x,y=y] {\myTable}; \addlegendentry{#1} }

informação relacionada