{
"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",
"< [Fancy Indexing](02.07-Fancy-Indexing.ipynb) | [Contents](Index.ipynb) | [Structured Data: NumPy's Structured Arrays](02.09-Structured-Data-NumPy.ipynb) >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Sorting Arrays"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Up to this point we have been concerned mainly with tools to access and operate on array data with NumPy.\n",
"This section covers algorithms related to sorting values in NumPy arrays.\n",
"These algorithms are a favorite topic in introductory computer science courses: if you've ever taken one, you probably have had dreams (or, depending on your temperament, nightmares) about *insertion sorts*, *selection sorts*, *merge sorts*, *quick sorts*, *bubble sorts*, and many, many more.\n",
"All are means of accomplishing a similar task: sorting the values in a list or array.\n",
"\n",
"For example, a simple *selection sort* repeatedly finds the minimum value from a list, and makes swaps until the list is sorted. We can code this in just a few lines of Python:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"\n",
"def selection_sort(x):\n",
" for i in range(len(x)):\n",
" swap = i + np.argmin(x[i:])\n",
" (x[i], x[swap]) = (x[swap], x[i])\n",
" return x"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([1, 2, 3, 4, 5])"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x = np.array([2, 1, 4, 3, 5])\n",
"selection_sort(x)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As any first-year computer science major will tell you, the selection sort is useful for its simplicity, but is much too slow to be useful for larger arrays.\n",
"For a list of $N$ values, it requires $N$ loops, each of which does on order $\\sim N$ comparisons to find the swap value.\n",
"In terms of the \"big-O\" notation often used to characterize these algorithms (see [Big-O Notation](#Aside:-Big-O-Notation)), selection sort averages $\\mathcal{O}[N^2]$: if you double the number of items in the list, the execution time will go up by about a factor of four.\n",
"\n",
"Even selection sort, though, is much better than my all-time favorite sorting algorithms, the *bogosort*:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"def bogosort(x):\n",
" while np.any(x[:-1] > x[1:]):\n",
" np.random.shuffle(x)\n",
" return x"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([1, 2, 3, 4, 5])"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x = np.array([2, 1, 4, 3, 5])\n",
"bogosort(x)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This silly sorting method relies on pure chance: it repeatedly applies a random shuffling of the array until the result happens to be sorted.\n",
"With an average scaling of $\\mathcal{O}[N \\times N!]$, (that's *N* times *N* factorial) this should–quite obviously–never be used for any real computation.\n",
"\n",
"Fortunately, Python contains built-in sorting algorithms that are *much* more efficient than either of the simplistic algorithms just shown. We'll start by looking at the Python built-ins, and then take a look at the routines included in NumPy and optimized for NumPy arrays."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Fast Sorting in NumPy: ``np.sort`` and ``np.argsort``\n",
"\n",
"Although Python has built-in ``sort`` and ``sorted`` functions to work with lists, we won't discuss them here because NumPy's ``np.sort`` function turns out to be much more efficient and useful for our purposes.\n",
"By default ``np.sort`` uses an $\\mathcal{O}[N\\log N]$, *quicksort* algorithm, though *mergesort* and *heapsort* are also available. For most applications, the default quicksort is more than sufficient.\n",
"\n",
"To return a sorted version of the array without modifying the input, you can use ``np.sort``:"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([1, 2, 3, 4, 5])"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x = np.array([2, 1, 4, 3, 5])\n",
"np.sort(x)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you prefer to sort the array in-place, you can instead use the ``sort`` method of arrays:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1 2 3 4 5]\n"
]
}
],
"source": [
"x.sort()\n",
"print(x)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A related function is ``argsort``, which instead returns the *indices* of the sorted elements:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1 0 3 2 4]\n"
]
}
],
"source": [
"x = np.array([2, 1, 4, 3, 5])\n",
"i = np.argsort(x)\n",
"print(i)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The first element of this result gives the index of the smallest element, the second value gives the index of the second smallest, and so on.\n",
"These indices can then be used (via fancy indexing) to construct the sorted array if desired:"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([1, 2, 3, 4, 5])"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x[i]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Sorting along rows or columns"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A useful feature of NumPy's sorting algorithms is the ability to sort along specific rows or columns of a multidimensional array using the ``axis`` argument. For example:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[[6 3 7 4 6 9]\n",
" [2 6 7 4 3 7]\n",
" [7 2 5 4 1 7]\n",
" [5 1 4 0 9 5]]\n"
]
}
],
"source": [
"rand = np.random.RandomState(42)\n",
"X = rand.randint(0, 10, (4, 6))\n",
"print(X)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[2, 1, 4, 0, 1, 5],\n",
" [5, 2, 5, 4, 3, 7],\n",
" [6, 3, 7, 4, 6, 7],\n",
" [7, 6, 7, 4, 9, 9]])"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# sort each column of X\n",
"np.sort(X, axis=0)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[3, 4, 6, 6, 7, 9],\n",
" [2, 3, 4, 6, 7, 7],\n",
" [1, 2, 4, 5, 7, 7],\n",
" [0, 1, 4, 5, 5, 9]])"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# sort each row of X\n",
"np.sort(X, axis=1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Keep in mind that this treats each row or column as an independent array, and any relationships between the row or column values will be lost!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Partial Sorts: Partitioning\n",
"\n",
"Sometimes we're not interested in sorting the entire array, but simply want to find the *k* smallest values in the array. NumPy provides this in the ``np.partition`` function. ``np.partition`` takes an array and a number *K*; the result is a new array with the smallest *K* values to the left of the partition, and the remaining values to the right, in arbitrary order:"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([2, 1, 3, 4, 6, 5, 7])"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x = np.array([7, 2, 3, 1, 6, 5, 4])\n",
"np.partition(x, 3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Note that the first three values in the resulting array are the three smallest in the array, and the remaining array positions contain the remaining values.\n",
"Within the two partitions, the elements have arbitrary order.\n",
"\n",
"Similarly to sorting, we can partition along an arbitrary axis of a multidimensional array:"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[3, 4, 6, 7, 6, 9],\n",
" [2, 3, 4, 7, 6, 7],\n",
" [1, 2, 4, 5, 7, 7],\n",
" [0, 1, 4, 5, 9, 5]])"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.partition(X, 2, axis=1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The result is an array where the first two slots in each row contain the smallest values from that row, with the remaining values filling the remaining slots.\n",
"\n",
"Finally, just as there is a ``np.argsort`` that computes indices of the sort, there is a ``np.argpartition`` that computes indices of the partition.\n",
"We'll see this in action in the following section."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Example: k-Nearest Neighbors\n",
"\n",
"Let's quickly see how we might use this ``argsort`` function along multiple axes to find the nearest neighbors of each point in a set.\n",
"We'll start by creating a random set of 10 points on a two-dimensional plane.\n",
"Using the standard convention, we'll arrange these in a $10\\times 2$ array:"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [],
"source": [
"X = rand.rand(10, 2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To get an idea of how these points look, let's quickly scatter plot them:"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"