Skip to main content

plotting - Labeling plots without evaluation


This is my first question here so please excuse my mistakes.


Let us consider a rather contrived example:


f[x_, y_, z_] := x y z
{y, z} = {1, 1};

Plot[f[x, y, z], {x, -1, 1}, PlotLabel -> f[x, y, z]]
Plot[f[x, y, z], {x, -1, 1}, PlotLabel -> Subscript[f, x, y, z]]
Manipulate[Plot[f[x, y, z], {x, -1, 1}, PlotLabel -> f[x, y, z]], {y, 0, 1}, {z, 0, 1}]

Only PlotLabel is used here, but I am making the same argument for AxesLabel, Epilog -> Inset[], and other ways of labeling plots. The problem here is that the evaluator in Mathematica eagerly replaces all occurrences of f, y and z by their values, so instead of $f(x,y,z)$ in the plot one sees $x$. There are several solutions:



  1. Different variables, say ff, yy and zz, can be used in the code, leaving the symbols f, y and z free for labeling. However, this makes the code much more incomprehensible.

  2. Labels can be enclosed in quotes, for example, "f[x, y, z]". This works with Subscript[f, x, y, z] but the formatting is wrong for f[x, y, z] (variables are not italicized and brackets appear in lieu of parentheses).

  3. Labels can be enclosed in HoldForm or Block, for example, HoldForm[f[x, y, z]] or Block[{f, x, y, z}, f[x, y, z]]. This does not work for Manipulate, presumably because it defines its own local variables.

  4. Type such monstrosity as \!\(\*FormBox[SubscriptBox[\(f\), \(x, y, z\)], TraditionalForm]\) directly in the code.



I wonder if there is a simple way to tell Mathematica to use the expression f[x,y,z] as-is, with formatting but without evaluation. It would even be better if I can tell it to, say, replace only y with its current value in Manipulate but leave z untouched.


As an additional question, it is sometimes nice to label a condition on the plot, such as $y=1$. I can get away with


Plot[f[x, y, z], {x, -1, 1}, PlotLabel -> HoldForm[y] == y]
Clear[y]
Manipulate[
Plot[f[x, y, z], {x, -1, 1}, PlotLabel -> Symbol["y"] == y], {y, 0,
1}, {z, 0, 1}]

but this seems awfully complicated and inconsistent. Perhaps I can again tell Mathematica to skip evaluating Equal, and treat it as a given expression?



Edit


Based on the discussion below I have summarized several ad-hoc strategies to deal with labels:




  • Define a label via



    l = {HoldForm[x], HoldForm@f[x, y, z]}
    l = StringForm["Plot of ``", HoldForm[Subscript[f, x, y, z]]]
    l = HoldForm[y == #1 \[And] z == #2] &


    in global scope, before local variables creep in. These should be shielded against all global and local definitions of the variables. (The last label should be used as PlotLabel -> l[y, z].)




  • Wrap Plot or Manipulate with a Module and define local labels in the same way. For example,



    Module[{l = HoldForm@f[x, y, z]},
    Manipulate[
    Plot[f[x, y, z], {x, -1, 1}, PlotLabel -> l], {y, 0, 1}, {z, 0, 1},
    Initialization :> (f[x_, y_, z_] := x y z)]]


    This is okay as long as the label is defined outside of the scope in which f, y and z are actually used. But if the plotting code is to be encapsulated in a function plot[f_] := ..., then this approach fails if the label involves f and f is declared globally as a pure function, such as f = #1 #2 #3 &. In that case, one may consider plot[fn_] := ....




  • If f is to be displayed unevaluated, such as $f(x,y,0.12)$, use



    Manipulate[
    Plot[f[x, y, z], {x, -1, 1},
    PlotLabel ->
    With[{y = Symbol["y"], z = z}, HoldForm@f[x, y, z]]], {y, 0,
    1}, {z, 0, 1}, Initialization :> (f[x_, y_, z_] := x y z)]


    y is displayed as-is, and it is wrapped in Symbol so that it is properly formatted; but y must not already be defined globally, otherwise that global value will show. z shows its current value, set probably through a Manipulate.




  • If f is to be displayed in evaluated form, such as $x\times y\times0.12$, use



    Manipulate[
    Plot[ReleaseHold@f[x, y, z], {x, -1, 1},
    PlotLabel -> With[{y = Symbol["y"], z = z}, f[x, y, z]]], {y, 0,
    1}, {z, 0, 1}, Initialization :> (f[x_, y_, z_] := HoldForm[x y z])]


    However, it is hard to guarantee all plotting functions are wrapped in HoldForm.




  • If any of the preserved variables (x and y) are already defined globally, it is possible to guard against them with With[{x = "x", y = "y"}, ...] at the cost of x and y not being properly formatted and the risk of wrong variable ordering.




  • Nothing could be done if the variable is declared in the same scope as the label is used. HoldForm[x] will display its decorated name.





In short, there is no one single solution that works in every case except global-variable injection.



Answer



I recommend not assigning values to y and z globally. I further recommend making your plot as follows:


f[x_,y_,z_] := x y z
Plot[f[x, 1, 1],{x, -1, 1}, PlotLabel -> HoldForm@f[x, 1, 1]]

I use the label f[x,1,1] because it more truly represents what you are plotting.


enter image description here


Edit


In the case where the Plot is evaluated inside a Manipulate expression, HoldForm can still be used, but must evaluated outside the Manipulate.



lbl = HoldForm@f[x, y, z];
Manipulate[Plot[f[x, y, z],{x, -1, 1}, PlotLabel->lbl],
{y, 0, 1},
{z, 0, 1},
Initialization:>(f[x_, y_, z_] := x y z;)]

Comments