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:
- Different variables, say
ff
,yy
andzz
, can be used in the code, leaving the symbolsf
,y
andz
free for labeling. However, this makes the code much more incomprehensible. - Labels can be enclosed in quotes, for example,
"f[x, y, z]"
. This works withSubscript[f, x, y, z]
but the formatting is wrong forf[x, y, z]
(variables are not italicized and brackets appear in lieu of parentheses). - Labels can be enclosed in
HoldForm
orBlock
, for example,HoldForm[f[x, y, z]]
orBlock[{f, x, y, z}, f[x, y, z]]
. This does not work forManipulate
, presumably because it defines its own local variables. - 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
orManipulate
with aModule
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
andz
are actually used. But if the plotting code is to be encapsulated in a functionplot[f_] := ...
, then this approach fails if the label involvesf
andf
is declared globally as a pure function, such asf = #1 #2 #3 &
. In that case, one may considerplot[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 inSymbol
so that it is properly formatted; buty
must not already be defined globally, otherwise that global value will show.z
shows its current value, set probably through aManipulate
.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
andy
) are already defined globally, it is possible to guard against them withWith[{x = "x", y = "y"}, ...]
at the cost ofx
andy
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.
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
Post a Comment