After using Mathematica for a while, you start to think you are on top of the richness of the language. You become familiar with a range of different functions and programming styles. But in fact, you haven’t even scratched the surface.
I can’t emphasize enough how important it is to look up the documentation from time to time, even for the functions you think you already know. There are so many options and additional arguments to workhorse functions that you might not have appreciated. Here are a few of my favorites. Some of them were noted in a question I once asked about the issue.
Hooray for Array
If you want to iterate over arbitrary objects, you need Table
.
Table[f[i], {i, {a, b, whatever}}] {f[a], f[b], f[whatever]}
If instead the iterator increments by one each time, Array
is cleaner. You don’t even need to give the iterator a name.
Array[f, 3] {f[1], f[2], f[3]}
It’s easy to forget the extended forms of standard functions like this. The iterator in Array
does not have to start at 1. Don’t forget that the first argument gives the resulting list’s length, not the end value of the iterator.
Array[f, {3}, {5}] {f[5], f[6], f[7]}
This is a general example of the flexible pattern-matching in Mathematica, which allows separate function definitions for different numbers and types of arguments.
Total
ly rad
Total[somematrix, {2}]
is equivalent to Map
ping the Total
function onto the rows of the matrix.
Partition
magic
The third argument to Partition
defines the offset, so that instead of
Partition[Array[f, 5], 2] // TableForm f[1] f[2] f[3] f[4]
You get
Partition[Array[f, 5], 2, 1] // TableForm f[1] f[2] f[2] f[3] f[3] f[4] f[4] f[5]
This means that, for example
Divide @@@ Partition[somevector, 2, 1]
is equivalent to
Most[somevector]/Rest[somevector]
This question on the Mathematica.SE site shows how to generalise Partition
for ragged lists (i.e., those with sub-lists that are not all the same length). There are a number of different ways to do this, including using Partition
itself, as well as various combinations of NestWhile
and Prepend
.
Join
in the fun
The third argument of Join
is also incredibly useful. How many times have you seen complicated code with Transpose
and Append
all over the place, just to join two matrices by column, instead of by row? The default syntax for Join
joins the rows, but you can join by columns with just two extra keystrokes. As noted in the answers to this question, this is also usually a little faster than the little-known but highly useful ArrayFlatten
function.
Join[Array[g, {4, 2}], Array[f, {4, 2}]] // TableForm g[1,1] g[1,2] g[2,1] g[2,2] g[3,1] g[3,2] g[4,1] g[4,2] f[1,1] f[1,2] f[2,1] f[2,2] f[3,1] f[3,2] f[4,1] f[4,2]
Join[Array[g, {4, 2}], Array[f, {4, 2}], 2] // TableForm g[1,1] g[1,2] f[1,1] f[1,2] g[2,1] g[2,2] f[2,1] f[2,2] g[3,1] g[3,2] f[3,1] f[3,2] g[4,1] g[4,2] f[4,1] f[4,2]
Flip and Flatten
There are also additional options and arguments in some other basic list-manipulation commands. For example, most experienced users know that Flatten
takes a level specification. For example, Flatten[list,1]
turns a three-dimensional tensor into a matrix.
Flatten[Array[f, {3, 3, 2}], 1] // TableForm f[1,1,1] f[1,1,2] f[1,2,1] f[1,2,2] f[1,3,1] f[1,3,2] f[2,1,1] f[2,1,2] f[2,2,1] f[2,2,2] f[2,3,1] f[2,3,2] f[3,1,1] f[3,1,2] f[3,2,1] f[3,2,2] f[3,3,1] f[3,3,2]
But did you know that the second argument can also be a matrix? This popular question on the Mathematica.SE site contains a lot of information about how it works. It can be used to Transpose
ragged lists, as this answer explains.
Speaking of Transpose
, consider what is possible using its second argument. Here is the result of a normal transpose on a three-dimensional list with dimensions 342.
t1 = Transpose[Array[f, {3, 4, 2}]]
{{{f[1, 1, 1], f[1, 1, 2]}, {f[2, 1, 1], f[2, 1, 2]}, {f[3, 1, 1], f[3, 1, 2]}}, {{f[1, 2, 1], f[1, 2, 2]}, {f[2, 2, 1], f[2, 2, 2]}, {f[3, 2, 1], f[3, 2, 2]}}, {{f[1, 3, 1], f[1, 3, 2]}, {f[2, 3, 1], f[2, 3, 2]}, {f[3, 3, 1], f[3, 3, 2]}}, {{f[1, 4, 1], f[1, 4, 2]}, {f[2, 4, 1], f[2, 4, 2]}, {f[3, 4, 1], f[3, 4, 2]}}}
Effectively it treats the list as a matrix to transpose normally: it just happens that the elements of that matrix are themselves lists.
Dimensions@t1 {4, 3, 2}
You can get a completely different result by choosing a different specification in the second argument of Transpose
. (The default is equivalent to specifying {2,1,3}
for the second argument.)
t2 = Transpose[Array[f, {3, 4, 2}], {2, 3, 1}] {{{f[1, 1, 1], f[1, 2, 1], f[1, 3, 1], f[1, 4, 1]}, {f[2, 1, 1], f[2, 2, 1], f[2, 3, 1], f[2, 4, 1]}, {f[3, 1, 1], f[3, 2, 1], f[3, 3, 1], f[3, 4, 1]}}, {{f[1, 1, 2], f[1, 2, 2], f[1, 3, 2], f[1, 4, 2]}, {f[2, 1, 2], f[2, 2, 2], f[2, 3, 2], f[2, 4, 2]}, {f[3, 1, 2], f[3, 2, 2], f[3, 3, 2], f[3, 4, 2]}}}
Dimensions@t2 {2, 3, 4}
The Bottom Line
Bottom line? Even if you are an experienced Mathematica user, it is worth having a good look at the documentation for basic functions from time to time. You might be missing some of the power they contain in their optional arguments.
Filed under programming
It seems to be worthwhile to teach people how to find out optional parameters programmatically. They are not always documented.
@Raphael See this question that shows how to find undocumented options.
@rm -rf Thanks, that seems to be very helpful!
Great post! BTW I believe that Array[f,{5}{3}] can be written without the curly brackets.