{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Indexing and slicing\n", "\n", "Like a standard Python list, a NumPy `array` can be accessed using the normal indexing syntax. This includes the negative indexing in order to count from the end of the array:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import numpy as np" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = np.arange(10)\n", "a" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(0, 2, 9)" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[0], a[2], a[-1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For a multidimensional array, the index is a tuple of integers. Unlke a normal Python `list` it is possible to put more than one number in the square brackets rather than having to chain up multiple pairs of square brackets:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0, 1, 2],\n", " [3, 4, 5],\n", " [6, 7, 8]])" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = np.arange(9).reshape((3,3))\n", "a" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[1, 1]" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 0, 1, 2],\n", " [ 3, 4, 5],\n", " [ 6, 10, 8]])" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[2, 1] = 10\n", "a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In 2D, the first dimension corresponds to rows, the second to columns. \n", "\n", "For a multidimensional array, you can under-specify the indices so `a[1]` is interpreted by taking all elements in the unspecified dimensions. In this case all the columns of row number `1`." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([3, 4, 5])" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you want to get a specific column, you can use `...` as a placeholder like so:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([2, 5, 8])" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[..., 2]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Like a normal Python list, a NumPy array can also be sliced:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = np.arange(10)\n", "a" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([2, 3, 4, 5])" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[2:6] # index 2 to (but not including) 6" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([3, 4, 5, 6, 7])" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[3:-2] # index 4 to (but not including) the second-last entry" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([2, 5, 8])" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[2:9:3] # 2 to 9 in steps of 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that not all entries are required, the first defaults to `0`, the second to '1 past the end' and the third to `1`:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([4, 5, 6, 7, 8, 9])" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[4:] # index 4 to end of array" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0, 2, 4, 6, 8])" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[::2] # even-index entries" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is also possible to combine slicing with a multi-dimensional array:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 0, 1, 2, 3, 4, 5],\n", " [ 6, 7, 8, 9, 10, 11],\n", " [12, 13, 14, 15, 16, 17],\n", " [18, 19, 20, 21, 22, 23],\n", " [24, 25, 26, 27, 28, 29],\n", " [30, 31, 32, 33, 34, 35]])" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = np.arange(36).reshape((6,6))\n", "a" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([3, 4])" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[0, 3:5]" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[28, 29],\n", " [34, 35]])" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[4:, 4:]" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 2, 8, 14, 20, 26, 32])" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[:, 2]" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[12, 14, 16],\n", " [24, 26, 28]])" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[2::2, ::2]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also combine assignment and slicing:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 0, 1, 2, 3, 4, 10, 10, 10, 10, 10])" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = np.arange(10)\n", "a[5:] = 10\n", "a" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0, 1, 2, 3, 4, 4, 3, 2, 1, 0])" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "b = np.arange(5)\n", "a[5:] = b[::-1]\n", "a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Exercise: Indexing and slicing\n", "\n", "Try the different flavours of slicing, using `start`, `end` and `step`: starting from a `linspace()`, try to obtain odd numbers counting backwards, and even numbers counting forwards." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Exercise: Array creation\n", "\n", "Create the following arrays with correct data types (don't just paste them verbatim into a `np.array()` call):\n", "\n", "```\n", "[[1, 1, 1, 1],\n", " [1, 1, 1, 1],\n", " [1, 1, 1, 2],\n", " [1, 6, 1, 1]]\n", "```\n", "\n", "```\n", "[[0., 0., 0., 0., 0.],\n", " [2., 0., 0., 0., 0.],\n", " [0., 3., 0., 0., 0.],\n", " [0., 0., 4., 0., 0.],\n", " [0., 0., 0., 5., 0.],\n", " [0., 0., 0., 0., 6.]]\n", "```\n", "\n", "Par on course: 3 statements for each\n", "\n", "*Hint*: Individual array elements can be accessed similarly to a `list`, e.g. `[1]` or `a[1, 2]`.\n", "\n", "*Hint*: Examine the docstring for `diag()`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Exercise: Tiling for array creation\n", "\n", "Skim through the documentation for `np.tile()`, and use this function to construct the array:\n", "\n", "```\n", "[[4, 3, 4, 3, 4, 3],\n", " [2, 1, 2, 1, 2, 1],\n", " [4, 3, 4, 3, 4, 3],\n", " [2, 1, 2, 1, 2, 1]]\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Fancy indexing\n", "\n", "NumPy arrays can be indexed with slices, but also with boolean or integer arrays (*masks*). This method is called *fancy indexing*. It creates *copies not views*.\n", "\n", "Using a boolean mask:" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([10, 3, 8, 0, 19, 10, 11, 9, 10, 6, 0, 12, 7, 14, 17])" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.random.seed(3)\n", "a = np.random.randint(0, 20, 15)\n", "a" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([False, True, False, True, False, False, False, True, False,\n", " True, True, True, False, False, False])" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(a % 3 == 0) # an array with True where the condition \"a[i] % 3 == 0\" is true." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 3, 0, 9, 6, 0, 12])" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mask = (a % 3 == 0)\n", "multiples_of_three = a[mask] # or, a[a%3==0]\n", "multiples_of_three # extract a sub-array with the mask" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Indexing with a mask can be very useful to assign a new value to a sub-array:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([10, -1, 8, -1, 19, 10, 11, -1, 10, -1, -1, -1, 7, 14, 17])" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[a % 3 == 0] = -1\n", "a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also do fancy indexing with an array of integers, where the same index is repeated several times:" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 0, 10, 20, 30, 40, 50, 60, 70, 80, 90])" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = np.arange(0, 100, 10)\n", "a" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([20, 30, 20, 40, 20])" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[[2, 3, 2, 4, 2]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "New values can be assigned with this sort of indexing:" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 0, 10, 20, 30, 40, 50, 60, -100, 80, -100])" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[[9, 7]] = -100\n", "a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exercise\n", "\n", "Try using fancy indexing on the left and array creation on the right to assign values into an array, for instance by setting parts of a large 2D array to zero." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Continue to the [next section](numerical operations.ipynb)." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.2" } }, "nbformat": 4, "nbformat_minor": 2 }