20. Arrays and Matrices

Last revision:  March 19 1998

In this lab we introduce a useful new data type, called an array.  We consider arrays, methods for their definition, initialization, and manipulation, and pay particular attention to using iterative structures to modify them.

Mathematica operations/structures introduced: 
     array
     vector
     size
     indices
     Dimensions[]
     ColumnForm[]
     MatrixForm[]
     IdentityMatrix[]
     DiagonalMatrix[]

Arrays

An array  is a general data type.  A one-dimensional array  is simply a list of elements, such as "{2,3,4}"; such an array is sometimes termed a vector.  A two-dimensional array  is a list of lists of elements, where the lists inside all have the same length, such as "{{1,2},{4,5},{7,8}}"; such an array is sometimes termed a matrix.   A three-dimensional array  is then a list of lists of lists of elements, where the innermost lists all have the same length and the lists containing those lists also all have the same length.  For an example of a three-dimensional array, consider the expression "{{{1,2,3},{2,3,4}},{{3,4,5},{5,6,7}},{{7,8,9},{8,9,10}}}".  Note that the innermost lists all have length 3, and the lists containing these innermost lists all have length 2 (containing 2 lists of length 3).  Arrays can be defined for any positive integral dimension by continuing in the same format.

Sizes

As you may surmise, arrays have a definite size.  For one-dimensional arrays, the size is simply the number of elements.  For two-dimensional arrays, however, the size must contain two numbers: the number of inner lists, followed by the number of elements in each list.  For three-dimensional arrays, the size is composed of three numbers: the number of outermost lists, the number of lists inside a given outermost list, and finally the number of elements in each of the innermost lists.  In the examples above, the one-dimensional array has size 3; the two-dimensional array has size "3,2" (or 3x2); and the three-dimensional array size "3,2,3" (or 3x2x3).

Mathematica can determine these sizes, using a command we have considered some time ago, "Dimensions[]".  Let's try it:

oneDArray = {2,3,4};
twoDArray = {{1,2},{4,5},{7,8}};
threeDArray = {{{1,2,3},{2,3,4}},{{3,4,5},{5,6,7}},
{{7,8,9},{8,9,10}}};

Dimensions[oneDArray]
Dimensions[twoDArray]
Dimensions[threeDArray]

Mathematica also has some functions which display arrays in rectangular ways.  "ColumnForm[]" displays a vector as a column instead of a row, and "MatrixForm[]" displays a two-dimensional array in rectangular form, where the inner lists are written out as rows.

ColumnForm[oneDArray]

MatrixForm[twoDArray]

(For obvious reasons, there is no simple way to display an array of dimensionality greater than two.)

Individual Elements

We can refer to individual elements of a list with array indices.  These indices tell us how to find the element by indicating, working from the outside in, which particular list, which element of if that list, and so on, contains the element we are interested in. In a one-dimensional array, we need only mention one number, the number of elements from the beginning of the list one must count to find the element.  In a two-dimensional array, we require two numbers:  the first index will tell us which list to use (which "row"), the second which element of that list (which "column").  For instance, in the two-dimensional array "{{1,2,3},{4,5,6}}" the element with value 3 has indices "1,3".  Similarly, in a three-dimensional array, the three indices pinpoint, respectively, which outer list, which list of that list, and which element of that inner list one must consider to reach the element in question.

To determine what value resides in an array at a particular set of array indices, we can use "Part[]" or its functional equivalent, "[[]]":

oneDArray[[2]]
Part[oneDArray,2]

twoDArray[[3,2]]
Part[twoDArray,3,2]

Note that if we specify too few indices, you will get a list or list of lists---all of the elements of the array whose first so many indices match our specified ones.

Part[threeDArray,2,2]

Arrays: Declaration and Initialization

To declare that a name refers to an array, we must decide from the outset what the size of the array will be and what values we will initially assign to its elements.  Initializing an array to be an array of zeros is simplest, and we will begin thus.  To initialize a one-dimensional array of zeros, we use a "Table[]" command.

myArray = Table[0,{10}]

For two-dimensional and three-dimensional arrays, we use multiple iterators.

my2 = Table[0,{10},{3}]

my3 = Table[0,{3},{2},{3}]

It may happen that we know some nonzero values to which we would like the array initialized.  We can use "Table[]" to initialize the values of the various entries to the results of some function or expression, which may depend on the indices of the entries.  We might want to initialize each entry of a 3x5 array to its own row number; in that case, we would execute

my4 = Table[i,{i,3},{j,5}]

Similarly, to initialize a 4x6 array with value "i+2^j" at indices "i,j", we would execute

Clear[f]
f[i_,j_]:=i+2^j;
my5 = Table[f[i,j],{i,4},{j,6}]

or

my5 = Table[i+2^j,{i,4},{j,6}]

It is also possible to define special sorts of arrays in Mathematica.  An identity matrix  is a two-dimensional array of size "n,n" which contains all zeros, except that the elements with two identical indices have value 1.  Mathematica can create such a matrix using the "IdentityMatrix[]" command.

IdentityMatrix[5]

More generally, to specify a diagonal matrix, which is a square two-dimensional array which contains all zeros,  except that the elements with two identical indices may have nonzero values.  Mathematica can create such a matrix using the "DiagonalMatrix[]" command.

DiagonalMatrix[{1,2,3}]

Can you tell how Mathematica decides what size the diagonal matrix should be?

Arrays: Modification

We can modify the value of any array element simply by executing an assignment statement, referring to an element via its indices.

oneDArray

oneDArray[[2]]=500

oneDArray

In many cases, though, instead of modifying a particular array's elements, we may wish to have a copy of the old array made, and then modify the elements in the new array.  (One particular case where we will need to do so occurs when an array is an actual parameter in a function call; since we cannot modify the value of a formal parameter inside a function, we must copy the formal parameter to a local variable in order to permit modifications.)  We can do so by using an assignment statement to complete the first copy of matrices.

twoDArray

twoD2 = twoDArray;
twoD2[[3,2]]=0;
twoD2

Arrays are frequently manipulated using iterative structures such as "For[]", "Do[]", and "Table[]".  Here are some examples.

Example 1: Adding to Every Element

Let's add the integer 1 to each element of a vector, by individually assigning each entry.

addOne[v_List] := Module[{newv},
   newv = v;
   Do[
      newv[[i]] = v[[i]] + 1
   ,{i,1,Length[v]}];
   newv
]

addOne[{2,7,3,4,9}]

(Of course, this process is much simpler using Mathematica's list-based operations.)

{2,7,3,4,9}+1

Note that we had to copy vector "v" to "newv" in order to modify it; formal parameters such as "v" cannot be modified inside a function.

Example 2: Cumulative Sums

A more interesting example is to take a vector and find the cumulative sum vector, made up of sums of consecutive elements beginning with the first element, so that "cumSum[]" of "{2,7,3,4,9}" would return "{2,9,12,16,25}".

Using Do and Assignment of Elements

cumSum[v_List] := Module[{sumv},
   sumv = v; (* copy the vector *)
   Do[ (* successively add vector elements *)
      sumv[[i]] = sumv[[i-1]] + v[[i]]
   ,{i,2,Length[v],1}];
   sumv (* return answer *)
]

cumSum[{2,7,3,4,9}]

Using Do and Append

Clear[cumSum];
cumSum[v_List] := Module[{sumv},
   sumv = {v[[1]]}; (* get the first entry *)
   Do[
      sumv = Append[sumv, sumv[[i-1]] + v[[i]] ]
   ,{i,2,Length[v],1}];
   sumv
]

cumSum[{2,7,3,4,9}]

One advantage of the first version of "cumSum[]" is that it does not use any list functions (such as "Append[]") except "Length[]".  Many languages support arrays but no list functions; thus, the first version is programmable in almost any language.

Example 3: Exchanging Two Rows

Suppose we have two matrices (two-dimensional arrays) which have the same number of columns, and we wish to exchange row "i" from the first matrix with row "j" of the second.  Our strategy will be to copy row "i" from the first matrix to another temporary array, to copy row "j" from the second matrix to row "i" of the first, and finally to copy our temporary array to row "i" of the first.  We will assume we are given two matrices as arguments together with "i" and "j"; we will return the two new matrices in a list.

Clear[rowSwitch]
rowSwitch[m1_,m2_,i_,j_] := Module[
		{newm1=m1,newm2=m2,temp,cols,k}, (* init newm1,2 *)
		If[Length[Dimensions[m1]]!=2||
		   Length[Dimensions[m2]]!=2,
		   Print["Arrays not 2-dimensional"],
		   If[Dimensions[m1][[2]]!=Dimensions[m2][[2]],
		      Print["Number of columns different"],
		      cols = Dimensions[m1][[2]]; (* find # cols *)
		      temp = Table[0,{cols}]; (* initialize temp *)
		      For[k=1, k<=cols, k++, (* k from 1 to cols *)
		         temp[[k]]=newm1[[i,k]]
		         ]; (* now temp has old row i *)
		      For[k=1, k<=cols, k++,
		      	  newm1[[i,k]] = newm2[[j,k]]
		      	  ]; (* now row i is old row j *)
		      For[k=1, k<=cols, k++,
		         newm2[[j,k]] = temp[[k]]
		         ]; (* now row j is temp *)
		      {newm1,newm2}
		   ]
		]
		];

rowSwitch[twoD2,twoDArray,2,3]

Study the code carefully.  Note again that local variables were used instead of formal parameters so that modifications could be made.  Some will notice that a simpler solution exists, which we give below, which uses the fact that we can reference an entire row of a two-dimensional matrix by specifying only the row number between double brackets.  For instance, "newm1[[i]]" refers to the entire list of row "i" in array "newm1".

Clear[rowSwitch2]
rowSwitch2[m1_,m2_,i_,j_] := Module[
		{newm1=m1,newm2=m2,temp,cols,k}, (* init newm1,2 *)
		If[Length[Dimensions[m1]]!=2||
		   Length[Dimensions[m2]]!=2,
		   Print["Arrays not 2-dimensional"],
		   If[Dimensions[m1][[2]]!=Dimensions[m2][[2]],
		      Print["Number of columns different"],
		      temp = newm1[[i]];
		      newm1[[i]] = newm2[[j]];
		      newm2[[j]] = temp;
		      {newm1,newm2}
		   ]
		]
		];

rowSwitch2[twoD2,twoDArray,2,3]