Create Functions to flexibly define Chart Options lists in Mathematica

别等时光非礼了梦想. 提交于 2019-12-21 05:49:06

问题


I am trying to create some functions that will avoid me to repeat plot options for different types of plot. I encounter some troubles as I try to create mechanisms to automatically handle Frameticks & Plot Range given the data considered within a given plot.

Module[{chartData},
        chartData = RandomInteger[20, 20];

BarChart[chartData,
                Frame -> {{True, True}, {True, True}},
                ImageSize -> 300,
                ChartStyle -> Black,
                FrameLabel -> {{"yName", None}, {None, "xName"}},
                ChartElementFunction -> "FadingRectangle",
                LabelStyle -> Directive[Black, Bold, 18],
                PlotRange -> {Automatic, 1.3*Max@chartData},
                FrameTicks -> {{{Round@Min@chartData, Round@(Max@chartData/2), 
                                Round@Max@chartData}, None}, 
                              {{0, Round@(Length@chartData/2), Length@chartData}, None}}
          ]]

Now Here is my attempt to simplify my life :

chartOptions[yName_, xName_] := {Frame -> {{True, True}, {True, True}},      
                                ImageSize -> 300,
                                ChartStyle -> Blue,
                                FrameLabel -> {{yName, None}, {None, xName}},
                                ChartElementFunction -> "FadingRectangle",
                                LabelStyle -> Directive[Black, Bold, 18],
                                FrameTicks -> {{{Round@Min@chartData, Round@(Max@chartData/2), 
                                                 Round@Max@chartData}, None}, 
                                              {{0,Round@(Length@chartData/2),     
                                               Length@chartData}, None}},
                                PlotRange -> {Automatic, 1.3*Max@chartData}}

This so that hopefully my actual Chart code is as such :

 Module[{chartData},
         chartData = RandomInteger[20, 20];

         BarChart[chartData,
         chartOptions["yName", "xName"]]]

But this does not work :

The idea is that the chartOptions function will be adjusted given the chart data of the actual plot where it is used.

I am now trying to only use things I trully understand so I hope such a problem does not require to much of your sophisticated skills :-) !


回答1:


The problem is that in chartOptions the variable chartData is a free variable referring to a global definition. In the BarChart expression, the chartData variable is localized to the containing module and is thus invisible to chartOptions. Change chartOptions to accept chartData as a parameter:

chartOptions[chartData_, yName_, xName] := ...

and then adjust the BarChart expression accordingly:

Module[{chartData},chartData=RandomInteger[20,20];
  BarChart[chartData,chartOptions[chartData, "yName","xName"]]]



回答2:


Not a direct answer to the question (which has been answered already by @WReach), but I think that @500 touched on an important topic. We may often want to create something like a configuration containing certain custom option settings for several options. Changing options globally is IMO more often than not a bad idea (this is not to detract from the answer of @Verbeia. There are certainly cases where global options resetting is appropriate. My suggestions should be viewed as complementary). Here I will reproduce an implementation of a very light-weight option-configuration manager to create persistent option configurations, that I wrote when answering a similar question on Mathgroup, and a relevant discussion.


Usage

Let me start with the usage - the implementation for the option-configuration manager can be found at the bottom of my post. We will start with a test function with options:

In[54]:= 
ClearAll[bar];
Options[bar] = {barA -> 1, barB -> 2};
bar[x__, OptionsPattern[]] :=
  Module[{},
    {"barA:  " -> OptionValue[barA], "barB:  " -> OptionValue[barB]}];

At first, we just call it without any explicitly passed options. It displays the values of options that it currently uses - in this case, those that are set globally through Options (OptionValue takes care of it):

In[57]:= bar[1, 2]
Out[57]= {"barA:  " -> 1, "barB:  " -> 2}

We now call one of the functions from our manager API (see below for the implementation):

setOptionConfiguration[bar,"first", {barA -> 11, barB -> 22, fooA -> 1}];

This defines an option configuration for the function bar under name "first". To make use of this option configuration, we use another function: withOptionConfiguration, like so:

In[58]:= withOptionConfiguration[bar[1, 2], "first"]
Out[58]= {"barA:  " -> 11, "barB:  " -> 22}

This looks as if we did change options for bar globally, but in fact we did not. Now, using our option configuration in the presence of explicitly passed option(s):

In[59]:= withOptionConfiguration[bar[1,2,barA->"overriding_barA"],"first"]
Out[59]= {barA:  ->overriding_barA,barB:  ->22}

In this setup, option configuartions override Options[bar], but options explicitly passed to bar override both. One can also inspect the option configurations for a given function, by calling another function from our API:

In[60]:= getOptionConfiguration[bar,"first"]
Out[60]= {barA->11,barB->22}

With this construct, one can do some cool things like creating shortcuts such as this:

In[61]:= 
first=Function[code,withOptionConfiguration[code,"first"],HoldFirst];

In[62]:= first@bar[1,2,barA->"overriding_barA"]
Out[62]= {barA:  ->overriding_barA,barB:  ->22}

In this way, one can create any number of option configurations one wants, and it is quite clear to anybody reading this code, what is going on.


Implementation

ClearAll[setOptionConfiguration, getOptionConfiguration, withOptionConfiguration];
SetAttributes[withOptionConfiguration, HoldFirst];
Module[{optionConfiguration},
  optionConfiguration[_][_] = {};
  setOptionConfiguration[f_, tag_, {opts___?OptionQ}] :=
     optionConfiguration[f][tag] = FilterRules[{opts}, Options[f]];
  getOptionConfiguration[f_, tag_] := optionConfiguration[f][tag];
  withOptionConfiguration[f_[args___], tag_] :=
  f[args, Sequence @@ optionConfiguration[f][tag]];
]; 

Remarks

On a few occasions, I found this type of constructs useful. Their main advantage is that while option configurations are persistent, they work by passing options locally, so options are not reset globally. And global option resetting is a dangerous business, particularly for common (e.g. built-in) functions, and when you deliver your functionality to others. Also, the above implementation is so light-weight that it can easily be extended or modified to suit one's needs.




回答3:


Creating a function like this one is the right way to handle options that depend on the data to be plotted. But some of these options can be set for all uses of BarChart in that Mathematica session using SetOptions. For your example you might choose to define:

 SetOptions[BarChart, Frame -> {{True, True}, {True, True}},
            ImageSize -> 300,
            ChartStyle -> Black,
            ChartElementFunction -> "FadingRectangle",
            LabelStyle -> Directive[Black, Bold, 18]]

And leave only the options that depend on the data in your chartOptions function.

EDIT - caveat I did this without a working copy of Mathematica nearby - typos might still remain

If you do not want to set options globally, which as @Leonid pointed out, can result in other unwanted effects, you could define a custom function:

myBarChart[data_List,yName_,xName_,
  opts:OptionsPattern[{myBarChart,BarChart,RectangleChart,Graphics}]]:=
  BarChart[data,opts,FrameLabel -> {{yName, None}, {None, xName}},
  FrameTicks -> {{{Round@Min@data, Round@(Max@data/2), 
      Round@Max@data}, None},  PlotRange -> {Automatic, 1.3*Max@data}}
]

And then define the options to that function:

Options[myBarChart] = {Frame -> True, ImageSize -> 300,
            ChartStyle -> Black,
            ChartElementFunction -> "FadingRectangle",
            LabelStyle -> Directive[Black, Bold, 18]};
SetOptions[BarChart, Frame -> True, ImageSize -> 300,
            ChartStyle -> Black,
            ChartElementFunction -> "FadingRectangle",
            LabelStyle -> Directive[Black, Bold, 18]]

I should also point out that you could avoid the customised PlotRange option by setting it to All and setting the PlotRangePadding option as required. Probably PlotRangePadding->{{Automatic, Automatic},{Scaled[0.23],0}}. Scaled gives the padding as a fraction of the plot dimensions. Since you want the whitespace to be 0.3 of the full range of data, that is probably 0.3/1.3= 0.23, but you might want to experiment.



来源:https://stackoverflow.com/questions/7192599/create-functions-to-flexibly-define-chart-options-lists-in-mathematica

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!