17
$\begingroup$

Consider the example

plotOptions = {PlotPoints -> 10, MaxRecursion -> 2}

Plot3D[x y , {x, 0, 10}, {y, 0, 10}, plotOptions]

Why does this causes the error:

Plot3D::nonopt: Options expected (instead of plotOptions) beyond position 3 in 
Plot3D[x y,{x,0,10},{y,0,10},plotOptions]. An option must be a rule or a list of rules. >>

?

I would say that plotOptions is as list of rules...

If I use

Plot3D[x y , {x, 0, 10}, {y, 0, 10}, {PlotPoints -> 10, MaxRecursion -> 2}]

it works:

enter image description here

What is then the correct way of passing a list of options to a plot? One situation on when this happens is when I want to create several plots with the same options.

$\endgroup$
  • 8
    $\begingroup$ Try Plot3D[x y, {x, 0, 10}, {y, 0, 10},Evaluate@plotOptions] its because of the HoldAll attribute of Plot functions! $\endgroup$ – PlatoManiac Feb 11 '13 at 10:59
  • $\begingroup$ Pff, it works! why is that? Can you provide an answer and respective explanation please? $\endgroup$ – Jorge Leitao Feb 11 '13 at 11:01
  • 1
    $\begingroup$ The answer is that Plot3D has HoldAll attribute. I'll let @PlatoManiac post the answer. $\endgroup$ – Ajasja Feb 11 '13 at 11:03
  • $\begingroup$ This is a good candidate for an FAQ. It's something novices can all too easily be mystified by, before they know anything about Hold, etc. $\endgroup$ – murray Feb 11 '13 at 17:12
22
$\begingroup$

Lets see the Attributes of Plot3D

Attributes[Plot3D]

{HoldAll, Protected, ReadProtected}

The HoldAll is the reason why your options are not read within the Plot3D command. In such situations (e.g NIntegrate, Plot,..) to resolve the values of a symbol (here options) within these commands we tend to use Evaluate to forcefully override this attribute.

So one solution is

Plot3D[x y, {x, 0, 10}, {y, 0, 10},Evaluate@plotOptions]

Another solution is to inject the options using With:

With[{iPlotOpts = plotOptions},
 Plot3D[x y, {x, 0, 10}, {y, 0, 10}, iPlotOpts]
]

Another way will be to get rid of this HoldAll globally using ClearAttributes[Plot3D, HoldAll]. Then you can just use Plot3D[x y , {x, 0, 10}, {y, 0, 10}, plotOptions]. But clearing attributes globally is a bad idea in general, as it disrupts the local scoping which is often a must for a function like Plot3D to work seamlessly! Try for example:

ClearAttributes[Plot3D, HoldAll]; x = 5; y = 10; 
Plot3D[x y, {x, 0, 10}, {y, 0, 10}]
$\endgroup$
  • 4
    $\begingroup$ The last paragraph is extremely bad advice! Try for example: ClearAttributes[Plot3D, HoldAll]; x = 5; y = 10; Plot3D[x y, {x, 0, 10}, {y, 0, 10}]. Another solution is to inject the plot options using With $\endgroup$ – Ajasja Feb 11 '13 at 12:12
  • $\begingroup$ @Ajasja While I was editing the answer the site became inaccessible (at least in Germany)! At that time I was just mentioning the bad side effect of ClearAttribute. Now I am back from lunch and did finish the editing ;) $\endgroup$ – PlatoManiac Feb 11 '13 at 12:40
  • $\begingroup$ I emphasised your warning just in case:) $\endgroup$ – Ajasja Feb 11 '13 at 12:56
  • $\begingroup$ Thanx @Ajasja... $\endgroup$ – PlatoManiac Feb 11 '13 at 13:01
8
$\begingroup$

There are other easy ways to inject the evaluated plotOptions into Plot3D. One very simple way is to use With. Here, the fundamental difference between With and Module comes to light: With does not localize variables. It assigns values to names and you can refer to your values by using the names:

With[{plotOptions = {PlotPoints -> 10, MaxRecursion -> 2}},
 Plot3D[x y, {x, 0, 10}, {y, 0, 10}, plotOptions]
]

Another way is to make a function from your Plot3D call and pass your options as parameters

Plot3D[x y, {x, 0, 10}, {y, 0, 10}, #] &[{PlotPoints -> 10, MaxRecursion -> 2}]

In this way, you can of course remove the list braces

Plot3D[x y, {x, 0, 10}, {y, 0, 10}, ##] &[PlotPoints -> 10, MaxRecursion -> 2]
$\endgroup$
7
$\begingroup$

I can't comment on halirutan's answer, so this will have to suffice: the problem with his last suggestion is that

Plot3D[x y, {x, 0, 10}, {y, 0, 10}, #] &[PlotPoints -> 10, MaxRecursion -> 2]

is equivalent to

Plot3D[x y, {x, 0, 10}, {y, 0, 10}, PlotPoints -> 10]

i.e., only the first option setting is inserted. The proper way to go about it is to use SlotSequence (##) instead; to wit,

Plot3D[x y, {x, 0, 10}, {y, 0, 10}, ##] &[PlotPoints -> 10, MaxRecursion -> 2]

and both options shall then be inserted.

If you have an entire pile of options to insert, you could do something like

optList = {ColorFunction -> Hue, MaxRecursion -> 2, Mesh -> False, PlotPoints -> 10};
Plot3D[x y, {x, 0, 10}, {y, 0, 10}, ##] & @@ optList
$\endgroup$
  • $\begingroup$ The first half of this answer would have been better as a comment (or edit) to halirutan's answer. I made the bug-fix edit myself. However, +1 for the final line of code as that is a valuable method. $\endgroup$ – Mr.Wizard Feb 26 '13 at 9:20
2
$\begingroup$

Another approach that may be useful for some is to use the optional arguments type of function. This allows you to set up a base style and then modify it when you use it without entering all the arguments.

This sets up a bunch of named options (in this case mainly duplicates of the normal plot options) and their default values:

imagesize = 300;;

Options[plot2dF] = {PlotRange -> All,
  Axes -> False, Frame -> True,
  FrameLabel -> None,
  FrameTicks -> Automatic,
  PlotStyle -> Automatic,
  PlotLegends -> None,
  ImageSize -> imagesize};

This then creates a function that returns a list of options:

plot2dF[OptionsPattern[]] :=
  {PlotRange -> OptionValue[PlotRange],
   Axes -> OptionValue[Axes],
   Frame -> OptionValue[Frame],
   FrameLabel -> OptionValue[FrameLabel],
   FrameTicks -> OptionValue[FrameTicks],
   PlotStyle -> OptionValue[PlotStyle],
   PlotLegends -> OptionValue[PlotLegends],
   ImageSize -> OptionValue[ImageSize]};

Which can then be used with evaluate as described in the other answers:

Plot[Sin[x], {x, 0, 2 \[Pi]}, 
 Evaluate[plot2dF[PlotStyle -> {Red, Dashed}]]]

Code output

The effect of this approach is very similar to just changing the default settings but it can be used in multiple plot scenarios (listplot, logpot etc) and you could have different versions of your function for different base settings.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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