Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I have a function that takes two inputs and processes them for a single output. What I need is one that can take a varying number of inputs. and process them to a single output. Is this possible in Mathematica?

 DatasetAverage[inputData_, inputData2_] := Block[{dataAvg, v, w},
   v = Length[inputData];
   w = Length[inputData2];
   If [v != w,
    Print["DatasetAverage: Data sample sizes do not match"]];
   dataAvg = Table[Mean[{inputData[[i]], inputData2[[i]]}], {i, 1, Length[inputData2]}]
 ]
share|improve this question
1  
You may want to wait for other answers before accepting. I am sure the regulars here can offer more refined answers. –  Michael Wijaya Jun 9 '12 at 1:44
1  
I discussed multiple-arg functions briefly in my book, which may add a little more info to the excellent answers you received here. –  Leonid Shifrin Jun 9 '12 at 9:58
    
thanks @Leonid Shifrin I will review it! –  R Hall Jun 10 '12 at 17:02
add comment

4 Answers

up vote 17 down vote accepted

Yes, for both named patterns and pure functions.

Pure functions

You can see that inherently they accept multiple arguments but discard those that are not used:

{#} &[1, 2, 3]  (* out: {1} *)

The object ##, (SlotSequence), represents all arguments wrapped in Sequence, e.g. Sequence[1, 2, 3]. (Internally it doesn't use Sequence but it behaves similarly in most places.)

{##} &[1, 2, 3]  (* out: {1, 2, 3} *)

You can combine # and ##, including their numbered forms:

{"first" -> #, "rest" -> {##2}, "all" -> {##}} &[1, 2, 3]
{"first" -> 1, "rest" -> {2, 3}, "all" -> {1, 2, 3}}

Named patterns

Using Blank*:

_ (Blank)
__ (BlackSequence)
___ (BlankNullSequence):

f[a_, b__] := {"first" -> a, "rest" -> {b}}

f[1, 2, 3] (* out: {"first" -> 1, "rest" -> {2, 3}} *)

__ requires an argument to be present while ___ does not:

f[1] (* out: f[1] *)

g[a_, b___] := {"first" -> a, "rest" -> {b}}

g[1] (* out: {"first" -> 1, "rest" -> {}} *)

Multiple variable length named patterns can be given and will by default be matched shortest first:

h[a_, b__, c__] := {"a" -> a, "b" -> {b}, "c" -> {c}}

h[1, 2, 3, 4, 5]  (* out: {"a" -> 1, "b" -> {2}, "c" -> {3, 4, 5}} *)

This can be controlled with Shortest and Longest:

i[a_, Longest[b__], c__] := {"a" -> a, "b" -> {b}, "c" -> {c}}

i[1, 2, 3, 4, 5] (* out: {"a" -> 1, "b" -> {2, 3, 4}, "c" -> {5}} *)

See this answer for an advanced use of these functions.

The Blank* functions are not the only way to create a variable length pattern. You can also use Repeated (..) or RepeatedNull (...):

j[x : _Real ..] := {x}

j[1.1, 1.2, 1.3]  (* out: {1.1, 1.2, 1.3} *)

These methods can be used in powerful ways such as destructuring.

share|improve this answer
    
Thanks very much! Looks like I will be re-writing some code to take advantage of these features! –  R Hall Jun 9 '12 at 2:42
    
I saw the pure functions and instantly knew who wrote the answer... –  Brett Champion Jun 9 '12 at 3:02
    
@Brett I hope that's a good thing. :-) –  Mr.Wizard Jun 9 '12 at 7:15
add comment

Mean already does what your DataSetAverage is expected to do. From docs Mean > More Information:

enter image description here

that is, Mean map-threads over sub-lists in its argument.

So

Mean[{{a, b, c}, {u, v, w}}]

gives

enter image description here

and

Mean[{{a, b, c}, {u, v, w, x}}]

throws the error

enter image description here

So, you can use something like

ClearAll[dtAvrg]; 
dtAvrg[lst__List] :=  Check[Mean[{lst}], "DatasetAverage: Data sample sizes do not match",  Mean::rectt]  

examples:

 dtAvrg[{a, b, c}, {u, v, w}, {x, y, z}] 

returns

enter image description here

and

 dtAvrg[{1, 2, 3}, {4, 5, 6}, {7, 8, 9}]

gives {4,5,6}.

For lists with unequal lengths,

dtAvrg[{a, b, c}, {u, v, w}, {x, y, z, t}] 

returns

Mean::rectt: Rectangular array expected at position 1 in Mean[{{a,b,c},{u,v,w},{x,y,z,t}}].
"DatasetAverage: Data sample sizes do not match"  

If you like you can Quiet the error message and display your message only:

 Quiet@dtAvrg[{a, b, c}, {x, y, z, t}] 
 (* gives:  "DatasetAverage: Data sample sizes do not match" *)   
share|improve this answer
    
Great tip Thank you! –  R Hall Jun 9 '12 at 10:54
    
+1 Your method is much cleaner than mine. It is good to know about Check too. –  Michael Wijaya Jun 9 '12 at 17:27
1  
@MichaelWijaya, thank you for the vote. –  kguler Jun 9 '12 at 17:36
add comment

This may be close to what you want:

datasetAverage[inputDataList_] :=
  If[
    Equal @@ Map[Length, inputDataList] == False,
    Print["DatasetAverage: Data sample sizes do not match"],
    Mean[inputDataList]
  ]

Some sample inputs:

In[10]:= datasetAverage[{{1,2,3},{4,5,6},{7,8,9}}]
Out[10]= {4,5,6}

datasetAverage[{{1,2,3},{4,5,6,7}}]
DatasetAverage: Data sample sizes do not match

Edit

My earlier version of datasetAverage was defined using BlankSequence, but it did not make use of the fact that it can take an unspecified number of parameters. It is now rewritten using Blank.

Here is one version which uses BlankSequence:

datasetAverage2[inputDataList__] :=
  If[
    Equal @@ Map[Length, {inputDataList}] == False, 
    Print["DatasetAverage: Data sample sizes do not match"], 
    Mean[{inputDataList}]
  ]

The only difference is that I now enclose every occurrence of inputDataList in parentheses.

In[9]:= datasetAverage2[{1,2,3},{4,5,6},{7,8,9}]
Out[9]= {4,5,6}
share|improve this answer
    
Thank you! What allows for the multiple inputs? –  R Hall Jun 9 '12 at 1:38
1  
@RHall I used the BlankSequence pattern, which stands for any sequence of one or more expressions. So now I can feed in a sequence of datasets. –  Michael Wijaya Jun 9 '12 at 1:40
    
Thanks very much! –  R Hall Jun 9 '12 at 1:43
    
I'm rather confused by your edit. Why did you convert this to a single argument function? –  Mr.Wizard Jun 9 '12 at 3:38
    
@Mr.Wizard I mixed up two ideas when I wrote up the answer. The original function does not actually require the use of BlankSequence. In fact, it fails if I evaluate datasetAverage[{1,2,3},{4,5,6}]. –  Michael Wijaya Jun 9 '12 at 3:48
add comment

Just one supplementary remark regarding conditions on the argument list:

Functions with a variable number of arguments can be very useful, and patterns like x__ allow you to define them easily.

But while the number of arguments may not be fixed, there may still be restrictions or limits on the allowed number. This can be enforced by using a condition as shown here:

f[x__] := Total@Apply[Times, Partition[{x}, 2], 2] /; EvenQ[Length[{x}]]

In this example, f is intended to form pairs from the arguments, multiply the pairs and add them. That's why there is a condition appended to the definition with /; - it insures that the definition is not used unless an even number of arguments is supplied.

If the condition isn't met, the function returns unevaluated - unless you provide another definition for a function with the same name with a different condition.

share|improve this answer
1  
Related question: mathematica.stackexchange.com/q/1835/121 –  Mr.Wizard Jun 9 '12 at 3:39
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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