{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "*This notebook contains an excerpt from the [Python Data Science Handbook](http://shop.oreilly.com/product/0636920034919.do) by Jake VanderPlas; the content is available [on GitHub](https://github.com/jakevdp/PythonDataScienceHandbook).*\n", "\n", "*The text is released under the [CC-BY-NC-ND license](https://creativecommons.org/licenses/by-nc-nd/3.0/us/legalcode), and code is released under the [MIT license](https://opensource.org/licenses/MIT). If you find this content useful, please consider supporting the work by [buying the book](http://shop.oreilly.com/product/0636920034919.do)!*\n", "\n", "*No changes were made to the contents of this notebook from the original.*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "< [Comparisons, Masks, and Boolean Logic](02.06-Boolean-Arrays-and-Masks.ipynb) | [Contents](Index.ipynb) | [Sorting Arrays](02.08-Sorting.ipynb) >" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Fancy Indexing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the previous sections, we saw how to access and modify portions of arrays using simple indices (e.g., ``arr[0]``), slices (e.g., ``arr[:5]``), and Boolean masks (e.g., ``arr[arr > 0]``).\n", "In this section, we'll look at another style of array indexing, known as *fancy indexing*.\n", "Fancy indexing is like the simple indexing we've already seen, but we pass arrays of indices in place of single scalars.\n", "This allows us to very quickly access and modify complicated subsets of an array's values." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exploring Fancy Indexing\n", "\n", "Fancy indexing is conceptually simple: it means passing an array of indices to access multiple array elements at once.\n", "For example, consider the following array:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[51 92 14 71 60 20 82 86 74 74]\n" ] } ], "source": [ "import numpy as np\n", "rand = np.random.RandomState(42)\n", "\n", "x = rand.randint(100, size=10)\n", "print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Suppose we want to access three different elements. We could do it like this:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[71, 86, 14]" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[x[3], x[7], x[2]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, we can pass a single list or array of indices to obtain the same result:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([71, 86, 60])" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ind = [3, 7, 4]\n", "x[ind]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When using fancy indexing, the shape of the result reflects the shape of the *index arrays* rather than the shape of the *array being indexed*:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[71, 86],\n", " [60, 20]])" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ind = np.array([[3, 7],\n", " [4, 5]])\n", "x[ind]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Fancy indexing also works in multiple dimensions. Consider the following array:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 0, 1, 2, 3],\n", " [ 4, 5, 6, 7],\n", " [ 8, 9, 10, 11]])" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X = np.arange(12).reshape((3, 4))\n", "X" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Like with standard indexing, the first index refers to the row, and the second to the column:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 2, 5, 11])" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "row = np.array([0, 1, 2])\n", "col = np.array([2, 1, 3])\n", "X[row, col]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that the first value in the result is ``X[0, 2]``, the second is ``X[1, 1]``, and the third is ``X[2, 3]``.\n", "The pairing of indices in fancy indexing follows all the broadcasting rules that were mentioned in [Computation on Arrays: Broadcasting](02.05-Computation-on-arrays-broadcasting.ipynb).\n", "So, for example, if we combine a column vector and a row vector within the indices, we get a two-dimensional result:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 2, 1, 3],\n", " [ 6, 5, 7],\n", " [10, 9, 11]])" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X[row[:, np.newaxis], col]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here, each row value is matched with each column vector, exactly as we saw in broadcasting of arithmetic operations.\n", "For example:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0, 0, 0],\n", " [2, 1, 3],\n", " [4, 2, 6]])" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "row[:, np.newaxis] * col" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is always important to remember with fancy indexing that the return value reflects the *broadcasted shape of the indices*, rather than the shape of the array being indexed." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Combined Indexing\n", "\n", "For even more powerful operations, fancy indexing can be combined with the other indexing schemes we've seen:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[ 0 1 2 3]\n", " [ 4 5 6 7]\n", " [ 8 9 10 11]]\n" ] } ], "source": [ "print(X)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can combine fancy and simple indices:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([10, 8, 9])" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X[2, [2, 0, 1]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also combine fancy indexing with slicing:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 6, 4, 5],\n", " [10, 8, 9]])" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X[1:, [2, 0, 1]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And we can combine fancy indexing with masking:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 0, 2],\n", " [ 4, 6],\n", " [ 8, 10]])" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mask = np.array([1, 0, 1, 0], dtype=bool)\n", "X[row[:, np.newaxis], mask]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All of these indexing options combined lead to a very flexible set of operations for accessing and modifying array values." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example: Selecting Random Points\n", "\n", "One common use of fancy indexing is the selection of subsets of rows from a matrix.\n", "For example, we might have an $N$ by $D$ matrix representing $N$ points in $D$ dimensions, such as the following points drawn from a two-dimensional normal distribution:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(100, 2)" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mean = [0, 0]\n", "cov = [[1, 2],\n", " [2, 5]]\n", "X = rand.multivariate_normal(mean, cov, 100)\n", "X.shape" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using the plotting tools we will discuss in [Introduction to Matplotlib](04.00-Introduction-To-Matplotlib.ipynb), we can visualize these points as a scatter-plot:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "image/png": "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WD0nJ8etZgFdbNiwQbfccovGjh2rhx9+2O3mOC4tgkhWVpaGDx8e8etffPGFtm7dqgsvvLDts5ycHA0ePFjV1dU90cSkE6tPwxYvXqzly5drzpw5uuOOO5jqisBqfyJ+4ZHN6upqDRkypO3z6upqRungGc8//7zuv/9+zZw5U4sXL+4ygpeKWKwq6bPPPtONN96ovXv3tn1WX1+vXbt28XJIwKJFi7R8+XItXLhQd955JyEEriovL1efPn20cePGts/q6+u1bds2TZw40cWWASesXLlS9957ry6//HI98sgjaRFCpDQZEYnl3HPPVVlZmRYuXKi77rpLoVBIixYtUkFBgb7//e+73byktHHjRj399NOaM2eOLrnkEh04cKDtayeddJJyc3NdbB3SUa9evXTFFVdo8eLFKiws1KBBg7Ro0SINGDBAM2fOdLt5SHO7du3SL37xC82cOVPXXnutDh482Pa13r17Ky8vz8XWOYsgohP/gXr66af14IMPat68eTp27JimTp2qBx54QH369HG7eUkpvO99xYoVWrFiRYevXX/99brhhhvcaBbS3I033qiWlhbdeeedampq0qRJk/TMM8+kzW+e8K5169bp+PHjWr9+fZe6NrNmzdIDDzzgUsuc5wtROhQAALiENSIAAMA1BBEAAOAagggAAHANQQQAALiGIAIAAFxDEAEAAK4hiAAAANcQRAAAgGsIIgAAwDUEEQAA4BqCCAAAcM3/By7JEwZLdABBAAAAAElFTkSuQmCC\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "import seaborn; seaborn.set() # for plot styling\n", "\n", "plt.scatter(X[:, 0], X[:, 1]);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's use fancy indexing to select 20 random points. We'll do this by first choosing 20 random indices with no repeats, and use these indices to select a portion of the original array:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([15, 23, 5, 3, 97, 82, 32, 42, 74, 29, 24, 65, 60, 48, 13, 53, 7,\n", " 14, 76, 47])" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "indices = np.random.choice(X.shape[0], 20, replace=False)\n", "indices" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(20, 2)" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "selection = X[indices] # fancy indexing here\n", "selection.shape" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now to see which points were selected, let's over-plot large circles at the locations of the selected points:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiIAAAGgCAYAAACXJAxkAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAABMmklEQVR4nO3deZDkdX0//ufn/vTdPTO7vbM3OyDLuYrrkYhi2CKpYGnKmKqE6AYVFQ+OSAAVUKggRoWAJyk51IgFEo9CgxBF/VVZUhV01fBFkWthd5llrp2enj4/9+f3R28Pc89096fn0z39fPyDdm9/Pu99z+x8nvM+Xm/B930fRERERCEQw24AERER9S4GESIiIgoNgwgRERGFhkGEiIiIQsMgQkRERKFhECEiIqLQMIgQERFRaBhEiIiIKDRy2A1YDd/34Xntq7smikJbr99r2J/BY58Gi/0ZPPZpsNZDf4qiAEEQVvxzXRFEPM9HLlduy7VlWUQmE0OhUIHjeG25Ry9hfwaPfRos9mfw2KfBWi/92dcXgyStHEQ4NUNEREShYRAhIiKi0DCIEBERUWgYRIiIiCg0DCJEREQUGgYRIiIiCg2DCBEREYWGQYSIiIhCwyBCREREoWEQISIiotC0LYg88MADOP/883HGGWfgLW95Cx5++OF23YqIiGhd8n0fpaqNqaKJUtWG7wd3/kw7r92Itpw188Mf/hDXXHMNPvaxj+HNb34zHnzwQVxxxRXYtGkTXvWqV7XjlkREROtKvmji4NFpTBYMOK4PWRLQn9SxY1MC6bjW2rVLJg6PFtty7UYFHkR838cXv/hFXHjhhbjwwgsBAB/5yEfwu9/9Dr/+9a8ZRIiIiFaQKxh4/OAxFCs20jEVqizBclyM5CooVCycsau/6cCQL5l44vlJlA0n8Gs3I/Ag8vzzz+Po0aN461vfOuf1u+++u6XrynJ7ZpEkSZzzX2oN+zN47NNgsT+Dxz4NligKeGY4j6rlYnN/FIJQO8FWUUREdRljU1UMT5TQn9Jn3lst3/cxPFGC0YZrNyvwIHLo0CEAQKVSwUUXXYQnn3wSW7duxYc+9CGce+65TV1TFAVkMrEAW7lQMhlp6/V7DfszeOzTYLE/g8c+DUahbGF8qorNGxKIaAsf07KqoFS1IWsqkjG14WtXbB9bssnAr92swINIqVQCAHzsYx/DJZdcgiuvvBI/+clP8OEPfxjf+MY38Gd/9mcNX9PzfBQKlaCbCqCW4JPJCAqFKlzXa8s9egn7M3js02CxP4PHPg3WdNmC7bhwbQEl21nwvuf5KBSrmDhWhGvpDV07VzBQKFahiYAb8LXnSyYjqxolCzyIKIoCALjooovw9re/HQBwyimn4Mknn2w6iACA47T3m9t1vbbfo5ewP4PHPg0W+zN47NNgiAKgyBIMy4EiSQveNywHoiBAFISG+7v+uarpQFcXRoBWrt2swCf0Nm3aBAB4xSteMef1E088EcPDw0HfjoiIaF2JRxRszESQL1kLttT6vo982UJ/UkdMb3wsIabL6E/qyJeDv3azAg8ip556KmKxGB5//PE5rz/zzDPYvn170LcjIiJaVwRBwNDWdG3xaL4Kw3LgeT4My8FYvoqYLmPHpkRTi0kFQcCOTQnE2nDtZgUeeXRdx/ve9z589atfRTabxZlnnokf//jHePTRR/HNb34z6NsRERGtO31JHXuGBmbqiBQqNmRJwGBftOVaH+m4hjN29c/UEQny2s1oy9jLhz/8YUQiEdx2220YGxvD0NAQvvzlL+N1r3tdO25HRES07qQTGs4c6kfZcGA7HhRZREyXAxmtSMc1pIbUtly7UW2bBHrPe96D97znPe26PBER0bonCALiEaXrrt0IVp8hIiKi0DCIEBERUWgYRIiIiCg0DCJEREQUGgYRIiIiCg2DCBEREYWGQYSIiIhCwyBCREREoWEQISIiotAwiBAREVFoGESIiIgoNAwiREREFBoGESIiIgoNgwgRERGFhkGEiIiIQsMgQkRERKFhECEiIqLQMIgQERFRaBhEiIiIKDQMIkRERBQaBhEiIiIKDYMIERERhYZBhIiIiELDIEJEREShYRAhIiKi0DCIEBERUWgYRIiIiCg0DCJEREQUGgYRIiIiCg2DCBEREYWGQYSIiIhCwyBCREREoWEQISIiotAwiBAREVFoGESIiIgoNAwiREREFBoGESIiIgpNW4PICy+8gFe96lX4wQ9+0M7bEBERUZdqWxCxbRtXXnklKpVKu25BREREXa5tQeTLX/4yYrFYuy5PRERE60BbgshvfvMb3H///fjc5z7XjssTERHROiEHfcFCoYCrr74a1113HQYHBwO7riy3Z/BGksQ5/6XWsD+Dxz4NFvszeOzTYPVa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8IaQWtXdLrjggrCb0NXqa74GBwfnvL5x40aMjIyE0aSud+655+Lcc88NuxnrQjKZXPAz8+GHH8aRI0dw9tlnh9SqtdETQWR4eBj79u1b8v1f/epX2LBhAwDgqquuwuWXX47/+I//wLvf/W488MADC35wUWN9CgA//elPcdttt2H//v3YvXv3WjSxqzTan9S4arUKAFBVdc7rmqat+984qfv89re/xTXXXIN9+/at+7DXE0Ekm83ioYceWvL9+pQMAJxyyikAgNtuuw1vfvOb8f3vfx+XXHJJ29vYbRrp0/vuuw833ngjzj//fHziE59Yi+Z1nUb6k5qj6zqA2lqR+v8GaovVI5FIWM0iWuBnP/sZrrzySuzZswe33npr2M1pu54IIoqiYGhoaMn3jx49ij/84Q/4q7/6q5nXIpEItm7divHx8bVoYtdZqU/rbrnlFtx5553Yv38/rr32Wk51LWG1/UnNq49sjo+PY/v27TOvj4+Pc5SOOsa3v/1t3HTTTTjvvPNwyy23LBjBW4+4WBXAn/70J1x22WU4cuTIzGuFQgEvvPACHw4tuPnmm3HnnXfi6quvxnXXXccQQqHavXs34vE4HnvssZnXCoUCnnzySezduzfElhHV3Hvvvbjxxhvxzne+E1/4whd6IoQAPTIispI3velNOPnkk3H11Vfjk5/8JHzfx80334xMJoN3vOMdYTevKz322GO46667sH//frztbW/DxMTEzHvRaBSxWCzE1lEvUlUV73rXu3DLLbegr68PW7Zswc0334xNmzbhvPPOC7t51ONeeOEFfOYzn8F5552Hiy++GJOTkzPv6bqORCIRYuvai0EEtR9Qd911Fz73uc/hoosugmVZOPvss/HZz34W8Xg87OZ1pfq+93vuuQf33HPPnPcuueQSXHrppWE0i3rcZZddBsdxcN1118EwDLzmNa/B3Xff3TO/eVLn+slPfgLbtvHII48sqGvz9re/HZ/97GdDaln7CT5LhxIREVFIuEaEiIiIQsMgQkRERKFhECEiIqLQMIgQERFRaBhEiIiIKDQMIkRERBQaBhEiIiIKDYMIERERhYZBhIiIiELDIEJEREShYRAhIiKi0Pz/89BELJiSFTIAAAAASUVORK5CYII=\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.scatter(X[:, 0], X[:, 1], alpha=0.3)\n", "plt.scatter(selection[:, 0], selection[:, 1],\n", " facecolor='none', s=200);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This sort of strategy is often used to quickly partition datasets, as is often needed in train/test splitting for validation of statistical models (see [Hyperparameters and Model Validation](05.03-Hyperparameters-and-Model-Validation.ipynb)), and in sampling approaches to answering statistical questions." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Modifying Values with Fancy Indexing\n", "\n", "Just as fancy indexing can be used to access parts of an array, it can also be used to modify parts of an array.\n", "For example, imagine we have an array of indices and we'd like to set the corresponding items in an array to some value:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ 0 99 99 3 99 5 6 7 99 9]\n" ] } ], "source": [ "x = np.arange(10)\n", "i = np.array([2, 1, 8, 4])\n", "x[i] = 99\n", "print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can use any assignment-type operator for this. For example:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ 0 89 89 3 89 5 6 7 89 9]\n" ] } ], "source": [ "x[i] -= 10\n", "print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice, though, that repeated indices with these operations can cause some potentially unexpected results. Consider the following:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[6. 0. 0. 0. 0. 0. 0. 0. 0. 0.]\n" ] } ], "source": [ "x = np.zeros(10)\n", "x[[0, 0]] = [4, 6]\n", "print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Where did the 4 go? The result of this operation is to first assign ``x[0] = 4``, followed by ``x[0] = 6``.\n", "The result, of course, is that ``x[0]`` contains the value 6.\n", "\n", "Fair enough, but consider this operation:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([6., 0., 1., 1., 1., 0., 0., 0., 0., 0.])" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "i = [2, 3, 3, 4, 4, 4]\n", "x[i] += 1\n", "x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You might expect that ``x[3]`` would contain the value 2, and ``x[3]`` would contain the value 3, as this is how many times each index is repeated. Why is this not the case?\n", "Conceptually, this is because ``x[i] += 1`` is meant as a shorthand of ``x[i] = x[i] + 1``. ``x[i] + 1`` is evaluated, and then the result is assigned to the indices in x.\n", "With this in mind, it is not the augmentation that happens multiple times, but the assignment, which leads to the rather nonintuitive results.\n", "\n", "So what if you want the other behavior where the operation is repeated? For this, you can use the ``at()`` method of ufuncs (available since NumPy 1.8), and do the following:" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0. 0. 1. 2. 3. 0. 0. 0. 0. 0.]\n" ] } ], "source": [ "x = np.zeros(10)\n", "np.add.at(x, i, 1)\n", "print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The ``at()`` method does an in-place application of the given operator at the specified indices (here, ``i``) with the specified value (here, 1).\n", "Another method that is similar in spirit is the ``reduceat()`` method of ufuncs, which you can read about in the NumPy documentation." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example: Binning Data\n", "\n", "You can use these ideas to efficiently bin data to create a histogram by hand.\n", "For example, imagine we have 1,000 values and would like to quickly find where they fall within an array of bins.\n", "We could compute it using ``ufunc.at`` like this:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "np.random.seed(42)\n", "x = np.random.randn(100)\n", "\n", "# compute a histogram by hand\n", "bins = np.linspace(-5, 5, 20)\n", "counts = np.zeros_like(bins)\n", "\n", "# find the appropriate bin for each x\n", "i = np.searchsorted(bins, x)\n", "\n", "# add 1 to each of these bins\n", "np.add.at(counts, i, 1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The counts now reflect the number of points within each bin–in other words, a histogram:" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "ename": "ValueError", "evalue": "'steps' is not a valid value for ls; supported values are '-', '--', '-.', ':', 'None', ' ', '', 'solid', 'dashed', 'dashdot', 'dotted'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m/var/folders/cg/lw142vwd7dx5c7zvnbcqpqqr0000gn/T/ipykernel_94280/78696556.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# plot the results\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mplt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mplot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbins\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcounts\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlinestyle\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'steps'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m;\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/pyplot.py\u001b[0m in \u001b[0;36mplot\u001b[0;34m(scalex, scaley, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 2765\u001b[0m \u001b[0;34m@\u001b[0m\u001b[0m_copy_docstring_and_deprecators\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mAxes\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mplot\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2766\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mplot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mscalex\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mscaley\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2767\u001b[0;31m return gca().plot(\n\u001b[0m\u001b[1;32m 2768\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mscalex\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mscalex\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mscaley\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mscaley\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2769\u001b[0m **({\"data\": data} if data is not None else {}), **kwargs)\n", "\u001b[0;32m~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/axes/_axes.py\u001b[0m in \u001b[0;36mplot\u001b[0;34m(self, scalex, scaley, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1633\u001b[0m \"\"\"\n\u001b[1;32m 1634\u001b[0m \u001b[0mkwargs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcbook\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnormalize_kwargs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmlines\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mLine2D\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1635\u001b[0;31m \u001b[0mlines\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_get_lines\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1636\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mline\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mlines\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1637\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_line\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mline\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/axes/_base.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 310\u001b[0m \u001b[0mthis\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 311\u001b[0m \u001b[0margs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 312\u001b[0;31m \u001b[0;32myield\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_plot_args\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mthis\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 313\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 314\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mget_next_color\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/axes/_base.py\u001b[0m in \u001b[0;36m_plot_args\u001b[0;34m(self, tup, kwargs, return_kwargs)\u001b[0m\n\u001b[1;32m 536\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mlist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 537\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 538\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0ml\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0ml\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 539\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 540\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/axes/_base.py\u001b[0m in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 536\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mlist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 537\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 538\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0ml\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0ml\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 539\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 540\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/axes/_base.py\u001b[0m in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 529\u001b[0m \u001b[0mlabels\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mlabel\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mn_datasets\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 530\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 531\u001b[0;31m result = (make_artist(x[:, j % ncx], y[:, j % ncy], kw,\n\u001b[0m\u001b[1;32m 532\u001b[0m {**kwargs, 'label': label})\n\u001b[1;32m 533\u001b[0m for j, label in enumerate(labels))\n", "\u001b[0;32m~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/axes/_base.py\u001b[0m in \u001b[0;36m_makeline\u001b[0;34m(self, x, y, kw, kwargs)\u001b[0m\n\u001b[1;32m 349\u001b[0m \u001b[0mdefault_dict\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_getdefaults\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkw\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 350\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_setdefaults\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdefault_dict\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkw\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 351\u001b[0;31m \u001b[0mseg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmlines\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mLine2D\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkw\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 352\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mseg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkw\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 353\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/lines.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, xdata, ydata, linewidth, linestyle, color, marker, markersize, markeredgewidth, markeredgecolor, markerfacecolor, markerfacecoloralt, fillstyle, antialiased, dash_capstyle, solid_capstyle, dash_joinstyle, solid_joinstyle, pickradius, drawstyle, markevery, **kwargs)\u001b[0m\n\u001b[1;32m 364\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 365\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_linewidth\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlinewidth\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 366\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_linestyle\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlinestyle\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 367\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_drawstyle\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdrawstyle\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 368\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/lines.py\u001b[0m in \u001b[0;36mset_linestyle\u001b[0;34m(self, ls)\u001b[0m\n\u001b[1;32m 1120\u001b[0m \u001b[0mls\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'None'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1121\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1122\u001b[0;31m \u001b[0m_api\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcheck_in_list\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_lineStyles\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0mls_mapper_r\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mls\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mls\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1123\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mls\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_lineStyles\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1124\u001b[0m \u001b[0mls\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mls_mapper_r\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mls\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/opt/anaconda3/lib/python3.8/site-packages/matplotlib/_api/__init__.py\u001b[0m in \u001b[0;36mcheck_in_list\u001b[0;34m(_values, _print_supported_values, **kwargs)\u001b[0m\n\u001b[1;32m 127\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0m_print_supported_values\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 128\u001b[0m \u001b[0mmsg\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;34mf\"; supported values are {', '.join(map(repr, values))}\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 129\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmsg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 130\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 131\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mValueError\u001b[0m: 'steps' is not a valid value for ls; supported values are '-', '--', '-.', ':', 'None', ' ', '', 'solid', 'dashed', 'dashdot', 'dotted'" ] }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# plot the results\n", "plt.plot(bins, counts, linestyle='steps');" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Of course, it would be silly to have to do this each time you want to plot a histogram.\n", "This is why Matplotlib provides the ``plt.hist()`` routine, which does the same in a single line:\n", "\n", "```python\n", "plt.hist(x, bins, histtype='step');\n", "```\n", "\n", "This function will create a nearly identical plot to the one seen here.\n", "To compute the binning, ``matplotlib`` uses the ``np.histogram`` function, which does a very similar computation to what we did before. Let's compare the two here:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "NumPy routine:\n", "15.7 µs ± 47 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n", "Custom routine:\n", "10.5 µs ± 75.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n" ] } ], "source": [ "print(\"NumPy routine:\")\n", "%timeit counts, edges = np.histogram(x, bins)\n", "\n", "print(\"Custom routine:\")\n", "%timeit np.add.at(counts, np.searchsorted(bins, x), 1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Our own one-line algorithm is several times faster than the optimized algorithm in NumPy! How can this be?\n", "If you dig into the ``np.histogram`` source code (you can do this in IPython by typing ``np.histogram??``), you'll see that it's quite a bit more involved than the simple search-and-count that we've done; this is because NumPy's algorithm is more flexible, and particularly is designed for better performance when the number of data points becomes large:" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "NumPy routine:\n", "66.2 ms ± 431 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n", "Custom routine:\n", "93.1 ms ± 1.41 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n" ] } ], "source": [ "x = np.random.randn(1000000)\n", "print(\"NumPy routine:\")\n", "%timeit counts, edges = np.histogram(x, bins)\n", "\n", "print(\"Custom routine:\")\n", "%timeit np.add.at(counts, np.searchsorted(bins, x), 1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What this comparison shows is that algorithmic efficiency is almost never a simple question. An algorithm efficient for large datasets will not always be the best choice for small datasets, and vice versa (see [Big-O Notation](02.08-Sorting.ipynb#Aside:-Big-O-Notation)).\n", "But the advantage of coding this algorithm yourself is that with an understanding of these basic methods, you could use these building blocks to extend this to do some very interesting custom behaviors.\n", "The key to efficiently using Python in data-intensive applications is knowing about general convenience routines like ``np.histogram`` and when they're appropriate, but also knowing how to make use of lower-level functionality when you need more pointed behavior." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "< [Comparisons, Masks, and Boolean Logic](02.06-Boolean-Arrays-and-Masks.ipynb) | [Contents](Index.ipynb) | [Sorting Arrays](02.08-Sorting.ipynb) >" ] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.8.15" } }, "nbformat": 4, "nbformat_minor": 1 }