8

I am a little bit confused by the documentation for Apply (@@).

I want to define a list of parameters, for instance:

parameters = {{1/2, 1/2}, {1, 1/3}, {2, 1/4}, {0.5, 1/5}};

Then I have a function:

(b/a)*((a/x)^(b+1))

Now I want to map the pair (a,b) to the values defined in parameters and generate a plot for each pair of parameters in the same graph.

Now the plot syntax would look something like this:

p = Plot[Evaluate[myfunction /@ parameters], {x, 0, 3},
         AxesOrigin -> {0, 0}, AxesLabel -> {x, f}, PlotStyle -> styles]

What I struggle with, is getting the syntax right in the following part:

myfunction /@ parameters

How do I need to define this so I get a proper map of (a,b) to my list of parameters? To be honest, I am not very satisfied with the documentation for Map. Are there better tutorials?

celtschk
  • 19,133
  • 1
  • 51
  • 106
Chris
  • 883
  • 4
  • 9
  • 11

4 Answers4

9

Here's another way:

parameters = {{1/2, 1/2}, {1, 1/3}, {2, 1/4}, {0.5, 1/5}};

myfunction[{a_, b_}] := (b/a)*((a/x)^(b + 1))

Plot[myfunction /@ parameters, {x, 0, 3}, Evaluated -> True]

Mathematica graphics


Using plain x here is not local; you should consider using \[FormalX] instead. The example above is "safe" because x is localized by Plot (it would not be if you used Plot[ Evaluated @ ... ] instead of Evaluated -> True). On the other hand if you accidentally assign a value to x and then try something like myfunction[{1, 1/3}] /. x -> 5 it will not behave as expected. Formal Symbols are symbols that Mathematica will not let you assign a value to, protect you from yourself in a case like this. It is a good idea to use them if you are going to create a definition with a "floating" undefined symbol like x in the example above.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
7

You can define your function as an anonymous function and use Apply at level 1 or @@@:

myfunction[x_] := #2/#1 ((#1/x)^(#2 + 1)) &
Plot[Evaluate[myfunction[x] @@@ parameters], {x, 0, 3},  
    AxesOrigin -> {0, 0}, AxesLabel -> {x, f}]

enter image description here


Think of Apply as something that replaces the Head of an expression. For example, the head of g[a, b] is g. Now if you apply f to that expression, you get:

f @@ g[a, b]
(* Out= f[a, b] *)

You see that the head has been replaced with f. The long form of Apply also takes a third argument, where you can specify the Level that you want it to act on i.e., you can write Apply[f, expr, {level}].

Apply at level 1 is used very often, that it has its own shorthand, which is @@@ and is what I've used above. Here's an example showing the difference between @@ and @@@:

f @@ g[h[a, b], h[c, d]]
(* Out= f[h[a, b], h[c, d]] *)

f @@@ g[h[a, b], h[c, d]]
(* Out= g[f[a, b], f[c, d]] *)
rm -rf
  • 88,781
  • 21
  • 293
  • 472
  • I was working so hard to include both methods, but you stole my thunder, +1. :) – rcollyer May 06 '12 at 03:09
  • Is that & at the end of the line where myfunction is defined necessary? Why can't I defined the function, put a ; at the end and then run the plot? – Chris May 06 '12 at 03:25
  • @Chris he's being clever. As pointed out by Mr.Wizard, x is global, and within the bounds of Evaluate it would take on it's global value. What that construct does is localize x to the local form. – rcollyer May 06 '12 at 03:29
  • @Chris Yes, it is necessary. The & is what completes the pure function definition and it won't work without it. My definition above is a mix of named patterns (x_) which localizes the variable x and slots (#) which take the parameter values when you apply at level 1. – rm -rf May 06 '12 at 03:30
7

There are two main ways of accomplishing this, both have their merits, but Map may be easier to understand initially. For instance, using (b/a)*((a/x)^(b+1)) I would do the following,

(#[[2]]/#[[1]])(#[[1]]/x)^(#[[2]]+1)& /@ parameters

where #[[1]] and #[[2]] are a and b, respectively. But, this makes it difficult to read, alternatively you can use With to improve the readability, as follows,

With[{a = #[[1]], b = #[[2]]}, (b/a)*((a/x)^(b+1))]& /@ parameters

It is longer, but it is more readable. I tend to use With in this way. The second method is to use Apply, as outlined by R.M. Here With can also be used,

With[{a = #1, b = #2}, (b/a)*((a/x)^(b+1))]& @@@ parameters

but it is less likely to be confusing when you come back to the code a month later.

rcollyer
  • 33,976
  • 7
  • 92
  • 191
  • Ok! Does the "#" in front designate it as a parameter? Does the number designate the order, so that the program knows which value from {number1, number2} to map? Why do we have 3 @'s. How does this "level" theory work? – Chris May 06 '12 at 03:15
  • @Chris The # is a formal parameter known as a Slot. # is shorthand for #1. In my example using Map, #[[1]] refers to the first element of the first parameter. As noted by RM, Apply differs in that it replaces the Head of an expression, so Apply[f, {x,y}] (shorthand f@@{x,y}) becomes f[x,y]. The @@@ is shorthand for Apply[f, {{1,2},{3,4}}, {1}] which results in {f[1,2], f[3,4]}. Because the elements become arguments, #1 becomes a and #2 becomes b in your question. – rcollyer May 06 '12 at 03:20
  • 1
    For the last approach (using @@@) you could use Function[{a, b}, (b/a)*((a/x)^(b + 1))] instead of With. – Brett Champion May 06 '12 at 04:19
5

Here's another solution, which uses replacement rules, and is syntactically closest to your original code (and, unlike most other solutions, doesn't require you to rewrite either myfunction or parameters).

p = Plot[Evaluate[myfunction /. {a->#1, b->#2}& @@@ parameters], {x, 0, 3},
                  AxesOrigin -> {0,0}, AxesLabel -> {x,f}, PlotStyle -> styles]

However, in case you can rewrite myfunction, the simplest solution would be to make it a function of the parameters:

myfunction[a_, b_] := (b/a)*((a/x)^(b+1))
p = Plot[Evaluate[myfunction @@@ parameters], {x, 0, 3},
                  AxesOrigin -> {0,0}, AxesLabel -> {x,f}, PlotStyle -> styles]

Or maybe cleaner (but slightly more verbose):

myfunction[a_, b_, x_] := (b/a)*((a/x)^(b+1))
p = Plot[Evaluate[myfunction[##,x]& @@@ parameters], {x, 0, 3},
                  AxesOrigin -> {0,0}, AxesLabel -> {x,f}, PlotStyle -> styles]

On the other hand, if you cannot change myfunction, but can change how your parameters are written, you could directly write the latter as rules:

parameters = {{a->1/2, b->1/2}, {a->1, b->1/3}, {a->2, b->1/4}, {a->0.5, b->1/5}};

Then your plot call would just read

p = Plot[Evaluate[myfunction /. parameters], {x, 0, 3},
                  AxesOrigin -> {0,0}, AxesLabel -> {x,f}, PlotStyle -> styles]
celtschk
  • 19,133
  • 1
  • 51
  • 106