{ "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", "< [Computation on NumPy Arrays: Universal Functions](02.03-Computation-on-arrays-ufuncs.ipynb) | [Contents](Index.ipynb) | [Computation on Arrays: Broadcasting](02.05-Computation-on-arrays-broadcasting.ipynb) >" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Aggregations: Min, Max, and Everything In Between" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Often when faced with a large amount of data, a first step is to compute summary statistics for the data in question.\n", "Perhaps the most common summary statistics are the mean and standard deviation, which allow you to summarize the \"typical\" values in a dataset, but other aggregates are useful as well (the sum, product, median, minimum and maximum, quantiles, etc.).\n", "\n", "NumPy has fast built-in aggregation functions for working on arrays; we'll discuss and demonstrate some of them here." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Summing the Values in an Array\n", "\n", "As a quick example, consider computing the sum of all values in an array.\n", "Python itself can do this using the built-in ``sum`` function:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import numpy as np" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "50.61347504653063" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "L = np.random.random(100)\n", "sum(L)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The syntax is quite similar to that of NumPy's ``sum`` function, and the result is the same in the simplest case:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "50.613475046530645" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.sum(L)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, because it executes the operation in compiled code, NumPy's version of the operation is computed much more quickly:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "80.2 ms ± 87.4 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n", "196 µs ± 2.67 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n" ] } ], "source": [ "big_array = np.random.rand(1000000)\n", "%timeit sum(big_array)\n", "%timeit np.sum(big_array)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Be careful, though: the ``sum`` function and the ``np.sum`` function are not identical, which can sometimes lead to confusion!\n", "In particular, their optional arguments have different meanings, and ``np.sum`` is aware of multiple array dimensions, as we will see in the following section." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Minimum and Maximum\n", "\n", "Similarly, Python has built-in ``min`` and ``max`` functions, used to find the minimum value and maximum value of any given array:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(4.953550197694945e-06, 0.9999992030837538)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "min(big_array), max(big_array)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "NumPy's corresponding functions have similar syntax, and again operate much more quickly:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(4.953550197694945e-06, 0.9999992030837538)" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.min(big_array), np.max(big_array)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "61.1 ms ± 536 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n", "177 µs ± 1.56 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n" ] } ], "source": [ "%timeit min(big_array)\n", "%timeit np.min(big_array)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For ``min``, ``max``, ``sum``, and several other NumPy aggregates, a shorter syntax is to use methods of the array object itself:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "4.953550197694945e-06 0.9999992030837538 499989.5987223674\n" ] } ], "source": [ "print(big_array.min(), big_array.max(), big_array.sum())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Whenever possible, make sure that you are using the NumPy version of these aggregates when operating on NumPy arrays!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Multi dimensional aggregates\n", "\n", "One common type of aggregation operation is an aggregate along a row or column.\n", "Say you have some data stored in a two-dimensional array:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[0.72315587 0.20589938 0.76404341 0.03872716]\n", " [0.560761 0.53302816 0.53671074 0.25571248]\n", " [0.87808561 0.77727467 0.32049239 0.34527769]]\n" ] } ], "source": [ "M = np.random.random((3, 4))\n", "print(M)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By default, each NumPy aggregation function will return the aggregate over the entire array:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5.93916856782921" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.sum()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Aggregation functions take an additional argument specifying the *axis* along which the aggregate is computed. For example, we can find the minimum value within each column by specifying ``axis=0``:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.560761 , 0.20589938, 0.32049239, 0.03872716])" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.min(axis=0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The function returns four values, corresponding to the four columns of numbers.\n", "\n", "Similarly, we can find the maximum value within each row:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.76404341, 0.560761 , 0.87808561])" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.max(axis=1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The way the axis is specified here can be confusing to users coming from other languages.\n", "The ``axis`` keyword specifies the *dimension of the array that will be collapsed*, rather than the dimension that will be returned.\n", "So specifying ``axis=0`` means that the first axis will be collapsed: for two-dimensional arrays, this means that values within each column will be aggregated." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Other aggregation functions\n", "\n", "NumPy provides many other aggregation functions, but we won't discuss them in detail here.\n", "Additionally, most aggregates have a ``NaN``-safe counterpart that computes the result while ignoring missing values, which are marked by the special IEEE floating-point ``NaN`` value (for a fuller discussion of missing data, see [Handling Missing Data](03.04-Missing-Values.ipynb)).\n", "Some of these ``NaN``-safe functions were not added until NumPy 1.8, so they will not be available in older NumPy versions.\n", "\n", "The following table provides a list of useful aggregation functions available in NumPy:\n", "\n", "|Function Name | NaN-safe Version | Description |\n", "|-------------------|---------------------|-----------------------------------------------|\n", "| ``np.sum`` | ``np.nansum`` | Compute sum of elements |\n", "| ``np.prod`` | ``np.nanprod`` | Compute product of elements |\n", "| ``np.mean`` | ``np.nanmean`` | Compute median of elements |\n", "| ``np.std`` | ``np.nanstd`` | Compute standard deviation |\n", "| ``np.var`` | ``np.nanvar`` | Compute variance |\n", "| ``np.min`` | ``np.nanmin`` | Find minimum value |\n", "| ``np.max`` | ``np.nanmax`` | Find maximum value |\n", "| ``np.argmin`` | ``np.nanargmin`` | Find index of minimum value |\n", "| ``np.argmax`` | ``np.nanargmax`` | Find index of maximum value |\n", "| ``np.median`` | ``np.nanmedian`` | Compute median of elements |\n", "| ``np.percentile`` | ``np.nanpercentile``| Compute rank-based statistics of elements |\n", "| ``np.any`` | N/A | Evaluate whether any elements are true |\n", "| ``np.all`` | N/A | Evaluate whether all elements are true |\n", "\n", "We will see these aggregates often throughout the rest of the book." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example: What is the Average Height of US Presidents?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Aggregates available in NumPy can be extremely useful for summarizing a set of values.\n", "As a simple example, let's consider the heights of all US presidents.\n", "This data is available in the file *president_heights.csv*, which is a simple comma-separated list of labels and values:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "order,name,height(cm)\r\n", "1,George Washington,189\r\n", "2,John Adams,170\r\n", "3,Thomas Jefferson,189\r\n" ] } ], "source": [ "!head -4 data/president_heights.csv" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll use the Pandas package, which we'll explore more fully in [Chapter 3](03.00-Introduction-to-Pandas.ipynb), to read the file and extract this information (note that the heights are measured in centimeters)." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[189 170 189 163 183 171 185 168 173 183 173 173 175 178 183 193 178 173\n", " 174 183 183 168 170 178 182 180 183 178 182 188 175 179 183 193 182 183\n", " 177 185 188 188 182 185]\n" ] } ], "source": [ "import pandas as pd\n", "data = pd.read_csv('data/president_heights.csv')\n", "heights = np.array(data['height(cm)'])\n", "print(heights)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that we have this data array, we can compute a variety of summary statistics:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Mean height: 179.73809523809524\n", "Standard deviation: 6.931843442745892\n", "Minimum height: 163\n", "Maximum height: 193\n" ] } ], "source": [ "print(\"Mean height: \", heights.mean())\n", "print(\"Standard deviation:\", heights.std())\n", "print(\"Minimum height: \", heights.min())\n", "print(\"Maximum height: \", heights.max())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that in each case, the aggregation operation reduced the entire array to a single summarizing value, which gives us information about the distribution of values.\n", "We may also wish to compute quantiles:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "25th percentile: 174.25\n", "Median: 182.0\n", "75th percentile: 183.0\n" ] } ], "source": [ "print(\"25th percentile: \", np.percentile(heights, 25))\n", "print(\"Median: \", np.median(heights))\n", "print(\"75th percentile: \", np.percentile(heights, 75))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We see that the median height of US presidents is 182 cm, or just shy of six feet.\n", "\n", "Of course, sometimes it's more useful to see a visual representation of this data, which we can accomplish using tools in Matplotlib (we'll discuss Matplotlib more fully in [Chapter 4](04.00-Introduction-To-Matplotlib.ipynb)). For example, this code generates the following chart:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Unable to revert mtime: /Library/Fonts\n" ] } ], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "import seaborn; seaborn.set() # set plot style" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjYAAAHJCAYAAACWmnNkAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA+WUlEQVR4nO3deXxMZ///8ffMEEISxBatUooQzaaRWIu0aG13de+NokVJfd12Wm5Lb7VULLHEXlqlVbV00dbdUtq7VSql1aKtFq2opLVFSpDM/P7wy9RIMJmMzMzxej4eeSS5zpnrfM6Va2beOefMjMlms9kEAABgAGZPFwAAAOAuBBsAAGAYBBsAAGAYBBsAAGAYBBsAAGAYBBsAAGAYBBsAAGAYBBsAAGAYBBvAA7zhfTGpAb6MuYOrIdjA540cOVLx8fFXXd6tWzd169atQH3Onj1boaGhbr/NhQsXNGnSJL377rvXXC8+Pl6hoaH2r3r16ikmJkZPPPGE3n777Tzrh4aGavbs2U7Xunr1ak2ZMuW66105dgXdztVkZGRoxIgR2rlz51W35WnZ2dl67rnn1KBBAzVo0EBffvllnnW2b9+u0NBQbd++Pd8+1q5dq9DQUB05csSh32XLlqlz586KiopSdHS0OnfurMWLF+vChQvXrCl3jl3+FRYWpri4OD377LP66aefCrfTTspvv6505MgRhYaGau3atW7ffkpKip555hm39wtjKObpAgBv9Mgjj6h58+Zu7zc9PV3Lli3TpEmTrrtuixYtlJCQIOnSk+HJkyf1/vvva/jw4dq/f79GjBhhX3fVqlUKCQlxuo558+YpNjb2uuuNHTvW6T4LYt++fVq/fr0efPDBG74tV3322Wdau3atEhIS1KRJE4WFhbml33//+9/auHGjevfurfDwcNlsNu3cuVOzZs3S119/reTk5Ov2sWrVKvvPOTk5Onr0qGbMmKEuXbpow4YNqlixoltqvZqWLVtq1apVqlSp0g3dztWsXr1aBw4c8Mi24f0INkA+QkJCChQUboTg4GBFRUU5tLVu3Vrly5fXyy+/rHvvvVd33XWXJOVZz11q1ap1Q/r19LaccerUKUnSgw8+qNtuu80tfR49elTr1q3T+PHj9dhjj9nbmzdvruDgYE2cOFHffvutIiIirtnPlX/vu+66S1WqVFGXLl20bt069enTxy31Xk1wcLCCg4Nv6DYAV3EqCjeljz/+WA8++KDCw8PVtGlTTZgwQWfPnrUvz++00pIlS3TPPfcoIiJCjz/+uDZv3pzvaYgtW7aoU6dOCg8PV9u2bbV+/XpJlw7N33PPPZKk55577pqnz65lwIAB8vPz0xtvvGFvu/IU0fLly3XfffcpPDxczZs317hx45SZmSnp0mmu1NRUrVu3zn46Ye3atQoLC9Pq1avVrFkz3X333frpp5/yPT2UmZmpoUOHKjo6Wo0bN9aECRN07tw5+/L8bnP5KZvt27frySeflCQ9+eST9nWvvN358+c1d+5c+360adNGCxculNVqddjWqFGjtHDhQrVs2VLh4eF6/PHH9c0331xzDHNycrRixQp17NhRERERatmypRITE3X+/HlJl05vjhw5UpJ07733uu0U2Z9//imbzZbv9SEdO3bU4MGDFRQU5FLfd955pyQpNTVV0qU53Lp1a82ZM0dxcXG69957dfLkSUmXjni0b99ed955p1q2bKnZs2crOzvb3teJEyc0dOhQNW3aVOHh4frHP/5hn8dS/qei/vvf/6pTp06KiIhQ586dtX///jw1njp1SmPGjFGTJk0UHh6uRx99VNu2bXNYJzQ0VCtWrNCoUaMUGxur6OhoDRgwQH/++aekS3+bdevWKTU11eFU1/vvv2/ffqNGjTR06FClp6e7NJbwbRyxgWFc/sB8OZvNJpPJZP/93Xff1dChQ9WxY0cNHDhQqampmjFjhg4cOKClS5c6rJtrzpw5mjt3rp5++mk1atRIn332mQYNGpTv9saMGaOBAweqUqVKWrBggUaOHKm6deuqZs2amjNnjvr3769+/fqpTZs2Lu1nUFCQIiIilJKSku/yDRs2aMqUKRoxYoRCQ0P1yy+/aMqUKcrKytLkyZM1Z84c9enTR2FhYUpISLCfTsjJydH8+fM1YcIEnThx4qpHUJYvX667775bM2fO1MGDBzVjxgydPHlS06ZNc6r++vXra8yYMXrhhRc0ZswYxcXF5VnHZrOpb9++2r17t5599lnVq1dP27dv18yZM/Xbb7/pP//5j33djRs36o477tDo0aNls9k0ZcoUDRgwQJs3b5bFYsm3hjFjxmj9+vXq1auXYmNjtXfvXs2dO1f79u3T4sWLlZCQoJCQEM2bN09z5sxRjRo1nNq366lbt66qVKmiSZMm6YcfflCrVq3UoEEDBQQEKDg4uFDXjRw8eFCSVK1aNXvb0aNH9dFHH2n69Ok6efKkypUrpwULFmjGjBnq2rWrnnvuOe3bt0+zZ8/W77//rokTJ0qShg0bpuPHj2v8+PEqXbq03nnnHY0YMUJVqlTJ9++1efNmDRgwQO3bt9fQoUO1f/9+DRs2zGGd8+fPq3v37vrzzz81aNAgVapUSWvWrFGvXr20ePFiNW7c2L7ujBkz1Lp1a02fPl2//fabJk2apGLFimn69OlKSEjQiRMntHfvXs2ZM0fVqlVTSkqKhg4dqoSEBDVs2FDHjh3T1KlTNWTIEC1fvtzlMYVvItjAEFJTU1W/fv2rLs+9nsRmsykxMVHNmzdXYmKiffntt9+uHj16aOvWrWrZsqXDbc+ePatFixapS5cuGjp0qCSpWbNmOnfunMO1DrkmTJigu+++W5J02223qU2bNtqxY4fq1q2revXqSbr05FOYazYqVKig7777Lt9l27dv16233qouXbrIbDYrNjZWpUqVsv+3HhYWJj8/v3xPdfXt2zfP/l+pRo0aSk5OltlsVosWLWQymTRp0iQlJCTojjvuuG7tAQEB9tBUq1atfAPUp59+qi+++EJTp05Vp06dJElNmzZVyZIllZSUpO7du9tvl52drSVLliggIECS9Ndff2nEiBHat2+f/SjG5Q4cOKC33npLAwcOVL9+/ex9V6pUScOHD9enn36qFi1a2ANCvXr1VLVq1evulzP8/Py0cOFCDR8+XCtXrtTKlStlNptVv3593XffferatatKlix53X4uD/FZWVnav3+/Jk6cqMDAQPt45a43YsQINWnSRJJ05swZzZs3T4899phGjx4t6dJcLlu2rEaPHq2ePXuqdu3a2rFjhxISEnTvvfdKkuLi4lS2bNmrBsW5c+eqfv369nCbO/8vD7tvv/229u/frzfffFORkZH29bp166bExEStWbPGvm6dOnUcrkP79ttv9eGHH0q6dN8JDg6Wn5+fff6+9dZbKlGihHr37q0SJUpIksqWLas9e/bk+ccGxsepKBhCxYoV9dZbb+X7dXng+eWXX3Ts2DHFx8crOzvb/tWwYUMFBATo888/z9P37t27lZWVpfvuu8+hvUOHDvnWEhMTY/8599qMjIwMd+ymUxo1aqRDhw7pwQcfVHJysvbu3auOHTuqe/fu171tnTp1rrtO27ZtZTb//dDRpk0b2Wy2fF815KodO3bIYrGoXbt2Du25T9qXn/6rVauWPdRIUuXKlSXJ4fTYlX1Ll079XK59+/ayWCxXfYVTfpx9wrx8vTp16mj9+vX2cBUXF6effvpJU6dO1QMPPKATJ05ct7/69evbv+666y516dJF58+f1+zZs/NcOHz533TXrl06d+5cnvmfe1o0d/7HxcVp9uzZ+te//qW1a9fqxIkTGjFihMPczpWVlaXvv//efpo11/333+/w+7Zt21SxYkXVr1/fvt2cnBy1atVK3333nU6fPm1f98rAHRISctW/pyQ1bNhQWVlZ6tixo2bMmKGUlBQ1a9ZM/fv3J9TchDhiA0Pw8/NTeHh4vstKly5t/zn3gtDx48dr/PjxedbN75x87hPNlRdLVqhQId/tlSpVyv5zbgBw93tupKWlXfXi5nbt2slqtWrlypWaM2eOkpKSdOutt2rIkCFq3779NfstX778dbd95X7n3sad4e306dMqV66cihVzfIjKfdI+c+aMvc3f399hndwxv/xanCv7vryvXMWKFVO5cuUc+r6e3G1f7WXaue1X1ihJ4eHhCg8PV79+/XTu3DktXbpUSUlJWrRokcMr3vLz1ltv2X8uXry4KlaseNW/3eV/r9z5f7WLi3Pn/4wZMzR//nx98MEH+vDDD2U2m9WkSRONGzcuz4XUp0+fls1my3P/uPIVU6dOndIff/xx1SOrf/zxh8qUKSMp/7/pte5D0dHRWrhwoZYtW6YlS5Zo/vz5qlixonr37u1UoIexEGxwU8m9MHP48OH5vtw594H1crkB4sSJE6pZs6a93Zn/rG+E06dP6/vvv9c//vGPq67ToUMHdejQQWfOnNH//vc/LVq0SMOGDVNMTIz9iIarrgwwf/zxhyTHUJSTk+OwzuUXZjujTJkyOnnypLKzsx3CTe4Tb7ly5QrU35V9S5fqvvwU08WLF+3XoTgrNxxd7SLVY8eOyc/Pz77NKVOm6JNPPrGfVsnl7++vhIQEbdy40amXMV8txF9P7vxPTEzU7bffnmd5bggKDAzUsGHDNGzYMP3yyy/atGmTkpOTNX78eC1evNjhNmXLlpXZbLZf3JsrN0TlCgwM1O233+5wCvhyhT3d17x5czVv3lznzp3Tl19+qVdffVUTJ05UVFSU/dQXbg6cisJNpWbNmipfvryOHDli/485PDxcISEhmjZtmvbu3ZvnNnXr1lVgYKD++9//OrRv3LixwNu/2jUKBTF//nxdvHjR4eXClxs4cKD69+8v6dKTyf3336+EhATl5OTYn4AvP5VUUJ999pnD7xs2bJDJZLIHxYCAAB07dsxhna+//trh9+uNQ2xsrHJycvT+++87tL/zzjuSZH+Zuyty67zyTRI3bNignJycAvUdEhKiatWq5Qkq0qVw9/HHH6thw4b2/a1Ro4YOHjyYZ7+kS9cGpaenO3U60FWRkZEqXry40tLSHOZ/8eLFNW3aNB05ckSpqalq0aKFfZ9q1qyp3r17q0mTJnn+rpJUokQJRUdH67///a/DUZXNmzc7rBcbG6vff/9d5cuXd9j2tm3btHjx4gLdN66cv1OmTNHDDz8sm80mf39/tWrVyn7U6/fff3e6XxgDR2xwU7FYLBo0aJDGjBkji8WiVq1aKSMjQ8nJyUpLS8v3MHlAQIB69eqlWbNmyd/fX7GxsdqxY4def/11SQULCYGBgZIuXW9wxx13XPM/yRMnTmj37t2SLj1JHj9+XBs3btR7772nvn37XvW/9kaNGmns2LGaMmWK7r77bmVkZGjOnDm6/fbbVbduXUmX/nPfu3evduzYcd33TLnSd999p1GjRqlDhw7as2ePZs2apYcffth+BKBVq1bavHmzXnzxRd17771KSUlxeKnw5eOwZcsWlSlTxl5XrrvvvltxcXEaO3as0tPTFRYWph07dmjRokXq3Llzod7zplatWurcubPmzJmjrKwsxcXFad++ffaXRRf0jRmHDh2qgQMHqm/fvnrooYdUrlw5paen64033lBqaqomT55sX/eBBx7Qu+++q+HDh2v79u1q0aKFgoKCdOjQIb366qsqWbKknnrqKZf37XrKlSunXr16KSkpSZmZmYqLi1NaWpqSkpJkMpnsIT4kJEQTJkxQZmamqlWrpu+++05bt2696qu2Bg8erO7du6t///567LHHdOjQIc2bN89hnQcffFCvvfaaevbsqb59+6pKlSr64osvtGjRInXt2lXFixd3ej+CgoL0559/auvWrapXr54aN26spUuXauTIkerUqZMuXryoxYsXq2zZsmrUqFGhxgy+h2CDm84jjzyi0qVLa/HixVq1apVKlSqlBg0aKDEx8apvxPbM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xtr+/e9/2w4dOmTbunWrrVGjRrbJkyfb17kZ55arY8W8umTXrl22unXr2saMGWM7cOCAbdOmTbamTZvakpOT7evcjPOqIAg2XurYsWO2p59+2hYVFWW77777HCb+mjVrbFFRUbY//vjD3vbpp5/a7rnnHvuTz7x582ydOnUq8ro94VpjdaV3333XFhYWZtu/f7/NZrsUEqOjo20rV660r3P69GlbRESE7b333rvhtXtCYcbLZmNu5froo49sderUsd/nbDabbeLEibYOHTrYbLabb24VZqxsNuZVrmeffdb28MMPO6z/9ttv2yIjI23nz5+/6eaVKzgV5aW+//57lSlTRu+8844iIyMdln322Wdq1KiRKlSoYG9r3ry5Pv74YwUEBEiSfvjhB9WqVatIa/aUa43V5c6ePauXXnpJ3bt3V2hoqCRp//79+uuvv9SoUSP7ekFBQQoLC9NXX311w2v3hMKMl8TcylW2bFlJ0uuvv66cnBwdOXJEW7duta93s82twoyVxLzKdfDgwTwf8BwWFqZz587p22+/venmlSt84rOibkbx8fGKj4/Pd9mhQ4cUExOjuXPnav369crOzlazZs00bNgwBQUFSZJ+/PFHVaxYUf/85z916NAhVa9eXQkJCWrevHlR7kaRuNZYXe6NN97QX3/9pX79+tnbjh07JkmqUqWKw7qVKlXS77//7t5CvURhxktibuWKiYlRnz59lJSUpBkzZignJ0exsbH697//Lenmm1uFGSuJeZWrYsWKeeZHamqqJOn48eMymUySbp555QqO2PigzMxMrV+/Xj/88IOmTZumF154QSkpKUpISJDNZtOFCxd06NAhZWZmauDAgVq4cKHCw8PVu3dvbdu2zdPle0ROTo6WL1+uf/7znwoMDLS3nzt3TpLyfKBqiRIldP78+SKt0ZtcbbyYW3/LyMjQoUOH1KVLF61evVpJSUn69ddfNW7cOEnMrctdb6yYV3978MEHtXHjRq1fv14XL17U4cOHNXPmTJlMJl24cIF55QSO2Pig4sWLq1SpUpo2bZqKFy8uSSpTpoweeeQR7dmzRxEREfrqq69UrFgx++S/88479fPPP2vJkiVq3LixJ8v3iB07dujo0aN69NFHHdpLliwp6dIDa+7PknT+/Hn5+/sXaY3e5Grj5efnx9z6/xITE5WRkaHZs2dLkurXr68yZcqoR48e6t69O3PrMtcbq7p16zKv/r9OnTrp2LFjGj9+vJ5//nmVK1dOw4YN08iRIxUYGGgPL8yrq+OIjQ8KCQlRjRo17KFGkmrXri1JOnLkiCSpVKlSeRJ9nTp1lJaWVnSFepGPP/5YERERuu222xzacw/npqenO7Snp6crJCSkyOrzNlcbL4m5lSslJUXh4eEObbnXSxw8eJC5dZnrjZXEvLpcnz59lJKSok8++USffvqp7rzzTtlsNlWvXp155QSCjQ+KiYnR/v37lZWVZW/78ccfJUnVq1fX/v37FR0drZ07dzrc7rvvvrtpLs67UkpKisPFdrnq1q2rgIAAbd++3d6WkZGhvXv35rmA72ZytfFibv0tJCREP/zwg0Nb7v3w9ttvZ25d5npjxbz624oVKzR27FiZzWZVrlxZFotFH374oapWraoaNWowr5xAsPFBjz/+uCwWi4YMGaIff/xRKSkpGj16tOLi4lS/fn3VqVNHtWvX1vjx47Vz5079/PPPmjRpknbv3q2+fft6uvwil5OTowMHDqhOnTp5lvn5+alr165KTEzUpk2btH//fg0aNEghISFq3bq1B6r1vGuNF3Prbz179tRnn32mmTNn6tdff9W2bds0cuRItWjRQvXq1WNuXeZ6Y8W8+lutWrW0evVqrV69WqmpqVq1apXmz5+vIUOGSOIxyxlcY+ODgoODtWLFCk2aNEmPPvqo/Pz8dO+99+q5556TJJnNZs2fP1+JiYkaOHCgMjIyFBYWpqVLlzq8bPdmcerUKV28eNH+ktMrDRgwQNnZ2Ro9erSysrLUsGFDLVmyJM9h8ZvFtcaLufW3Zs2aacGCBZo7d65eeeUVlStXTq1bt9a//vUv+zrMrUuuN1bMq7/FxcXpxRdf1Pz58zVhwgRVr15dU6dO1f33329fh3l1bSabzWbzdBEAAADuwKkoAABgGAQbAABgGAQbAABgGAQbAABgGAQbAABgGAQbAABgGAQbAABgGAQbAIbD23MBNy+CDQCnxcfHa+TIkYXuZ+3atQoNDbV/aKs7bzNv3jwtWbLEqT5ffvllDR061OkaXHHhwgW1bdtWu3fvvqHbAXAJwQZAkWvZsqVWrVqlSpUqub3vmTNn6ty5c9dd7+eff9b8+fM1bNgwt9dwOT8/Pw0ZMkQjR47U+fPnb+i2ABBsAHhAcHCwoqKiPPrZNlOnTlW7du1UuXLlG76tNm3ayGKx6PXXX7/h2wJudgQbAAVy8eJFvfTSS2ratKmioqL01FNP6fDhww7r7Ny5U127dlVkZKRiY2M1YsQInThxwr48v9NK69atU7t27RQeHq5OnTpp27ZtCgsL09q1ax36/uabb/T4448rPDxcLVu2dDjtlPuBiXPmzLnmhyf++OOP2rJlizp27OjQ/uuvv2rAgAGKjY1Vw4YN1bt3b/3000+SpCNHjig0NFQbN25UQkKCoqKi1KRJEyUnJyszM1PPP/+87rrrLjVp0kRTp07Nc51Px44d9fLLL+vChQvODDMAFxFsABTI+++/r59++kmTJ0/WmDFjtGfPHg0aNMi+/KuvvlKPHj1UsmRJzZw5U88//7x27NihJ598UllZWfn2uX79eo0cOVINGjRQcnKy2rZtq4SEBOXk5ORZd9y4cerQoYMWLFigiIgIvfTSS/rkk08kSatWrZIkPfzww/af8/Puu++qYsWKatCggb0tPT1djzzyiH755ReNHTtWiYmJOn36tHr06OEQykaNGqU6depo3rx5atSokZKSkvTwww+rZMmSSkpKUnx8vBYvXqwPP/zQYZv333+/0tLStGPHDidGGYCrinm6AAC+pXLlykpOTlbx4sUlSYcPH9b8+fOVmZmpgIAATZs2TTVq1NCCBQtksVgkSZGRkWrfvr3WrFmjLl265OkzKSlJrVq10oQJEyRJzZs3V/HixTVt2rQ86w4ePFhPPPGEJCkqKkqbN2/Wl19+qVatWikqKkqSFBISYv85P19++aXCw8NlMpnsbUuXLlVWVpaWLl2qihUrSpLq1aunxx57TLt371adOnXstQ0cOFCSVKtWLW3YsEHly5fXmDFjJElNmzbVBx98oK+//lr333+/vf/q1aurTJky2rZtm5o1a3bdcQbgGo7YACiQiIgIe6iRpNtuu02SlJGRoXPnzumbb75RixYtZLPZlJ2drezsbN12222644479Pnnn+fp7/Dhwzp69Kjuu+8+h/b27dvnu/2YmBj7z6VKlVKFChWUkZFRoH347bffVLVqVYe2lJQURUVF2UONJFWqVEmffPKJ4uPj7W3R0dH2n3PXjYyMtLeZTCaVKVNGZ86cybPdW265pUCvBANQcByxAVAgpUqVcvjdbL70/5HValVGRoasVqsWLVqkRYsW5bltiRIl8rTlnuYpX768Q/vlAeNy/v7+ebZf0PetyczMzNPPqVOn8oSd/AQEBFy3pqvx9/dXZmamc0UCcAnBBoDblC5dWiaTST169Mj3iEt+ASAkJESSdPz4cYf2K393p7Jly+Y5ohIYGOhwLU2ubdu2qWrVqg6nrVyVkZGhW265pdD9ALg6TkUBcJuAgACFhYXpl19+UXh4uP2rdu3amjNnjrZv357nNiEhIapWrZo++ugjh/aNGze6VEPuEaRrufXWW/X77787tMXExGj37t0OgerEiRPq3bu3Nm3a5FItl7PZbEpLS9Ott95a6L4AXB3BBoBbDR48WP/73/80ZMgQbd26VZs3b1avXr30xRdfqH79+nnWN5lMGjBggD7++GONHTtW//vf/7R48WIlJSVJci6oXC4oKEi7du3SV199ddVTVE2bNtWuXbsclvfo0UMlSpTQ008/rQ8//FCffPKJEhISVKlSJT3wwAMFqiE/P/zwg86cOaPmzZsXui8AV0ewAeBWzZo105IlS3Ts2DENGDBAw4cPl8Vi0dKlS6/6SqWOHTvqhRde0LZt29S3b19t2LBBo0aNkpT3mp7r6du3r/bs2aPevXvnOSqTq02bNjp58qT27Nljb6tSpYpWrlypkJAQPffccxo5cqQqVqyoV155RWXLli1QDfn59NNP87zEHID7mWx8WhwAD3vvvfcUFhammjVr2tu2bNmiZ555Rm+//bbq1q3r9m327dtXwcHBmjhxotv7vpLNZlObNm3UpUsX9ejR44ZvD7iZccQGgMe988476t27t959913t3LlTb731lsaMGaPY2NgbEmokadCgQdq4caOOHj16Q/q/3AcffCCr1arHH3/8hm8LuNlxxAaAx508eVLTpk3Tp59+qhMnTqhChQpq27atBgwYoNKlS9+w7S5cuFD79+/X9OnTb9g2Lly4oPbt22vSpEkO78ED4MYg2AAAAMPgVBQAADAMgg0AADAMgg0AADAMgg0AADAMgg0AADAMgg0AADAMgg0AADAMgg0AADCM/weWixQvbRRjUgAAAABJRU5ErkJggg==\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.hist(heights)\n", "plt.title('Height Distribution of US Presidents')\n", "plt.xlabel('height (cm)')\n", "plt.ylabel('number');" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "These aggregates are some of the fundamental pieces of exploratory data analysis that we'll explore in more depth in later chapters of the book." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "< [Computation on NumPy Arrays: Universal Functions](02.03-Computation-on-arrays-ufuncs.ipynb) | [Contents](Index.ipynb) | [Computation on Arrays: Broadcasting](02.05-Computation-on-arrays-broadcasting.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 }