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

list manipulation - Selecting multiple columns from a matrix?

Sample data: data = { {{2013, 1, 1}, 24.13, 167.67, 231.82}, {{2013, 1, 2}, 32.15, 170.92, 225.99}, {{2013, 1, 3}, 35.43, 172.68, 221.67}, {{2013, 1, 4}, 36.73, 173.05, 218.32}, {{2013, 1, 5}, 58.19, 165.96, 197.05}, {{2013, 1, 6}, 69.99, 163.50, 187.52}, {{2013, 1, 7}, 71.37, 154.21, 175.58}, {{2013, 1, 8}, 72.51, 149.66, 163.25}}; I want a DateListPlot with three graphs, so for a matrix formed by columns 1 and 2, one for columns 1 and 3, and 1 for columns 1 and 4. At the moment I'm using this code: data2 = Transpose[{data[[All, 1]], data[[All, 2]]}]; data3 = Transpose[{data[[All, 1]], data[[All, 3]]}]; data4 = Transpose[{data[[All, 1]], data[[All, 4]]}]; DateListPlot[{data2, data3, data4}, Joined -> True, Filling -> {3 -> {1}}] but I have a hunch that this can be done more efficiently. I don't like the Transpose s in particular. Any ideas? edit (for extra credit) What if I need to multiply the second column by 2, which in my solution is simp...

plotting - How to draw lines between specified dots on ListPlot?

I would like to create a plot where I have unconnected dots and some connected. So far, I have figured out how to draw the dots. My code is the following: ListPlot[{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4,13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full] I have thought using ListLinePlot command, but I don't know how to specify to the command to draw only selected lines between the dots. Do have any suggestions/hints on how to do that? Thank you. Answer One possibility would be to use Epilog with Line : ListPlot[ {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4, 13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full, Epilog -> { Line[ ...

dynamic - How can I make a clickable ArrayPlot that returns input?

I would like to create a dynamic ArrayPlot so that the rectangles, when clicked, provide the input. Can I use ArrayPlot for this? Or is there something else I should have to use? Answer ArrayPlot is much more than just a simple array like Grid : it represents a ranged 2D dataset, and its visualization can be finetuned by options like DataReversed and DataRange . These features make it quite complicated to reproduce the same layout and order with Grid . Here I offer AnnotatedArrayPlot which comes in handy when your dataset is more than just a flat 2D array. The dynamic interface allows highlighting individual cells and possibly interacting with them. AnnotatedArrayPlot works the same way as ArrayPlot and accepts the same options plus Enabled , HighlightCoordinates , HighlightStyle and HighlightElementFunction . data = {{Missing["HasSomeMoreData"], GrayLevel[ 1], {RGBColor[0, 1, 1], RGBColor[0, 0, 1], GrayLevel[1]}, RGBColor[0, 1, 0]}, {GrayLevel[0], GrayLevel...