Math 480: Lecture 11

Numpy - The fundamental package for scientific computing with Python

Documentation

Some Quick Teasers

Mandelbrot Example (see http://www.scipy.org/Tentative_NumPy_Tutorial/Mandelbrot_Set_Example).  This is how very clever advanced users of Numpy can do amazing things incredibly quickly in just a few lines of code.

{{{id=207| %python import numpy def mandelbrot(h,w, maxiter=20): y,x = numpy.ogrid[-1.4:1.4:h*1j, -2:0.8:w*1j] c = x + y*1j z = c divtime = maxiter + numpy.zeros(z.shape, dtype=int) for i in xrange(maxiter): z = z**2 + c diverge = z*numpy.conj(z) > 2**2 # who is diverging div_now = diverge & (divtime==maxiter) # who is diverging now divtime[div_now] = i # note when z[diverge] = 2 # avoid diverging too much return divtime /// }}} {{{id=166| @interact def _(size=(300,(100..600)), maxiter=(20,(1..40))): import pylab time im = mandelbrot(size, size, maxiter) pylab.imshow(im) time pylab.savefig('a.png') /// }}} {{{id=2| /// }}}

Image Compression

{{{id=209| import pylab; import numpy A_image = numpy.mean(pylab.imread(DATA + 'quad.png'), 2) u,s,v = numpy.linalg.svd(A_image) S = numpy.zeros( A_image.shape ) S[:len(s),:len(s)] = numpy.diag(s) n = A_image.shape[0] @interact def svd_image(i = ("Eigenvalues (quality)",(20,(1..A_image.shape[0]//2)))): A_approx = numpy.dot(numpy.dot(u[:,:i], S[:i,:i]), v[:i,:]) g = graphics_array([matrix_plot(A_approx), matrix_plot(A_image)]) show(g, axes=False, figsize=(10,6)) html("Compressed to %.1f%% of size using %s eigenvalues."%( 100*(2.0*i*n+i)/(n*n), i)) ///
Eigenvalues (quality) 
}}} {{{id=208| /// }}} {{{id=210| /// }}} {{{id=6| /// }}} {{{id=12| /// }}}

Sage Matrices and Numpy Arrays

Sage has a notion of matrices, which model the matrices that come up mainly in pure mathematics (e.g., linear algebra).

{{{id=172| set_random_seed(2) A = random_matrix(QQ, 3, 3); A /// [ -1 -1 -1/2] [-1/2 2 0] [ 0 0 0] }}} {{{id=171| A.echelon_form() /// [ 1 0 2/5] [ 0 1 1/10] [ 0 0 0] }}} {{{id=173| A * A.transpose() /// [ 9/4 -3/2 0] [-3/2 17/4 0] [ 0 0 0] }}} {{{id=169| /// }}}

Numpy arrays are vastly different than Sage matrices, to put it mildly. Essentially every rule, constraint, convention, etc., is different. Numpy arrays serve a very different purpose than Sage matrices. They are the ultimate Python general n-dimensional dense array object. In contrast, Sage matrices model very mathematical objects with clear mathematically meaningful semantics and fast algorithms mainly for certain types of algebraic computations.  (In some cases Sage matrices are implemented using Numpy under the hood.)

Numpy array have similar functionality to Matlab arrays, given that they have very similar computational domains. See Numpy for Matlab Users for a table that compares and contrasts their functions. That page also has an excellent table showing equivalent commands in Matlab and Numpy.

{{{id=0| import numpy B = numpy.array(A); B /// array([[-1. , -1. , -0.5], [-0.5, 2. , 0. ], [ 0. , 0. , 0. ]]) }}} {{{id=180| /// }}}

Echelon form?

On Tue, Nov 18, 2008 at 14:21, Robert Young  wrote:
> Hi,
>
> Is there a method in NumPy that reduces a matrix to it's reduced row echelon
> form? I'm brand new to both NumPy and linear algebra, and I'm not quite sure
> where to look.

No, we don't have a function to do that. What do you need it for?
While many linear algebra books talk about it, I have never seen a
practical use for it outside of manual matrix computations.

-- 
Robert Kern

(See http://mail.scipy.org/pipermail/numpy-discussion/2008-November/038704.html.)

In doing linear algebra over the rational numbers (or any field with exact entries) reduced row echelon form is a critically important algorithm.  For matrix algebra with floating point numbers (reals, complexes, etc.) reduced row echelon form is basically pointless.  Numpy doesn't even have it. 

Numpy array multiplication (with a *) is componentwise multiplication of entries:

{{{id=177| B * B.T /// array([[ 1. , 0.5, -0. ], [ 0.5, 4. , 0. ], [-0. , 0. , 0. ]]) }}}

Use dot for matrix multiplication:

{{{id=178| numpy.dot(B, B.T) /// array([[ 2.25, -1.5 , 0. ], [-1.5 , 4.25, 0. ], [ 0. , 0. , 0. ]]) }}}

(NOTE: numpy does have numpy.matrix, where * does to matrix multiplication...)

{{{id=185| C = numpy.matrix(A) C * C.T /// matrix([[ 2.25, -1.5 , 0. ], [-1.5 , 4.25, 0. ], [ 0. , 0. , 0. ]]) }}} {{{id=175| /// }}} Create a numpy array as follows. For numeric types, one can and should explicitly give the datatype (dtype), and it can be any numerical type or generic Python objects. This is already much different than Sage's very mathematical matrices. {{{id=25| Z = numpy.array([[sqrt(2) + 3, random_matrix(QQ, 2)], [1.5, 2/3]]) Z /// array([[sqrt(2) + 3, [1/2 -1] [ -1 2]], [1.50000000000000, 2/3]], dtype=object) }}} {{{id=26| Z + Z /// array([[2*sqrt(2) + 6, [ 1 -2] [-2 4]], [3.00000000000000, 4/3]], dtype=object) }}}

Symbolics look funny above because Numpy arrays use the "wrong" formatting option when converting them to strings.   The following function is a pretty printer for Numpy arrays for the Sage notebook...

{{{id=30| def shownp(A): if len(A.shape) > 2: for i in range(A.shape[0]): shownp(A[i]) return if len(A.shape) == 1: A = A.reshape(A.shape[0], 1) nrows, ncols = A.shape s = r'\left(\begin{array}{' s += 'r'*ncols + '}\n' for i in range(nrows): s += ' & '.join([latex(A[i,j]) for j in range(ncols)]) + '\\\\\n' s += r'\end{array}\right)' s = s.replace('\\texttt','') html('$%s$'%s) /// }}} {{{id=31| shownp(Z) /// \left(\begin{array}{rr} \sqrt{2} + 3 & \left(\begin{array}{rr} \frac{1}{2} & -1 \\ -1 & 2 \end{array}\right)\\ 1.50000000000000 & \frac{2}{3}\\ \end{array}\right) }}} {{{id=66| # A 1-d array A = numpy.array([1..5]) shownp(A) /// \left(\begin{array}{r} 1\\ 2\\ 3\\ 4\\ 5\\ \end{array}\right) }}} Numpy arrays can have any number of dimensions! Think of these higher dimensional arrays as arrays of arrays (of arrays...). They are useful, e.g., in image processing, where there is one array for each color channel. {{{id=41| M = numpy.array([[[1,2], [3,4]], [[1/3,5], [10,2]], [[8,1], [-5,2]]], dtype=int) M /// array([[[ 1, 2], [ 3, 4]], [[ 0, 5], [10, 2]], [[ 8, 1], [-5, 2]]]) }}} {{{id=82| # The actual array entries are *NOT* Python int's! type(M[0,0,0]) /// }}} {{{id=83| M[0,0,0].item() /// 1 }}} {{{id=50| shownp(M) /// \left(\begin{array}{rr} 1 & 2\\ 3 & 4\\ \end{array}\right) \left(\begin{array}{rr} 0 & 5\\ 10 & 2\\ \end{array}\right) \left(\begin{array}{rr} 8 & 1\\ -5 & 2\\ \end{array}\right) }}} {{{id=73| # dtype: A data-type object that fully describes each # fixed-length item in the array. # The dtype attribute can be set to anything that can be # interpreted as a data type (numpy includes 21 of these). # Setting this attribute allows you to change the interpretation of # the data in the array. The new data type must be compatible with the array's # current data type. /// }}} {{{id=74| M.dtype /// dtype('int64') }}} {{{id=75| M.dtype = float /// }}} {{{id=76| shownp(M) /// \left(\begin{array}{rr} {4.94065645841e-324} & {9.88131291682e-324}\\ {1.48219693752e-323} & {1.97626258336e-323}\\ \end{array}\right) \left(\begin{array}{rr} 0.0 & {2.47032822921e-323}\\ {4.94065645841e-323} & {9.88131291682e-324}\\ \end{array}\right) \left(\begin{array}{rr} {3.95252516673e-323} & {4.94065645841e-324}\\ {nan} & {9.88131291682e-324}\\ \end{array}\right) }}} {{{id=77| M.dtype = numpy.int32 /// }}} {{{id=78| shownp(M) /// \left(\begin{array}{rrrr} 1 & 0 & 2 & 0\\ 3 & 0 & 4 & 0\\ \end{array}\right) \left(\begin{array}{rrrr} 0 & 0 & 5 & 0\\ 10 & 0 & 2 & 0\\ \end{array}\right) \left(\begin{array}{rrrr} 8 & 0 & 1 & 0\\ -5 & -1 & 2 & 0\\ \end{array}\right) }}} {{{id=84| shownp(M.astype(float)) /// \left(\begin{array}{rrrr} 1.0 & 0.0 & 2.0 & 0.0\\ 3.0 & 0.0 & 4.0 & 0.0\\ \end{array}\right) \left(\begin{array}{rrrr} 0.0 & 0.0 & 5.0 & 0.0\\ 10.0 & 0.0 & 2.0 & 0.0\\ \end{array}\right) \left(\begin{array}{rrrr} 8.0 & 0.0 & 1.0 & 0.0\\ -5.0 & -1.0 & 2.0 & 0.0\\ \end{array}\right) }}} {{{id=48| # flags: (not settable) special array-connected dictionary-like object with # attributes showing the state of flags in this array; # only the flags WRITEABLE, ALIGNED, and # UPDATEIFCOPY can be modified by setting # attributes of this object M.flags /// C_CONTIGUOUS : True F_CONTIGUOUS : False OWNDATA : True WRITEABLE : True ALIGNED : True UPDATEIFCOPY : False }}} {{{id=43| # shape: (settable) tuple showing the array shape; setting this # attribute re-shapes the array M.shape /// (3, 2, 4) }}} {{{id=67| M.shape = (3,4,2) shownp(M) /// \left(\begin{array}{rr} 1 & 0\\ 2 & 0\\ 3 & 0\\ 4 & 0\\ \end{array}\right) \left(\begin{array}{rr} 0 & 0\\ 5 & 0\\ 10 & 0\\ 2 & 0\\ \end{array}\right) \left(\begin{array}{rr} 8 & 0\\ 1 & 0\\ -5 & -1\\ 2 & 0\\ \end{array}\right) }}} {{{id=68| M.shape = (3,2,4); M /// array([[[ 1, 0, 2, 0], [ 3, 0, 4, 0]], [[ 0, 0, 5, 0], [10, 0, 2, 0]], [[ 8, 0, 1, 0], [-5, -1, 2, 0]]], dtype=int32) }}} {{{id=71| # Reshape makes a reshaped copy: shownp(M.reshape((4,3,2))) /// \left(\begin{array}{rr} 1 & 0\\ 2 & 0\\ 3 & 0\\ \end{array}\right) \left(\begin{array}{rr} 4 & 0\\ 0 & 0\\ 5 & 0\\ \end{array}\right) \left(\begin{array}{rr} 10 & 0\\ 2 & 0\\ 8 & 0\\ \end{array}\right) \left(\begin{array}{rr} 1 & 0\\ -5 & -1\\ 2 & 0\\ \end{array}\right) }}} {{{id=72| M.shape /// (3, 2, 4) }}} {{{id=44| # strides: (settable) tuple showing how many bytes must be jumped in # the data segment to get from one entry to the next M.strides /// (32, 16, 4) }}} {{{id=51| # ndim: (not settable) number of dimensions in array M.ndim /// 3 }}} {{{id=52| # data: (settable) buffer object loosely wrapping the array data # (only works for single-segment arrays) d = M.data; d /// }}} {{{id=54| d[0:10] /// '\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00' }}} {{{id=55| type(d) /// }}} {{{id=56| # size: (not settable) total number of elements M.size /// 24 }}} {{{id=46| # itemsize: (settable) one-dimensional, indexable iterator # object that acts somewhat like a 1-d array M.flat /// }}} {{{id=57| len(M.flat) /// 24 }}} {{{id=47| list(M.flat) /// [1, 0, 2, 0, 3, 0, 4, 0, 0, 0, 5, 0, 10, 0, 2, 0, 8, 0, 1, 0, -5, -1, 2, 0] }}} {{{id=63| M.flat[0] = 20090283 /// }}} {{{id=64| shownp(M) /// \left(\begin{array}{rrrr} 20090283 & 0 & 2 & 0\\ 3 & 0 & 4 & 0\\ \end{array}\right) \left(\begin{array}{rrrr} 0 & 0 & 5 & 0\\ 10 & 0 & 2 & 0\\ \end{array}\right) \left(\begin{array}{rrrr} 8 & 0 & 1 & 0\\ -5 & -1 & 2 & 0\\ \end{array}\right) }}} {{{id=61| # __array_interface__: (not settable) dictionary with keys (data, # typestr, descr, shape, strides) for compliance with Python side # of array protocol M.__array_interface__ /// {'descr': [('', ' }}} {{{id=65| # __array_priority__: (not settable) always 0.0 for base type ndarray M.__array_priority__ /// 0.0 }}}

{{{id=42| /// }}}

A 2d Numpy array:

{{{id=4| A = numpy.array([[1/3,2,3], [4,5,6], [7,8,9]], dtype=float) shownp(A) /// \left(\begin{array}{rrr} 0.333333333333 & 2.0 & 3.0\\ 4.0 & 5.0 & 6.0\\ 7.0 & 8.0 & 9.0\\ \end{array}\right) }}} {{{id=8| type(A[0,0]) /// }}} {{{id=11| A[0,0] = 10; shownp(A) /// \left(\begin{array}{rrr} 10.0 & 2.0 & 3.0\\ 4.0 & 5.0 & 6.0\\ 7.0 & 8.0 & 9.0\\ \end{array}\right) }}} {{{id=5| A.flags /// C_CONTIGUOUS : True F_CONTIGUOUS : False OWNDATA : True WRITEABLE : True ALIGNED : True UPDATEIFCOPY : False }}} {{{id=7| A.flags['WRITEABLE'] = False /// }}} {{{id=9| A.flags /// C_CONTIGUOUS : True F_CONTIGUOUS : False OWNDATA : True WRITEABLE : False ALIGNED : True UPDATEIFCOPY : False }}} {{{id=10| A[0,0] = 10 /// Traceback (most recent call last): File "", line 1, in File "_sage_input_324.py", line 10, in exec compile(u'open("___code___.py","w").write("# -*- coding: utf-8 -*-\\n" + _support_.preparse_worksheet_cell(base64.b64decode("QVswLDBdID0gMTA="),globals())+"\\n"); execfile(os.path.abspath("___code___.py"))' + '\n', '', 'single') File "", line 1, in File "/tmp/tmp2vGkEn/___code___.py", line 3, in exec compile(u'A[_sage_const_0 ,_sage_const_0 ] = _sage_const_10 ' + '\n', '', 'single') File "", line 1, in RuntimeError: array is not writeable }}} {{{id=13| # Mutability (writeability) is not strictly enforced once set, # since you can trivially change it to true, make a write, then # change the flag back. before = A.flags['WRITEABLE'] A.flags['WRITEABLE'] = True A[0,0] = 20 A.flags['WRITEABLE'] = before shownp(A) /// \left(\begin{array}{rrr} 20.0 & 2.0 & 3.0\\ 4.0 & 5.0 & 6.0\\ 7.0 & 8.0 & 9.0\\ \end{array}\right) }}} {{{id=19| # This is different than Sage matrix mutability! B = matrix(RDF, 3, 3, [1..9]) B.set_immutable() # B *can't* be changed back to being mutable... B[0,0] = 20 # Sage is much more about enforcing rigor, etc., since a lot of subtle # mathematical errors in complicated algebraic algorithms can arise # from the lax (but more user-friendly?) numpy approach. /// Traceback (most recent call last): # mathematical errors in complicated algebraic algorithms can arise File "", line 1, in File "/tmp/tmpk6hZHR/___code___.py", line 6, in B[_sage_const_0 ,_sage_const_0 ] = _sage_const_20 File "matrix0.pyx", line 1281, in sage.matrix.matrix0.Matrix.__setitem__ (sage/matrix/matrix0.c:5702) File "matrix0.pyx", line 404, in sage.matrix.matrix0.Matrix.check_mutability (sage/matrix/matrix0.c:3750) ValueError: matrix is immutable; please change a copy instead (i.e., use copy(M) to change a copy of M). }}} {{{id=17| # numpy arrays can be serialized and saved to disk save(M, '/tmp/M.sobj') print M.dumps() (M == loads(M.dumps())).all() /// cnumpy.core.multiarray _reconstruct qcnumpy ndarray qK�UbRq(KKKKcnumpy dtype qUi4K�KRq(KU\left(\begin{array}{rrrr} 20090283 & 0 & 2 & 0\\ 3 & 0 & 4 & 0\\ \end{array}\right) \left(\begin{array}{rrrr} 0 & 0 & 5 & 0\\ 10 & 0 & 2 & 0\\ \end{array}\right) \left(\begin{array}{rrrr} 8 & 0 & 1 & 0\\ -5 & -1 & 2 & 0\\ \end{array}\right) }}} {{{id=69| /// }}}

Converting between Numpy and Sage Matrices

{{{id=70| M = matrix(ZZ, 2, [17, 2, 4, -5]); show(M) ///
\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rr} 17 & 2 \\ 4 & -5 \end{array}\right)
}}} {{{id=93| type(M) /// }}} {{{id=87| A = M.numpy(); A /// array([[17, 2], [ 4, -5]]) }}} {{{id=88| type(A) /// }}} {{{id=186| A.dtype /// dtype('int64') }}}

WARNING: overflow can occur with numpy arrays!

{{{id=199| M = matrix(ZZ,1,[2^62]) A = M.numpy(); A /// array([[4611686018427387904]]) }}} {{{id=197| M*M /// [21267647932558653966460912964485513216] }}} {{{id=200| A.dot(A) /// array([[0]]) }}} {{{id=91| /// }}}

NOTE: All numpy array have exactly the same Python data type numpy.ndarray, whereas Sage's matrices all have their own types and there is an object inheritance scheme.

{{{id=92| B = matrix(ZZ, 3, [1..9]); B /// [1 2 3] [4 5 6] [7 8 9] }}} {{{id=94| type(B) /// }}} {{{id=95| M = matrix(CDF, 3, [1,2,3.4, 1.2, 2.5, -1.3, 2+I,2-I,e]); M /// [ 1.0 2.0 3.4] [ 1.2 2.5 -1.3] [ 2.0 + 1.0*I 2.0 - 1.0*I 2.71828182846] }}} {{{id=96| A = M.numpy(); A /// array([[ 1.00000000+0.j, 2.00000000+0.j, 3.40000000+0.j], [ 1.20000000+0.j, 2.50000000+0.j, -1.30000000+0.j], [ 2.00000000+1.j, 2.00000000-1.j, 2.71828183+0.j]]) }}} {{{id=97| A.dtype /// dtype('complex128') }}} {{{id=202| type(A) /// }}} {{{id=102| /// }}}

Numpy Arrays Having an Amazingly Rich Range of Functionality

{{{id=100| A = random_matrix(RDF,3).numpy() shownp(A) /// \left(\begin{array}{rrr} -0.690424129344 & -0.601202105805 & 0.941510332578\\ -0.812768082131 & 0.674948609905 & 0.217071372435\\ -0.823886509427 & 0.117422363199 & 0.67512079055\\ \end{array}\right) }}} {{{id=105| # max: return the largest value in A. Do not use max(A)... A.max() /// 0.94151033257764793 }}} {{{id=108| max(A) /// Traceback (most recent call last): File "", line 1, in File "_sage_input_345.py", line 10, in exec compile(u'open("___code___.py","w").write("# -*- coding: utf-8 -*-\\n" + _support_.preparse_worksheet_cell(base64.b64decode("bWF4KEEp"),globals())+"\\n"); execfile(os.path.abspath("___code___.py"))' + '\n', '', 'single') File "", line 1, in File "/tmp/tmp39kq1O/___code___.py", line 2, in exec compile(u'max(A)' + '\n', '', 'single') File "", line 1, in ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all() }}} {{{id=109| # min: Return the smallest value in A. Much better than min(A) A.min() /// -0.82388650942711705 }}} {{{id=110| # ptp: Difference of the largest to the smallest value of A A.ptp() /// 1.765396842004765 }}} {{{id=111| # clip: Return a new array where any element in A less than min is set to min # and any element less than max is set to max. shownp(A.clip(min=-0.5, max=0.5)) /// \left(\begin{array}{rrr} -0.5 & -0.5 & 0.5\\ -0.5 & 0.5 & 0.217071372435\\ -0.5 & 0.117422363199 & 0.5\\ \end{array}\right) }}} {{{id=112| # conj: return a new array with each of the elements of the input array # replaced by their complex conjugate B = random_matrix(CDF, 3).numpy() print "Matrix B = ", shownp(B) print "\nComplex conjugate = ", shownp(B.conj()) /// Matrix B = \left(\begin{array}{rrr} {(-0.630051803721-0.674837411052j)} & {(-0.0457999180133+0.363640413152j)} & {(-0.808158516069+0.707812488238j)}\\ {(0.811073097409+0.0573659164154j)} & {(0.0455576698897+0.962909597554j)} & {(-0.479694160045+0.232594815682j)}\\ {(-0.828714836632-0.254595969583j)} & {(0.413293002166-0.798046463186j)} & {(-0.834752991411-0.881384010874j)}\\ \end{array}\right) Complex conjugate = \left(\begin{array}{rrr} {(-0.630051803721+0.674837411052j)} & {(-0.0457999180133-0.363640413152j)} & {(-0.808158516069-0.707812488238j)}\\ {(0.811073097409-0.0573659164154j)} & {(0.0455576698897-0.962909597554j)} & {(-0.479694160045-0.232594815682j)}\\ {(-0.828714836632+0.254595969583j)} & {(0.413293002166+0.798046463186j)} & {(-0.834752991411+0.881384010874j)}\\ \end{array}\right) }}} {{{id=98| shownp(A) shownp(A.round()) /// \left(\begin{array}{rrr} -0.690424129344 & -0.601202105805 & 0.941510332578\\ -0.812768082131 & 0.674948609905 & 0.217071372435\\ -0.823886509427 & 0.117422363199 & 0.67512079055\\ \end{array}\right) \left(\begin{array}{rrr} -1.0 & -1.0 & 1.0\\ -1.0 & 1.0 & 0.0\\ -1.0 & 0.0 & 1.0\\ \end{array}\right) }}} {{{id=107| shownp(A) A.trace() /// \left(\begin{array}{rrr} -0.690424129344 & -0.601202105805 & 0.941510332578\\ -0.812768082131 & 0.674948609905 & 0.217071372435\\ -0.823886509427 & 0.117422363199 & 0.67512079055\\ \end{array}\right) 0.65964527111113913 }}} {{{id=113| # sum: Sum up all the elements of the array (or along some "axis") shownp(A) print "sum = ", A.sum() print "sum(A.flatten()) = ", sum(A.flatten()) /// \left(\begin{array}{rrr} -0.690424129344 & -0.601202105805 & 0.941510332578\\ -0.812768082131 & 0.674948609905 & 0.217071372435\\ -0.823886509427 & 0.117422363199 & 0.67512079055\\ \end{array}\right) sum = -0.302207358039 sum(A.flatten()) = -0.302207358039 }}} {{{id=114| # sum: sum along an "axis", i.e., i,axis -- I do *NOT* understand this. shownp(A) A.sum(0) # sum of the rows /// \left(\begin{array}{rrr} -0.690424129344 & -0.601202105805 & 0.941510332578\\ -0.812768082131 & 0.674948609905 & 0.217071372435\\ -0.823886509427 & 0.117422363199 & 0.67512079055\\ \end{array}\right) array([-2.32707872, 0.19116887, 1.8337025 ]) }}} {{{id=121| A[0,0:] /// array([-0.69042413, -0.60120211, 0.94151033]) }}} {{{id=122| A[1,0:] /// array([-0.81276808, 0.67494861, 0.21707137]) }}} {{{id=123| A[2,0:] /// array([-0.82388651, 0.11742236, 0.67512079]) }}} {{{id=124| sum(A[i,0:] for i in (0..2)) /// array([-2.32707872, 0.19116887, 1.8337025 ]) }}} {{{id=128| # mean -- return the average value shownp(A) A.mean() /// \left(\begin{array}{rrr} -0.690424129344 & -0.601202105805 & 0.941510332578\\ -0.812768082131 & 0.674948609905 & 0.217071372435\\ -0.823886509427 & 0.117422363199 & 0.67512079055\\ \end{array}\right) -0.033578595337720216 }}} {{{id=129| # var -- return the variance of the entries of A # (Like with sum above an axis can be specified.) # The variance is the mean of the squares of the differences from the mean. html(r'$$\frac{1}{N}\sum_{i=0}^{N-1} (a_i - \mu)^2$$') shownp(A) A.var() ///
\frac{1}{N}\sum_{i=0}^{N-1} (a_i - \mu)^2
\left(\begin{array}{rrr} -0.690424129344 & -0.601202105805 & 0.941510332578\\ -0.812768082131 & 0.674948609905 & 0.217071372435\\ -0.823886509427 & 0.117422363199 & 0.67512079055\\ \end{array}\right) 0.44733956598604219 }}} {{{id=130| # std -- return the standard deviation of the entries in the matrix # This is the square root of the variance. shownp(A) A.std() /// \left(\begin{array}{rrr} -0.690424129344 & -0.601202105805 & 0.941510332578\\ -0.812768082131 & 0.674948609905 & 0.217071372435\\ -0.823886509427 & 0.117422363199 & 0.67512079055\\ \end{array}\right) 0.66883448325130646 }}} {{{id=131| sqrt(A.var()) /// 0.66883448325130646 }}} {{{id=132| # prod -- product of all entries of self. A.prod() /// 0.0030394929110662927 }}} {{{id=133| # all -- all entries bool(...) to True A.all() /// True }}} {{{id=137| B = A.copy(); B[0,0] = 0 B.all() /// False }}} {{{id=134| # all -- any entry bool(...) to True A.any() /// True }}}

Summary of Array Calculation Methods

{{{id=142| # Lots of other functions that aren't methods! import numpy # numpy.cov -- compute the covariance matrix of data shownp(numpy.cov(A)) /// \left(\begin{array}{rrr} 0.841855387505 & 0.184499113396 & 0.565105909106\\ 0.184499113396 & 0.580586970423 & 0.448130061185\\ 0.565105909106 & 0.448130061185 & 0.57401880249\\ \end{array}\right) }}} {{{id=143| shownp(numpy.corrcoef(A)) /// \left(\begin{array}{rrr} 1.0 & 0.263901520932 & 0.812920436437\\ 0.263901520932 & 1.0 & 0.776260300837\\ 0.812920436437 & 0.776260300837 & 1.0\\ \end{array}\right) }}} {{{id=144| hist, bins = numpy.histogram(A, bins=4) shownp(bins) hist /// \left(\begin{array}{r} -0.823886509427\\ -0.382537298926\\ 0.0588119115753\\ 0.500161122076\\ 0.941510332578\\ \end{array}\right) array([4, 0, 2, 3]) }}} {{{id=146| B = random_matrix(RDF,100,100).numpy() hist, bins = numpy.histogram(B, bins=10) shownp(bins) hist /// \left(\begin{array}{r} -0.999728239189\\ -0.799755544168\\ -0.599782849147\\ -0.399810154126\\ -0.199837459105\\ 0.000135235916064\\ 0.200107930937\\ 0.400080625958\\ 0.600053320979\\ 0.800026016\\ 0.999998711021\\ \end{array}\right) array([ 993, 1047, 985, 979, 996, 1004, 990, 1006, 1016, 984]) }}} {{{id=140| B = random_matrix(ZZ,400,400,x=-9,y=9).numpy(dtype=int) hist, bins = numpy.histogram(B, bins=100) print hist points(list(enumerate(hist))) /// [9017 0 0 0 0 8928 0 0 0 0 0 8710 0 0 0 0 0 8967 0 0 0 0 0 8922 0 0 0 0 0 9030 0 0 0 0 0 8851 0 0 0 0 0 8779 0 0 0 0 0 9001 0 0 0 0 8909 0 0 0 0 0 8806 0 0 0 0 0 8927 0 0 0 0 0 8795 0 0 0 0 0 8939 0 0 0 0 0 8820 0 0 0 0 0 8768 0 0 0 0 0 8796 0 0 0 0 9035] }}} {{{id=148| /// }}} {{{id=147| /// }}}

Array Slicing

Numpy (and Sage) both have sophisticated array slicing.

{{{id=141| shownp(A) /// \left(\begin{array}{rrr} -0.690424129344 & -0.601202105805 & 0.941510332578\\ -0.812768082131 & 0.674948609905 & 0.217071372435\\ -0.823886509427 & 0.117422363199 & 0.67512079055\\ \end{array}\right) }}} {{{id=149| shownp(A[:2,]) /// \left(\begin{array}{rrr} -0.690424129344 & -0.601202105805 & 0.941510332578\\ -0.812768082131 & 0.674948609905 & 0.217071372435\\ \end{array}\right) }}} {{{id=150| shownp(A[:3,0:2]) /// \left(\begin{array}{rr} -0.690424129344 & -0.601202105805\\ -0.812768082131 & 0.674948609905\\ -0.823886509427 & 0.117422363199\\ \end{array}\right) }}} {{{id=151| shownp(A[1:3,0:2]) /// \left(\begin{array}{rr} -0.812768082131 & 0.674948609905\\ -0.823886509427 & 0.117422363199\\ \end{array}\right) }}} {{{id=152| shownp(A[1:3,1:3]) /// \left(\begin{array}{rr} 0.674948609905 & 0.217071372435\\ 0.117422363199 & 0.67512079055\\ \end{array}\right) }}} {{{id=153| B = matrix(A); show(B) # sage matrix ///
\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rrr} -0.690424129344 & -0.601202105805 & 0.941510332578 \\ -0.812768082131 & 0.674948609905 & 0.217071372435 \\ -0.823886509427 & 0.117422363199 & 0.67512079055 \end{array}\right)
}}} {{{id=154| show(B[1:3, 1:3]) ///
\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rr} 0.674948609905 & 0.217071372435 \\ 0.117422363199 & 0.67512079055 \end{array}\right)
}}} {{{id=155| show(B[:3, 0:2]) ///
\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rr} -0.690424129344 & -0.601202105805 \\ -0.812768082131 & 0.674948609905 \\ -0.823886509427 & 0.117422363199 \end{array}\right)
}}}

A key difference between Numpy and Sage slicing is that in Sage the slices are new copies, but in numpy they are references into the original matrix!

{{{id=157| shownp(A) B = A[0:2,0:2] /// \left(\begin{array}{rrr} -0.690424129344 & -0.601202105805 & 0.941510332578\\ -0.812768082131 & 0.674948609905 & 0.217071372435\\ -0.823886509427 & 0.117422363199 & 0.67512079055\\ \end{array}\right) }}} {{{id=158| B[0,0] = 100 shownp(B) /// \left(\begin{array}{rr} 100.0 & -0.601202105805\\ -0.812768082131 & 0.674948609905\\ \end{array}\right) }}} {{{id=159| shownp(A) /// \left(\begin{array}{rrr} 100.0 & -0.601202105805 & 0.941510332578\\ -0.812768082131 & 0.674948609905 & 0.217071372435\\ -0.823886509427 & 0.117422363199 & 0.67512079055\\ \end{array}\right) }}} {{{id=160| # But in Sage... C = matrix(A) # sage matrix show(C) ///
\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rrr} 100.0 & -0.601202105805 & 0.941510332578 \\ -0.812768082131 & 0.674948609905 & 0.217071372435 \\ -0.823886509427 & 0.117422363199 & 0.67512079055 \end{array}\right)
}}} {{{id=161| B = C[0:2, 0:2] B[0,0] = -999 show(B) ///
\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rr} -999.0 & -0.601202105805 \\ -0.812768082131 & 0.674948609905 \end{array}\right)
}}} {{{id=162| show(C) ///
\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rrr} 100.0 & -0.601202105805 & 0.941510332578 \\ -0.812768082131 & 0.674948609905 & 0.217071372435 \\ -0.823886509427 & 0.117422363199 & 0.67512079055 \end{array}\right)
}}}

There is a LOT more

Eigenvalues, eigenvectors, decompositions, several different Fourier transform methods, bit fiddling, random numbers, distributions, and a C-API, etc. ...

Cython also has builtin language extensions that make it easy to have very optimized access to Numpy arrays from Cython. This is the next topic we'll address.

{{{id=164| numpy.linalg /// }}} {{{id=167| numpy.linalg.svd(A) /// (array([[ -9.99933345e-01, -1.15423494e-02, 2.83363230e-04], [ 8.14685571e-03, -7.22741647e-01, -6.91070286e-01], [ 8.18137308e-03, -6.91021914e-01, 7.22787506e-01]]), array([ 100.01290514, 0.84745218, 0.51004688]), array([[-0.99993792, 0.00607543, -0.00934035], [ 0.00296187, -0.66318269, -0.7484517 ], [-0.01074153, -0.7484329 , 0.66312352]])) }}} {{{id=165| shownp(numpy.fft.fft(A)) /// \left(\begin{array}{rrr} {(100.340308227+0j)} & {(99.8298458866+1.33602816237j)} & {(99.8298458866-1.33602816237j)}\\ {(0.0792519002097+0j)} & {(-1.2587780733-0.396533319464j)} & {(-1.2587780733+0.396533319464j)}\\ {(-0.0313433556781+0j)} & {(-1.2201580863+0.482981005736j)} & {(-1.2201580863-0.482981005736j)}\\ \end{array}\right) }}} {{{id=203| /// }}} {{{id=205| /// }}} {{{id=206| /// }}}