Arrays are objects and as such have functions called methods and variables called properties. Using IPython (see Chapter 2), you can list an object's methods and properties by using character completion, accessible via the Tab key. Alternatively, you can issue the following:
>>> [m for m in dir(numpy.ndarray) if not(m.startswith('_'))]
['T', 'all', 'any', 'argmax', 'argmin', 'argsort', 'astype', 'base', 'byteswap', 'choose', 'clip', 'compress', 'conj', 'conjugate', 'copy', 'ctypes', 'cumprod', 'cumsum', 'data', 'diagonal', 'dtype', 'dump', 'dumps', 'fill', 'flags', 'flat' , 'flatten', 'getfield', 'imag', 'item', 'itemset', 'itemsize', 'max', 'mean', ' min', 'nbytes', 'ndim', 'newbyteorder', 'nonzero', 'prod', 'ptp', 'put', 'ravel' , 'real', 'repeat', 'reshape', 'resize', 'round', 'searchsorted', 'setfield', 's etflags', 'shape', 'size', 'sort', 'squeeze', 'std', 'strides', 'sum', 'swapaxes ', 'take', 'tofile', 'tolist', 'tostring', 'trace', 'transpose', 'var', 'view']
I've used the preceding list to create Table 7-6; it's only a subset of the methods and attributes, and I chose to describe those I feel are the most useful for data processing and visualization. I've also split the methods into categories for easier viewing. Methods are denoted with (), while properties do not have a trailing parenthesis. In this table, a refers to an array variable.
Figure 7-4. Fourier expansion of a rectangular wave
Table 7-6. Array Methods and Attributes (Partial)
Modifying clip(min, max)
True if all elements of a are true (nonzero).
True if at least one element of a is true (nonzero).
A tuple of indices to nonzero elements of a.
Sorts elements in a.
Returns indices to insert x such that the array's order is preserved. Assumes a is already sorted.
If an element of a is less than min, returns min; if an element of a is greater than max, returns max; otherwise, returns the element.
Returns an array whose elements match the condition specified in cond; equivalent to a[cond].
Sets all values of an array to x; equivalent to a[:] = x.
arange(lo).all() returns False (the first element is zero). arange(-5, -2).all() returns True.
arange(lo).any() returns True.
a = arange(3, 0, -l) sets a to array([3, 2, l]). a.sort() changes a to array([lj 2, 3]).
arange(4).searchsorted(l.5) returns 2.
arange(5).clip(l, 3) returns array([l, 1, 2, 3, 3]).
a = zeros([2, 2]) a[:] = -1 sets a to array([[-l., -1.], [-1., -1.]]). a.fill(-2) sets a to array([[-2.j -2.], [-2., -2.]]).
For math examples, assume a = array([l, lj, -l]), which can also be expressed as a =
cumprod() Cumulative product. Each element a.cumprod() returns array([
is the product of the previous ele- l.+o.j, O.+l.j, O.-l.j]). ments in the array.
cumsum() Cumulative sum. Each element is a.cumsum() returns array([
the sum of the previous elements l.+o.j, l.+l.j, O.+l.j]). in the array.
Examples real and imag conj()
Shape Related flattenQ
Real and imaginary values of elements in a.
Complex conjugate of a (negation of the imaginary part; rows and columns transposed).
Maximum and minimum values of a (performed on real part only).
Mean value of a.
Product of all the values in a.
Peak-to-peak value of a; equivalent to a.max()-a.min().
Rounded values of a.
Standard deviation of elements in a.
Sum of all the values in a.
Sum of the diagonal of a 2-D array. If n is provided, sums the offset diagonal.
Variance of elements in a.
The values in a as a 1-D array.
Number of dimensions of a. Copies a over n times, flattened.
.) Resizes the current array to size (d1, d2, . . . ).
A tuple representing shape of a.
Transposes a matrix. This is equivalent to conjugate but without negating the imaginary parts.
a.mean() returns 0.33333333333333331] (1j/3).
exp(a).round()returnsarray([ 3.+0.j, l.+l.j, 0.+0.j]).
eye(2).repeat(2) returns array([ 1., 1., 0., 0., 0., 0., 1., 1.]).
arange(4).reshape(2, 2)returns array([[0, l], [2, 3]]).
a = arange(4) a.resize(2, 2) sets a to array([[0, l], [2, 3]]).
eye(2, 3).transpose() returns array([[ l., o.], [ 0., l.], [ 0., 0.]]).
244 CHAPTER 7 ■ MATH GAMES Table 7-6. Continued
Function Description Examples
Conversion tofile(fname) Writes an array to file (binary). eye(2).tofile('eye2.bin')
fromfile(fid) Reads an array from file (binary). a = fromfile(open('eye2.bin'))
tolist() Converts an array to a list eye(2).tolist() returns [[1.0,
Example: A Magic Square
A magic square is a square with the sum of each row and column equal and the same. Typically, magic squares do not allow numbers to repeat twice. In this example, we'll generate magic squares, populating the values from 1 to N2 in a square of size N by N.
A modern variation on the magic square idea is the Sudoku puzzle game. The ideas presented in this example can be modified to provide solutions to Sudoku puzzles (see http:// en.wikipedia.org/wiki/Sudoku for possible strategies for implementing a computer solution).
Back to our example. We'll create a magic square implementing the De la Loubere method (also known as the Siamese method), which works for squares of odd values of N only. Constructing a magic square is performed by placing the first value, 1, in the middle column at the top. Incremented values are placed diagonally up and to the right. If the spot up and to the right is outside the square, it is wrapped around to the bottom row (if exceeded at the top) or to the first column (if exceeded to the right) or both. If a cell is already occupied, the value moves a row below (again, wrapping if needed). Figure 7-5 illustrates the algorithm with example magic squares of sizes 3 and 5.
An implementation of the algorithm using an array is presented in Listing 7-5.
Listing 7-5. Creating a Magic Square from numpy import * def magicsq(n=3):
Returns a magic square of size n; n must be odd
raise ValueError, "Magic(n) requires n to be odd" m, row, col = zeros([n, n]), 0, n/2 for num in xrange(l, n**2+l):
m[row, col] = num # fill the cell col = (col+1) % n row = (row-l) % n if m[row, col]:
col = (col-1) % n row = (row+2) % n return m def testmagicsq(m):
Returns True ifmisa magic square
return all(m.sum(o) == msum) and all(m.sum(l) == msum)
The main for loop is quite straightforward and follows the algorithm strictly. However, calculation of the column and row values using the modulo (%) operator is tricky and requires some explanation. Consider the way the algorithm is specified: increment the column value and check whether the new value is within the size of the matrix. If it is not, wrap it around to the beginning. A similar approach is taken with the row: decrement and wrap if required. Instead of implementing these two steps, an increment/decrement followed by an if statement, we could use the modulo operation, which captures the idea quite elegantly: col = (col+1) % n.
I've chosen to initialize the variables m, row, and col with a multiple assignment. Multiple assignments can also be used inside the for loop: col, row = (col+l) % n, (row-l) % n; however, in my mind it's less clear, and there's no impact performance-wise. My personal preference is to use multiple assignments in initializations and not calculations.
I've defined another function here, testmagicsq(), which checks whether a square is indeed a magic square. The function also works on even values (which is a plus) and makes use of the sum() member function of the array object.
■Tip Python supports testing via several built-in packages, including doctest and unittest. However, for the purpose of this example, I've chosen to write a dedicated test function, which will further show the properties of NumPy arrays.
The function sum(o) returns an array of the sums of columns (i.e., along axis 0); sum(l) returns an array summing rows (along axis 1). Here's a listing demonstrating summing along the 0 axis and the 1 axis:
>>> a = eye(2, 3)
array([[ 1., 0.,
[ 0., 1.,
array([ 1., 1.,
array([ 1., 1.])
>>> m = magicsq(5)
array([ 65., 65.,
array([ 65., 65.,
As can be seen, the matrix eye(2, 3) has two rows and three columns. Summing along axis 0 via sum(o) returns a 1-D array (a vector) holding the sums of all three columns. Consequently, sum(l) returns a vector holding the sum of the rows. The next lines show how this can be used to check for "magic-ness" of a square—both vectors, sum(o) and sum(l), should be equal element-wise to the sum along an arbitrary axis.
In the testmagicsq() function I've chosen to compare both sums of columns and of rows with the sum of the first column: sum(m[0, :]). If you compare a vector (1-D array) with a scalar (a single value), the result is a vector with each element compared with the scalar. To ensure all are indeed equal to the required sum, you could use the all() member function. I've opted to use the notation all(m.sum(o) == msum) over (m.sum(o) == msum).all() because I think it's more readable, but that again is personal preference; both do the job.
■ Note In the function testmagicsq(), it's not enough to check that m.sum(o) is equal to m.sum(i) because this only checks that the sums of rows is equal to the sums of columns. However, that's not a sufficient condition. Consider the array m = array([[i, o], [o, 2]]): it satisfies the condition m.sum(o) == m.sum(i), but it's not a magic square. You might raise the question whether the array eye(N) is a magic square—the function testmagicsq() will return True, but maybe this is a trivial case of a magic square.
One other interesting aspect of the Siamese method is that the sum along the diagonal is also identical to the sum of each row and each column; that's true for both diagonals. The function trace() calculates the sum along the diagonal (top left to bottom right). To calculate the sum of the second diagonal (bottom left to top right), you could use the fliplr() function.
>>> m = magicsq(5) >>> m.trace() 65.0
Was this article helpful?