3

I want to pass a float variable from a pythontex calculation to latex such that the variable can be used in a pgfplots plot as in the following example. The example code shows two approaches that don't work. The syntax for passing should be as short and convenient as possible.

\documentclass{article}

\usepackage[gobble=auto]{pythontex}
\usepackage{pgfplots}

\begin{document}

\begin{pycode}
from numpy import * 
coeffA = 7
coeffB = sqrt(coeffA)  
# print(r'\def\coeffA{%s}' %coeffA)
# print(r'\def\coeffB{%s}' %coeffB)
\end{pycode}



\begin{tikzpicture}
  \begin{axis}[xlabel=$x$,ylabel=$y$]
    %\addplot gnuplot {\coeffA*x**2 + \coeffB*x}; % Results in undefined control sequence error
    \addplot gnuplot {\py{coeffA}*x**2 + \py{coeffB}*x}; %Results in an empty plot without errors
  \end{axis} 
\end{tikzpicture}


\end{document}
Julia
  • 1,648
  • 1
    For a compact approach designed for this sort of situation, see the pysub example in https://tex.stackexchange.com/a/65294/10742. – G. Poore Oct 19 '17 at 11:33

2 Answers2

3

I can see a few issues here:

  1. You can't use \py{} directly inside \addplot gnuplot because it isn't fully expandable, so gnuplot gets literally \py{coeffA} as its input and not the value of the coefficient.

  2. The idea of defining \coeffA and \coeffB macros is good, but when you put the definitions inside the pycode environment they don't work since pycode doesn't typeset its contents (it doesn't return anything to LaTeX).

  3. Even if you move the defs further, they won't appear in the document on the first run, so you'll have to do something about it. In the code below I just define them as some dummy values just to avoid the error report.

To make things easier to use, I've added some code which automatically iterates through all global numeric variables and creates corresponding LaTeX macros. Then their usage in the main document becomes as easy as \pyvar{varname}.

The code:

\documentclass{article}

\usepackage[gobble=auto]{pythontex}
\usepackage{pgfplots}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\def\pyvar#1{\csname pythonvar@#1\endcsname}
\begin{pycode}
from numbers import Number
def exposeNumericGlobals():
  res = r'\makeatletter '

  d = globals()
  for key in d.keys():
    val = d[key]
    if isinstance(val, Number):
      res = res + (r'\gdef\pythonvar@%s{%s} ' % (key, val))

  res = res + r'\makeatother'
  return res
\end{pycode}
\AtBeginDocument{\py{exposeNumericGlobals()}}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{pycode}
from numpy import * 
coeffA = 7
coeffB = sqrt(coeffA)
\end{pycode}

\begin{document}

\begin{tikzpicture}
  \begin{axis}[xlabel=$x$,ylabel=$y$]
    \addplot gnuplot {\pyvar{coeffA}*x**2 + \pyvar{coeffB}*x};
  \end{axis} 
\end{tikzpicture}
\end{document}

The output:

enter image description here

  • Thanks. It works. But the solution is pretty complicated for such a principially simple task. In particular that you have to define for the first run befor begin{document}. – Julia Oct 19 '17 at 07:53
  • You're right. It's a bit too complicated. So I've separated the complicated part into a non-changing section. Now you just define a numeric global variable and use \pyvar{var}. The variable definition has to be before \begin{document} though. – Sergei Golovan Oct 19 '17 at 18:17
3

Following @G.Poore's comment you can use pysub (beginning from version v0.15) as follows:

\documentclass{article}

\usepackage[gobble=auto]{pythontex}
\usepackage{pgfplots}

\begin{document}

\begin{pycode}
from numpy import * 
coeffA = 7
coeffB = sqrt(coeffA)  
\end{pycode}   

\begin{pysub}
\begin{tikzpicture}
  \begin{axis}[xlabel=$x$,ylabel=$y$]
    \addplot gnuplot {!{coeffA}*x**2 + !{coeffB}*x}; 
  \end{axis} 
\end{tikzpicture}
\end{pysub}

\end{document}
student
  • 29,003