11
$\begingroup$

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.

$\endgroup$
3
  • $\begingroup$ You can get it to replace (and update) inside the Manipulate using Dynamic as PlotLabel -> HoldForm@f[x, Dynamic@y, Dynamic@z]. I'm not sure how to not evaluate the z and not have it be FE`z$123 other than the ways you've already mentioned. $\endgroup$
    – rm -rf
    Commented Oct 31, 2012 at 3:18
  • $\begingroup$ Welcome to Mathematica.SE! I suggest the following: 1) As you receive help, try to give it too, by answering questions in your area of expertise. 2)Read the FAQs! 3) When you see good Q&A, vote them up by clicking the gray triangles, because the credibility of the system is based on the reputation gained by users sharing their knowledge. ALSO, remember to accept the answer, if any, that solves your problem, by clicking the checkmark sign $\endgroup$
    – chris
    Commented Oct 31, 2012 at 19:47
  • $\begingroup$ Closely related: Using a function name instead of its definition in AxesLabel $\endgroup$
    – Mr.Wizard
    Commented May 3, 2014 at 5:58

3 Answers 3

6
$\begingroup$

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;)]
$\endgroup$
7
  • 1
    $\begingroup$ Global assignment is more an illustration than actual practice. Consider Manipulate[Plot[x y, {x, -1, 1}, PlotLabel -> x y], {y, 0, 1}]; here y is local but I still have a similar problem. I think whether the explicit appearance of the value of y is clearer depends on the subject matter at hand. Maybe for the sake of clarity I want to label the plot $y>0$ (or perhaps $y>z$, with the value of $z$ substituted in), but Mathematica usually gives me either True or False for a title. $\endgroup$ Commented Oct 31, 2012 at 5:43
  • $\begingroup$ Using HoldForm with PlotLabel is still the way to go. $\endgroup$
    – m_goldberg
    Commented Oct 31, 2012 at 5:55
  • $\begingroup$ As I noted in the question, HoldForm works except in Manipulate, in which case it gives me the decorated name of the local variable. Unfortunately I do need Manipulate. If one needs partial substitution, one needs to put a HoldForm around each preserved variable, which is not altogether elegant either. $\endgroup$ Commented Oct 31, 2012 at 13:52
  • 1
    $\begingroup$ @photon.engine Even in Manipulate, HoldForm is the way to go. See my edit to my answer. $\endgroup$
    – m_goldberg
    Commented Oct 31, 2012 at 15:55
  • $\begingroup$ I think @photon refers to the case when f is local to the Manipulate, e.g. is entered by the user in an InputField. There are some trobules... $\endgroup$ Commented Oct 31, 2012 at 16:13
3
$\begingroup$

A more general approach specifies the level of "unevaluatedness". If we have the function:

f[x_, y_, z_] := x + y + z;

and we have some local values for y and z inside e.g. a Manipulate, there are at least 6 levels to keep f unevaluated:

  • do not evaluate anything, and keep local symbol names: f[x, FE`y$$182, FE`z$$182] (or similar)
  • do not evaluate anything: f[x, y, z]
  • evaluate y and z: f[x, 1, 3]
  • evaluate only f: x + y + z
  • evaluate f, y and z: x + 1 + 3
  • evaluate f, y, z and Plus: 4 + x

Accordingly:

Manipulate[
 Column@{
   HoldForm@f[x, y, z],
   With[{y = "y", z = "z"}, HoldForm@f[x, y, z]],
   With[{y = y, z = z}, HoldForm@f[x, y, z]],
   With[{x = "x", y = "y", z = "z"}, f[x, y, z]],(* x must be specified, otherwise order would be: "y" + "z" + x  *)
   ReleaseHold@Block[{x, y, z}, Hold@f[x, y, z]],
   Framed@Plot[f[x, y, z], {x, 0, 10}, PlotLabel -> ReleaseHold@Block[{x, y, z}, Hold@f[x, y, z]]]
   },
 {y, 1, 2},
 {z, 3, 4},
 Initialization :> (f[x_, y_, z_] := x + y + z)]

Mathematica graphics

Note, that holding Plus from evaluation can only be done if the righthand side of the definition of f is wrapped in Hold or HoldForm, which requires some extra ReleaseHold, especially in the first argument of Plot:

Manipulate[
 Column@{
   HoldForm@f[x, y, z],
   With[{y = "y", z = "z"}, HoldForm@f[x, y, z]],
   With[{y = y, z = z}, HoldForm@f[x, y, z]],
   ReleaseHold@With[{x = "x", y = "y", z = "z"}, f[x, y, z]],
   f[x, y, z],
   ReleaseHold@f[x, y, z],
   Framed@Plot[ReleaseHold@f[x, y, z], {x, 0, 10}, PlotLabel -> f[x, y, z]]
   },
 {y, 1, 2},
 {z, 3, 4},
 Initialization :> (f[x_, y_, z_] := HoldForm[x + y + z])]

Mathematica graphics

(It's nice to know that ReleaseHold works on HoldForm too.)

$\endgroup$
1
  • 2
    $\begingroup$ I would improve on your solution by With[{y = Symbol["y"], z = z}, HoldForm@f[x, y, z]] and With[{x = "x", y = Symbol["y"], z = z}, f[x, y, z]]}]. The first one gives $f(x,y,0.12)$ whereas the second one gives $\mathrm{x}+y+0.12$. x = "x" guards against any globally defined value of x, but unfortunately prevents it from being formatted. It would be rather inconvenient to wrap all functions in HoldForm though. $\endgroup$ Commented Oct 31, 2012 at 20:37
2
$\begingroup$

I am not sure I understood the question well. If this does not answer it, please let me know and will delete it.

But formating labels, titles, plot/graphics related labels and such, takes me long time to do it right and is not always easy. If you mean you want to format the label as f(x,number,number) , then using Row[] and manually format it is how I would do it,something like

Manipulate[
Plot[f[x,y,z],{x,-1,1},PlotLabel->Row[{Style["f",Italic],"(",x,",",y,",",z,")"}] ],
{y,0,1},
{z,0,1},
Initialization:>
(
f[x_,y_,z_]:=x y z;
)]

enter image description here

For you follow up question: 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

Again, I would do using Row[] like this

Manipulate[
Plot[f[x,y,z],{x,-1,1},PlotLabel->Row[{Style["f",Italic],
           "(",Style["x",Italic],",",Style["y",Italic],",",Style["z",Italic],")"}] ],
{y,0,1},
{z,0,1},
Initialization:>
(
f[x_,y_,z_]:=x y z;
)]

enter image description here

And at this time I do not yet understand your last question (the additional one). (May be you can post that as a separate question?).

$\endgroup$
1
  • $\begingroup$ Thanks! But the problem I have is that Mathematica is too quick at replacing my variables, whereas I would like my labels to stay as $f(x,y,z)$ which is clearer. You can see in my Manipulate example that both y and z show what their actual values are (instead of just $y$ and $z$). I do agree your second method works but it seems too much typing for something as simple as $f(x,y,z)$ ... maybe there is a general algorithm for generating such expressions? In addition, the second method seems to force the variables to be italic instead of using Mathematica's own formatting system. $\endgroup$ Commented Oct 31, 2012 at 5:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.