У меня есть несколько таблиц, которые я хочу прочитать и построить, каждая из них в своем стиле. Для удобства я люблю использовать те же теги. Вот 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}
Могу ли я добиться того же результата, используя \pgfplotsforeachinvoke
или \foreach
? Что-то вроде
\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}
Конечно, в этом простом случае я мог бы просто использовать
\addplot[#1] table[x=x,y=y] {#1.dat};
но иногда имя файла не соответствует шаблону, а иногда мне просто нужно прочитать и сохранить таблицу, чтобы изменить ее или использовать повторно несколько раз.
решение1
Добро пожаловать в TeX.SE! Всегда возможно одно:
написание макроса, который собирает список токенов нужным вам образом (в данном случае объединяет ряд
\addplot
команд с соответствующими параметрами);затем используется второй макрос (в моем коде, созданный из второго аргумента
\foreachTable
), который выводит что-то вроде\begin{axis}[...]#1\end{axis}
, с#1
заменой на ранее собранный список токенов, содержащий все\addplot
команды.
Эта техникавсегда работает(после раскрытия второго макроса входной поток TeX находится в том же состоянии, как если бы вы ввели весь код вручную). Поэтому вы можете использовать его для программного создания таблиц, изображений и всего, что захотите.
Полный код:
\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}
Объяснение вызова:
\foreachTable{tabA, tabB}
{
\begin{axis}[legend, legend pos=south east]
#1
\end{axis}
}
{ \addplot[#1] table[x=x,y=y] {\myTable}; \addlegendentry{#1} }
Первый аргумент — список записей (каждая запись приводит к одной \addplot
команде).
Второй аргумент — это то, что будет вставлено после того, как #1
внутри него будут заменены автоматически сгенерированные \addplot
команды.
Третий аргумент указывает код для каждого автоматически сгенерированного графика с некоторыми удобными заменами:
#1
заменяется на имя записи (здесь:tabA
thentabB
);\myTable
заменен токеном управляющей последовательности, созданным из имени записи (здесь:\tabA
для первой записи,\tabB
для второй).
Если вы хотите вручную добавить больше графиков (в данном случае один до и один после автоматически сгенерированных), вы можете, например, сделать следующее:
\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} }
Для людей, у которых нет\regex_replace_all:nnN
Если вы l3kernel
слишком стары, чтобы иметь \regex_replace_all:nnN
, вы можете:
добавить
\cs_generate_variant:Nn \tl_replace_all:Nnn { Nno }
перед\cs_new_protected:Npn \millo_foreach_table_do_axis:nNn #1#2#3
;заменить строку
\regex_replace_all:nnN { \c{myTable} } { \c{##1} } \l_tmpa_tl
с
\exp_args:NNno \tl_replace_all:Nno \l_tmpa_tl { \myTable } { \use:c {##1} }
Тогда это должно сработать.при условиичто вы не используете \myTable
внутри скобок. Например, используйте
\foreachTable{tabA, tabB}
{
...
}
{ \addplot[#1] table[x=x,y=y] \myTable; \addlegendentry{#1} }
вместо:
\foreachTable{tabA, tabB}
{
...
}
{ \addplot[#1] table[x=x,y=y] {\myTable}; \addlegendentry{#1} }