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

Popular posts from this blog

plotting - Filling between two spheres in SphericalPlot3D

Manipulate[ SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, Mesh -> None, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], {n, 0, 1}] I cant' seem to be able to make a filling between two spheres. I've already tried the obvious Filling -> {1 -> {2}} but Mathematica doesn't seem to like that option. Is there any easy way around this or ... Answer There is no built-in filling in SphericalPlot3D . One option is to use ParametricPlot3D to draw the surfaces between the two shells: Manipulate[ Show[SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], ParametricPlot3D[{ r {Sin[t] Cos[1.5 Pi], Sin[t] Sin[1.5 Pi], Cos[t]}, r {Sin[t] Cos[0 Pi], Sin[t] Sin[0 Pi], Cos[t]}}, {r, 1, 2 - n}, {t, 0, Pi}, PlotStyle -> Yellow, Mesh -> {2, 15}]], {n, 0, 1}]

plotting - Plot 4D data with color as 4th dimension

I have a list of 4D data (x position, y position, amplitude, wavelength). I want to plot x, y, and amplitude on a 3D plot and have the color of the points correspond to the wavelength. I have seen many examples using functions to define color but my wavelength cannot be expressed by an analytic function. Is there a simple way to do this? Answer Here a another possible way to visualize 4D data: data = Flatten[Table[{x, y, x^2 + y^2, Sin[x - y]}, {x, -Pi, Pi,Pi/10}, {y,-Pi,Pi, Pi/10}], 1]; You can use the function Point along with VertexColors . Now the points are places using the first three elements and the color is determined by the fourth. In this case I used Hue, but you can use whatever you prefer. Graphics3D[ Point[data[[All, 1 ;; 3]], VertexColors -> Hue /@ data[[All, 4]]], Axes -> True, BoxRatios -> {1, 1, 1/GoldenRatio}]

plotting - Adding a thick curve to a regionplot

Suppose we have the following simple RegionPlot: f[x_] := 1 - x^2 g[x_] := 1 - 0.5 x^2 RegionPlot[{y < f[x], f[x] < y < g[x], y > g[x]}, {x, 0, 2}, {y, 0, 2}] Now I'm trying to change the curve defined by $y=g[x]$ into a thick black curve, while leaving all other boundaries in the plot unchanged. I've tried adding the region $y=g[x]$ and playing with the plotstyle, which didn't work, and I've tried BoundaryStyle, which changed all the boundaries in the plot. Now I'm kinda out of ideas... Any help would be appreciated! Answer With f[x_] := 1 - x^2 g[x_] := 1 - 0.5 x^2 You can use Epilog to add the thick line: RegionPlot[{y < f[x], f[x] < y < g[x], y > g[x]}, {x, 0, 2}, {y, 0, 2}, PlotPoints -> 50, Epilog -> (Plot[g[x], {x, 0, 2}, PlotStyle -> {Black, Thick}][[1]]), PlotStyle -> {Directive[Yellow, Opacity[0.4]], Directive[Pink, Opacity[0.4]],