\newpage \subsection{Numpy} Numpy is a library that, according to its website, is ``The fundamental package for scientific computing with Python''. If you prefer to read an online guide, the \hlhref{https://numpy.org/doc/stable/user/quickstart.html}{official quick start guide}\ is excellent. \subsubsection{Arrays} The heart of numpy is the \texttt{numpy.ndarray} class (it has the alias \texttt{numpy.array}). As the name would imply, it can be any dimension you would like. The most important attributes for this course are: \texttt{shape} (returns a tuple that indicates the number of elements for each dimension), \texttt{dtype} (indicates the data type of elements in the array), \texttt{ndim} (the number of dimensions, equal to \texttt{len(shape)}) To create an array, we can use a few functions: \drmvspace \begin{multicols}{2} \begin{itemize}[noitemsep] \item \texttt{np.array([...])} \item \texttt{np.zeros(shape)} \item \texttt{np.zeros\_like(arr)} \item \texttt{np.ones(shape)} \item \texttt{np.ones\_like(arr)} \item \texttt{np.empty\_like(arr)} \item \texttt{np.arange(start, stop, step)} \item \texttt{np.linspace(start, stop, num\_el)} \item \texttt{np.logspace(start, stop, num\_el)} \item \texttt{np.eye(N, M)} (\texttt{N} is rows, \texttt{M} is cols) \item \texttt{np.identity(N, M)} \item \texttt{np.fromfunction(f, (dim1, \dots))} \end{itemize} \end{multicols} \dnrmvspace To use complex numbers, we can write \texttt{a + b * 1.j} Another useful method is \texttt{np.meshgrid(x, y, \dots)}, which returns a coordinate grid and treats the input vectors as \texttt{x}, \texttt{y}, etc coordinates of point \texttt{i}. Arrays aren't usually copied, but you get a view. The \texttt{reshape} method below is an example of a case where you get a shallow copy (that still technically is a view). The values are still in the same object, but you get access to it in a different shape. To deep copy an array (i.e. create a new array), use \texttt{arr.copy()} \subsubsection{Operations} The same basic operations that Python supports are also supported by numpy, though they are executed on each element. \begin{itemize}[noitemsep] \item You can subtract a number from an ndarray \item You can subtract an ndarray from another ndarray (vector, matrix, ... difference) \item To compute a matrix product (or matrix-vector product), you can do \texttt{A @ B} or \texttt{np.dot(A, B)} \item \texttt{arr.sum()} sums up all elements and \texttt{arr.sum(axis=n)} sums up all elements on that axis (0 is column, 1 is row) \item \texttt{arr.cumsum()} computes the cumulative sum (i.e. each element in the output is the sum of all preceding elements). You can also use the axis argument again. \item \texttt{np.exp}, \texttt{np.sqrt}, etc operate element-wise on the array \item \texttt{np.where(condition, arr\_true, arr\_false)} returns a numpy array where if \texttt{condition} is true, element \texttt{i} is chosen from \texttt{arr\_true}, else from \texttt{arr\_false} \end{itemize} A useful trick to create a mask is to use \texttt{a < b} (or any other comparison), as that will return an array of booleans. For piecewise interpolation, a useful method is \texttt{np.searchsorted(arr.flat, vals\_insert, side='right')}, where \texttt{vals\_insert} are the values to be inserted and the \texttt{side} argument indicates on which side of the match they are to be inserted. For that though, the array needs to be sorted, for which we can use \texttt{np.argsort(arr, axis=-1)}. This will return the indices in the order that would sort the array along the given axis. If axis is unspecified, \texttt{-1} is used. To use these indices to sort an array, we can simply use \texttt{arr[np.argsort(arr)]}. Slicing and indexing works just as in Python (assume \texttt{a} is a numpy array): \begin{code}{python} a = np.arange(10) print(a[2]) # Outputs 2 (third element) print(a[-1]) # Outputs 9 (last element) print(a[-2]) # Outputs 8 (2nd to last element) print(a[2:5]) # Outputs [2, 3, 4] (elements 2, 3, and 4) print(a[2:5:-1]) # Outputs [4, 3, 2] (reversed) print(a[::-1]) # Reverses the array \end{code} So, the basic syntax for slicing is \texttt{a[start : stop : step]} and any of them can be omitted, though the corresponding colons cannot be omitted if you omit start. We can also iterate over a numpy array normally (the iterator variable will be the \texttt{i}-th element of the outermost array of the numpy array). To iterate over all elements of the array (i.e. the actual data values), we can use \texttt{arr.flat} to get a view (similar to a reference, not copied) that has length that corresponds to the sum of all elements of \texttt{arr.shape}. For \texttt{n}-d arrays, we can use \texttt{a[0, 1]} to access element \texttt{a[0][1]}, which is more efficient. \subsubsection{Shape manipulation} We can reshape an array by using \texttt{arr.reshape(dim1, dim2, \dots)}. This however returns the array with modified shape, whereas \texttt{arr.resize((dim1, dim2, \dots))} modifies the array directly (notice that here we have to pass a tuple!) To get a one-dimensional array, we can use \texttt{arr.ravel()}, after which the array looks the same as \texttt{arr.flat}, but the change is permanent \fhlc{Cyan}{Stacking arrays} \texttt{np.vstack((a, b))} adds array \texttt{b}'s elements to array \texttt{a} (i.e. stacks along \texttt{axis=0}). \texttt{np.hstack((a, b))} adds array \texttt{b}'s elements to the inner arrays of \texttt{a} (i.e. stacks along \texttt{axis=1}). \texttt{np.concatenate((a, b, \dots), axis=n)} does as the above, but applies it to axis \texttt{n} \fhlc{Cyan}{Splitting arrays} \texttt{np.hsplit(a, count)} splits \texttt{a} into \texttt{count} arrays (along \texttt{axis=0}) \texttt{np.vsplit(a, count)} splits \texttt{a} into \texttt{count} arrays (along \texttt{axis=1}) \texttt{np.array\_split(a, count, axis=n)} splits \texttt{a} into \texttt{count} arrays (along \texttt{axis=n})