Skip to content
Snippets Groups Projects
Gaussian Processes.ipynb 363 KiB
Newer Older
{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "6a764d92",
   "metadata": {},
   "source": [
    "# Gaussian Processes\n",
    "\n",
    "If one is ready to assume the data available is such that the variable being fit is Gaussian for any pair of samples taken, one can use fit the data and obtain a reliable uncertainty with a very small set of assumptions.\n",
    "\n",
    "For this example, we will use the dataset collected in Mauna Loa showing the amount of CO2 present in the atmosphere. This data may be downloaded from https://gml.noaa.gov/ccgg/trends/data.html\n",
    "\n",
    "If you have Linux or MacOS with standard tools installed, just run the next line of the notebook to download it with wget. If you are use Windows, download the dataset at: https://gml.noaa.gov/webdata/ccgg/trends/co2/co2_weekly_mlo.txt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "8337a15a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--2021-12-17 16:04:48--  https://gml.noaa.gov/webdata/ccgg/trends/co2/co2_weekly_mlo.txt\n",
      "Resolving gml.noaa.gov (gml.noaa.gov)... 140.172.200.41, 2610:20:8800:6101::29\n",
      "Connecting to gml.noaa.gov (gml.noaa.gov)|140.172.200.41|:443... connected.\n",
      "HTTP request sent, awaiting response... 200 OK\n",
      "Length: 191093 (187K) [text/plain]\n",
      "Saving to: ‘co2_weekly_mlo.txt.4’\n",
      "co2_weekly_mlo.txt. 100%[===================>] 186,61K  67,4KB/s    in 2,8s    \n",
      "2021-12-17 16:04:52 (67,4 KB/s) - ‘co2_weekly_mlo.txt.4’ saved [191093/191093]\n",
      "\n"
     ]
    }
   ],
   "source": [
    "!wget --no-check-certificate https://gml.noaa.gov/webdata/ccgg/trends/co2/co2_weekly_mlo.txt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c8131588",
   "metadata": {},
   "source": [
    "We can take a quick look at the file to see what is inside:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "07ecc0d3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "# --------------------------------------------------------------------\n",
      "# USE OF NOAA GML DATA\n",
      "# \n",
      "# These data are made freely available to the public and the\n",
      "# scientific community in the belief that their wide dissemination\n",
      "# will lead to greater understanding and new scientific insights.\n",
      "# The availability of these data does not constitute publication\n",
      "# of the data.  NOAA relies on the ethics and integrity of the user to\n",
      "# ensure that GML receives fair credit for their work.  If the data \n",
      "# are obtained for potential use in a publication or presentation, \n",
      "# GML should be informed at the outset of the nature of this work.  \n",
      "# If the GML data are essential to the work, or if an important \n",
      "# result or conclusion depends on the GML data, co-authorship\n",
      "# may be appropriate.  This should be discussed at an early stage in\n",
      "# the work.  Manuscripts using the GML data should be sent to GML\n",
      "# for review before they are submitted for publication so we can\n",
      "# ensure that the quality and limitations of the data are accurately\n",
      "# represented.\n",
      "# \n",
      "# Contact:   Pieter Tans (303 497 6678; pieter.tans@noaa.gov)\n",
      "# \n",
      "# File Creation:  Fri Dec 17 05:00:13 2021\n",
      "# \n",
      "# RECIPROCITY\n",
      "# \n",
      "# Use of these data implies an agreement to reciprocate.\n",
      "# Laboratories making similar measurements agree to make their\n",
      "# own data available to the general public and to the scientific\n",
      "# community in an equally complete and easily accessible form.\n",
      "# Modelers are encouraged to make available to the community,\n",
      "# upon request, their own tools used in the interpretation\n",
      "# of the GML data, namely well documented model code, transport\n",
      "# fields, and additional information necessary for other\n",
      "# scientists to repeat the work and to run modified versions.\n",
      "# Model availability includes collaborative support for new\n",
      "# users of the models.\n",
      "# --------------------------------------------------------------------\n",
      "#  \n",
      "#  \n",
      "# See www.esrl.noaa.gov/gmd/ccgg/trends/ for additional details.\n",
      "#  \n",
      "# NOTE: DATA FOR THE LAST SEVERAL MONTHS ARE PRELIMINARY, ARE STILL SUBJECT\n",
      "# TO QUALITY CONTROL PROCEDURES.\n",
      "# NOTE: The week \"1 yr ago\" is exactly 365 days ago, and thus does not run from\n",
      "# Sunday through Saturday. 365 also ignores the possibility of a leap year.\n",
      "# The week \"10 yr ago\" is exactly 10*365 days +3 days (for leap years) ago.\n",
      "#  \n",
      "#      Start of week      CO2 molfrac           (-999.99 = no data)  increase\n",
      "# (yr, mon, day, decimal)    (ppm)  #days       1 yr ago  10 yr ago  since 1800\n",
      "  1974   5  19  1974.3795    333.37  5          -999.99   -999.99     50.40\n",
      "  1974   5  26  1974.3986    332.95  6          -999.99   -999.99     50.06\n",
      "  1974   6   2  1974.4178    332.35  5          -999.99   -999.99     49.60\n",
      "  1974   6   9  1974.4370    332.20  7          -999.99   -999.99     49.65\n",
      "  1974   6  16  1974.4562    332.37  7          -999.99   -999.99     50.06\n",
      "  1974   6  23  1974.4753    331.73  5          -999.99   -999.99     49.72\n",
      "  1974   6  30  1974.4945    331.68  6          -999.99   -999.99     50.02\n",
      "  1974   7   7  1974.5137    331.46  6          -999.99   -999.99     50.20\n",
      "  1974   7  14  1974.5329    330.83  5          -999.99   -999.99     50.01\n",
      "  1974   7  21  1974.5521    330.76  7          -999.99   -999.99     50.41\n",
      "  1974   7  28  1974.5712    329.80  4          -999.99   -999.99     49.97\n",
      "  1974   8   4  1974.5904    329.85  5          -999.99   -999.99     50.54\n"
     ]
    }
   ],
   "source": [
    "with open(\"co2_weekly_mlo.txt\", \"r\") as f:\n",
    "    for i, line in enumerate(f.readlines()):\n",
    "        if i > 60: # let's print only the first 60 lines only\n",
    "            break\n",
    "        print(line[:-1])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fce4d8e8",
   "metadata": {},
   "source": [
    "We start by loading the necessary Python modules. If you have not yet installed them, run the following cell to install them with pip:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "44ca341e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Requirement already satisfied: numpy in /home/daniloefl/miniconda3/envs/mltut/lib/python3.6/site-packages (1.19.2)\n",
      "Requirement already satisfied: scikit-learn in /home/daniloefl/miniconda3/envs/mltut/lib/python3.6/site-packages (0.24.2)\n",
      "Requirement already satisfied: pandas in /home/daniloefl/miniconda3/envs/mltut/lib/python3.6/site-packages (1.1.5)\n",
      "Requirement already satisfied: matplotlib in /home/daniloefl/miniconda3/envs/mltut/lib/python3.6/site-packages (3.3.4)\n",
      "Requirement already satisfied: threadpoolctl>=2.0.0 in /home/daniloefl/miniconda3/envs/mltut/lib/python3.6/site-packages (from scikit-learn) (2.2.0)\n",
      "Requirement already satisfied: joblib>=0.11 in /home/daniloefl/miniconda3/envs/mltut/lib/python3.6/site-packages (from scikit-learn) (1.0.1)\n",
      "Requirement already satisfied: scipy>=0.19.1 in /home/daniloefl/miniconda3/envs/mltut/lib/python3.6/site-packages (from scikit-learn) (1.5.2)\n",
      "Requirement already satisfied: python-dateutil>=2.7.3 in /home/daniloefl/miniconda3/envs/mltut/lib/python3.6/site-packages (from pandas) (2.8.2)\n",
      "Requirement already satisfied: pytz>=2017.2 in /home/daniloefl/miniconda3/envs/mltut/lib/python3.6/site-packages (from pandas) (2021.3)\n",
      "Requirement already satisfied: pillow>=6.2.0 in /home/daniloefl/miniconda3/envs/mltut/lib/python3.6/site-packages (from matplotlib) (8.3.1)\n",
      "Requirement already satisfied: cycler>=0.10 in /home/daniloefl/miniconda3/envs/mltut/lib/python3.6/site-packages (from matplotlib) (0.11.0)\n",
      "Requirement already satisfied: kiwisolver>=1.0.1 in /home/daniloefl/miniconda3/envs/mltut/lib/python3.6/site-packages (from matplotlib) (1.3.1)\n",
      "Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.3 in /home/daniloefl/miniconda3/envs/mltut/lib/python3.6/site-packages (from matplotlib) (3.0.4)\n",
      "Requirement already satisfied: six>=1.5 in /home/daniloefl/miniconda3/envs/mltut/lib/python3.6/site-packages (from python-dateutil>=2.7.3->pandas) (1.16.0)\n"
     ]
    }
   ],
   "source": [
    "!pip install numpy scikit-learn pandas matplotlib"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "300cf8d3",
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "from sklearn.gaussian_process import GaussianProcessRegressor\n",
    "from sklearn.gaussian_process.kernels import RBF, ExpSineSquared, WhiteKernel, RationalQuadratic"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0ecd6a69",
   "metadata": {},
   "source": [
    "Let us now load the data downloaded using pandas. If you are not familiar with Pandas, take a look at the documentation for a quick introduction at https://pandas.pydata.org/docs/user_guide/10min.html\n",
    "\n",
    "We can easily parse the data file using Pandas' read_csv function. It would require some coding to do it manually. As Pandas does not detect the column names, we have to specify it manually in the names parameter below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "4959a292",
   "metadata": {},
   "outputs": [],
   "source": [
    "data = pd.read_csv(\"co2_weekly_mlo.txt\",\n",
    "                   delim_whitespace=True,\n",
    "                   comment='#',\n",
    "                   header=None,\n",
    "                   names=[\"year\", \"month\", \"day\", \"decimal\",\n",
    "                          \"co2\",\n",
    "                          \"ndays\",\n",
    "                          \"last_year\", \"last_decade\",\n",
    "                          \"increase\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d8295e8a",
   "metadata": {},
   "source": [
    "Let's print out the dataset read first."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "024fb65a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>year</th>\n",
       "      <th>month</th>\n",
       "      <th>day</th>\n",
       "      <th>decimal</th>\n",
       "      <th>co2</th>\n",
       "      <th>ndays</th>\n",
       "      <th>last_year</th>\n",
       "      <th>last_decade</th>\n",
       "      <th>increase</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1974</td>\n",
       "      <td>5</td>\n",
       "      <td>19</td>\n",
       "      <td>1974.3795</td>\n",
       "      <td>333.37</td>\n",
       "      <td>5</td>\n",
       "      <td>-999.99</td>\n",
       "      <td>-999.99</td>\n",
       "      <td>50.40</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>1974</td>\n",
       "      <td>5</td>\n",
       "      <td>26</td>\n",
       "      <td>1974.3986</td>\n",
       "      <td>332.95</td>\n",
       "      <td>6</td>\n",
       "      <td>-999.99</td>\n",
       "      <td>-999.99</td>\n",
       "      <td>50.06</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>1974</td>\n",
       "      <td>6</td>\n",
       "      <td>2</td>\n",
       "      <td>1974.4178</td>\n",
       "      <td>332.35</td>\n",
       "      <td>5</td>\n",
       "      <td>-999.99</td>\n",
       "      <td>-999.99</td>\n",
       "      <td>49.60</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>1974</td>\n",
       "      <td>6</td>\n",
       "      <td>9</td>\n",
       "      <td>1974.4370</td>\n",
       "      <td>332.20</td>\n",
       "      <td>7</td>\n",
       "      <td>-999.99</td>\n",
       "      <td>-999.99</td>\n",
       "      <td>49.65</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>1974</td>\n",
       "      <td>6</td>\n",
       "      <td>16</td>\n",
       "      <td>1974.4562</td>\n",
       "      <td>332.37</td>\n",
       "      <td>7</td>\n",
       "      <td>-999.99</td>\n",
       "      <td>-999.99</td>\n",
       "      <td>50.06</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2477</th>\n",
       "      <td>2021</td>\n",
       "      <td>11</td>\n",
       "      <td>7</td>\n",
       "      <td>2021.8507</td>\n",
       "      <td>414.97</td>\n",
       "      <td>7</td>\n",
       "      <td>412.97</td>\n",
       "      <td>390.09</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2478</th>\n",
       "      <td>2021</td>\n",
       "      <td>11</td>\n",
       "      <td>14</td>\n",
       "      <td>2021.8699</td>\n",
       "      <td>414.88</td>\n",
       "      <td>7</td>\n",
       "      <td>412.80</td>\n",
       "      <td>390.71</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2479</th>\n",
       "      <td>2021</td>\n",
       "      <td>11</td>\n",
       "      <td>21</td>\n",
       "      <td>2021.8890</td>\n",
       "      <td>415.36</td>\n",
       "      <td>7</td>\n",
       "      <td>413.55</td>\n",
       "      <td>390.78</td>\n",
       "      <td>136.99</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2480</th>\n",
       "      <td>2021</td>\n",
       "      <td>11</td>\n",
       "      <td>28</td>\n",
       "      <td>2021.9082</td>\n",
       "      <td>416.16</td>\n",
       "      <td>7</td>\n",
       "      <td>414.36</td>\n",
       "      <td>391.33</td>\n",
       "      <td>137.45</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2481</th>\n",
       "      <td>2021</td>\n",
       "      <td>12</td>\n",
       "      <td>5</td>\n",
       "      <td>2021.9274</td>\n",
       "      <td>415.86</td>\n",
       "      <td>5</td>\n",
       "      <td>413.50</td>\n",
       "      <td>391.72</td>\n",
       "      <td>136.84</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "      year  month  day    decimal     co2  ndays  last_year  last_decade  \\\n",
       "0     1974      5   19  1974.3795  333.37      5    -999.99      -999.99   \n",
       "1     1974      5   26  1974.3986  332.95      6    -999.99      -999.99   \n",
       "2     1974      6    2  1974.4178  332.35      5    -999.99      -999.99   \n",
       "3     1974      6    9  1974.4370  332.20      7    -999.99      -999.99   \n",
       "4     1974      6   16  1974.4562  332.37      7    -999.99      -999.99   \n",
       "...    ...    ...  ...        ...     ...    ...        ...          ...   \n",
       "2477  2021     11    7  2021.8507  414.97      7     412.97       390.09   \n",
       "2478  2021     11   14  2021.8699  414.88      7     412.80       390.71   \n",
       "2479  2021     11   21  2021.8890  415.36      7     413.55       390.78   \n",
       "2480  2021     11   28  2021.9082  416.16      7     414.36       391.33   \n",
       "2481  2021     12    5  2021.9274  415.86      5     413.50       391.72   \n",
       "\n",
       "      increase  \n",
       "0        50.40  \n",
       "1        50.06  \n",
       "2        49.60  \n",
       "3        49.65  \n",
       "4        50.06  \n",
       "...        ...  \n",
       "2477    137.30  \n",
       "2478    136.86  \n",
       "2479    136.99  \n",
       "2480    137.45  \n",
       "2481    136.84  \n",
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1c178424",
   "metadata": {},
   "source": [
    "We can plot this fairly easily using Matplotlib."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "e63b38c5",
   "metadata": {},
   "outputs": [
    {
     "data": {
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "/* global mpl */\n",
       "window.mpl = {};\n",
       "\n",
       "mpl.get_websocket_type = function () {\n",
       "    if (typeof WebSocket !== 'undefined') {\n",
       "        return WebSocket;\n",
       "    } else if (typeof MozWebSocket !== 'undefined') {\n",
       "        return MozWebSocket;\n",
       "    } else {\n",
       "        alert(\n",
       "            'Your browser does not have WebSocket support. ' +\n",
       "                'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
       "                'Firefox 4 and 5 are also supported but you ' +\n",
       "                'have to enable WebSockets in about:config.'\n",
       "        );\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n",
       "    this.id = figure_id;\n",
       "\n",
       "    this.ws = websocket;\n",
       "\n",
       "    this.supports_binary = this.ws.binaryType !== undefined;\n",
       "\n",
       "    if (!this.supports_binary) {\n",
       "        var warnings = document.getElementById('mpl-warnings');\n",
       "        if (warnings) {\n",
       "            warnings.style.display = 'block';\n",
       "            warnings.textContent =\n",
       "                'This browser does not support binary websocket messages. ' +\n",
       "                'Performance may be slow.';\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.imageObj = new Image();\n",
       "\n",
       "    this.context = undefined;\n",
       "    this.message = undefined;\n",
       "    this.canvas = undefined;\n",
       "    this.rubberband_canvas = undefined;\n",
       "    this.rubberband_context = undefined;\n",
       "    this.format_dropdown = undefined;\n",
       "\n",
       "    this.image_mode = 'full';\n",
       "\n",
       "    this.root = document.createElement('div');\n",
       "    this.root.setAttribute('style', 'display: inline-block');\n",
       "    this._root_extra_style(this.root);\n",
       "\n",
       "    parent_element.appendChild(this.root);\n",
       "\n",
       "    this._init_header(this);\n",
       "    this._init_canvas(this);\n",
       "    this._init_toolbar(this);\n",
       "\n",
       "    var fig = this;\n",
       "\n",
       "    this.waiting = false;\n",
       "\n",
       "    this.ws.onopen = function () {\n",
       "        fig.send_message('supports_binary', { value: fig.supports_binary });\n",
       "        fig.send_message('send_image_mode', {});\n",
       "        if (fig.ratio !== 1) {\n",
       "            fig.send_message('set_dpi_ratio', { dpi_ratio: fig.ratio });\n",
       "        }\n",
       "        fig.send_message('refresh', {});\n",
       "    };\n",
       "\n",
       "    this.imageObj.onload = function () {\n",
       "        if (fig.image_mode === 'full') {\n",
       "            // Full images could contain transparency (where diff images\n",
       "            // almost always do), so we need to clear the canvas so that\n",
       "            // there is no ghosting.\n",
       "            fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
       "        }\n",
       "        fig.context.drawImage(fig.imageObj, 0, 0);\n",
       "    };\n",
       "\n",
       "    this.imageObj.onunload = function () {\n",
       "        fig.ws.close();\n",
       "    };\n",
       "\n",
       "    this.ws.onmessage = this._make_on_message_function(this);\n",
       "\n",
       "    this.ondownload = ondownload;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._init_header = function () {\n",
       "    var titlebar = document.createElement('div');\n",
       "    titlebar.classList =\n",
       "        'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n",
       "    var titletext = document.createElement('div');\n",
       "    titletext.classList = 'ui-dialog-title';\n",
       "    titletext.setAttribute(\n",
       "        'style',\n",
       "        'width: 100%; text-align: center; padding: 3px;'\n",
       "    );\n",
       "    titlebar.appendChild(titletext);\n",
       "    this.root.appendChild(titlebar);\n",
       "    this.header = titletext;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function () {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = (this.canvas_div = document.createElement('div'));\n",
       "    canvas_div.setAttribute(\n",
       "        'style',\n",
       "        'border: 1px solid #ddd;' +\n",
       "            'box-sizing: content-box;' +\n",
       "            'clear: both;' +\n",
       "            'min-height: 1px;' +\n",
       "            'min-width: 1px;' +\n",
       "            'outline: 0;' +\n",
       "            'overflow: hidden;' +\n",
       "            'position: relative;' +\n",
       "            'resize: both;'\n",
       "    );\n",
       "\n",
       "    function on_keyboard_event_closure(name) {\n",
       "        return function (event) {\n",
       "            return fig.key_event(event, name);\n",
       "        };\n",
       "    }\n",
       "\n",
       "    canvas_div.addEventListener(\n",
       "        'keydown',\n",
       "        on_keyboard_event_closure('key_press')\n",
       "    );\n",
       "    canvas_div.addEventListener(\n",
       "        'keyup',\n",
       "        on_keyboard_event_closure('key_release')\n",
       "    );\n",
       "\n",
       "    this._canvas_extra_style(canvas_div);\n",
       "    this.root.appendChild(canvas_div);\n",
       "\n",
       "    var canvas = (this.canvas = document.createElement('canvas'));\n",
       "    canvas.classList.add('mpl-canvas');\n",
       "    canvas.setAttribute('style', 'box-sizing: content-box;');\n",
       "\n",
       "    this.context = canvas.getContext('2d');\n",
       "\n",
       "    var backingStore =\n",
       "        this.context.backingStorePixelRatio ||\n",
       "        this.context.webkitBackingStorePixelRatio ||\n",
       "        this.context.mozBackingStorePixelRatio ||\n",
       "        this.context.msBackingStorePixelRatio ||\n",
       "        this.context.oBackingStorePixelRatio ||\n",
       "        this.context.backingStorePixelRatio ||\n",
       "        1;\n",
       "\n",
       "    this.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "\n",
       "    var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n",
       "        'canvas'\n",
       "    ));\n",
       "    rubberband_canvas.setAttribute(\n",
       "        'style',\n",
       "        'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n",
       "    );\n",
       "\n",
       "    // Apply a ponyfill if ResizeObserver is not implemented by browser.\n",
       "    if (this.ResizeObserver === undefined) {\n",
       "        if (window.ResizeObserver !== undefined) {\n",
       "            this.ResizeObserver = window.ResizeObserver;\n",
       "        } else {\n",
       "            var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n",
       "            this.ResizeObserver = obs.ResizeObserver;\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n",
       "        var nentries = entries.length;\n",
       "        for (var i = 0; i < nentries; i++) {\n",
       "            var entry = entries[i];\n",
       "            var width, height;\n",
       "            if (entry.contentBoxSize) {\n",
       "                if (entry.contentBoxSize instanceof Array) {\n",
       "                    // Chrome 84 implements new version of spec.\n",
       "                    width = entry.contentBoxSize[0].inlineSize;\n",
       "                    height = entry.contentBoxSize[0].blockSize;\n",
       "                } else {\n",
       "                    // Firefox implements old version of spec.\n",
       "                    width = entry.contentBoxSize.inlineSize;\n",
       "                    height = entry.contentBoxSize.blockSize;\n",
       "                }\n",
       "            } else {\n",
       "                // Chrome <84 implements even older version of spec.\n",
       "                width = entry.contentRect.width;\n",
       "                height = entry.contentRect.height;\n",
       "            }\n",
       "\n",
       "            // Keep the size of the canvas and rubber band canvas in sync with\n",
       "            // the canvas container.\n",
       "            if (entry.devicePixelContentBoxSize) {\n",
       "                // Chrome 84 implements new version of spec.\n",
       "                canvas.setAttribute(\n",
       "                    'width',\n",
       "                    entry.devicePixelContentBoxSize[0].inlineSize\n",
       "                );\n",
       "                canvas.setAttribute(\n",
       "                    'height',\n",
       "                    entry.devicePixelContentBoxSize[0].blockSize\n",
       "                );\n",
       "            } else {\n",
       "                canvas.setAttribute('width', width * fig.ratio);\n",
       "                canvas.setAttribute('height', height * fig.ratio);\n",
       "            }\n",
       "            canvas.setAttribute(\n",
       "                'style',\n",
       "                'width: ' + width + 'px; height: ' + height + 'px;'\n",
       "            );\n",
       "\n",
       "            rubberband_canvas.setAttribute('width', width);\n",
       "            rubberband_canvas.setAttribute('height', height);\n",
       "\n",
       "            // And update the size in Python. We ignore the initial 0/0 size\n",
       "            // that occurs as the element is placed into the DOM, which should\n",
       "            // otherwise not happen due to the minimum size styling.\n",
       "            if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n",
       "                fig.request_resize(width, height);\n",
       "            }\n",
       "        }\n",
       "    });\n",
       "    this.resizeObserverInstance.observe(canvas_div);\n",
       "\n",
       "    function on_mouse_event_closure(name) {\n",
       "        return function (event) {\n",
       "            return fig.mouse_event(event, name);\n",
       "        };\n",
       "    }\n",
       "\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mousedown',\n",
       "        on_mouse_event_closure('button_press')\n",
       "    );\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mouseup',\n",
       "        on_mouse_event_closure('button_release')\n",
       "    );\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mousemove',\n",
       "        on_mouse_event_closure('motion_notify')\n",
       "    );\n",
       "\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mouseenter',\n",
       "        on_mouse_event_closure('figure_enter')\n",
       "    );\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mouseleave',\n",
       "        on_mouse_event_closure('figure_leave')\n",
       "    );\n",
       "\n",
       "    canvas_div.addEventListener('wheel', function (event) {\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        on_mouse_event_closure('scroll')(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.appendChild(canvas);\n",
       "    canvas_div.appendChild(rubberband_canvas);\n",
       "\n",
       "    this.rubberband_context = rubberband_canvas.getContext('2d');\n",
       "    this.rubberband_context.strokeStyle = '#000000';\n",
       "\n",
       "    this._resize_canvas = function (width, height, forward) {\n",
       "        if (forward) {\n",
       "            canvas_div.style.width = width + 'px';\n",
       "            canvas_div.style.height = height + 'px';\n",
       "        }\n",
       "    };\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n",
       "        event.preventDefault();\n",
       "        return false;\n",
       "    });\n",
       "\n",
       "    function set_focus() {\n",
       "        canvas.focus();\n",
       "        canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    window.setTimeout(set_focus, 100);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function () {\n",
       "    var fig = this;\n",
       "\n",
       "    var toolbar = document.createElement('div');\n",
       "    toolbar.classList = 'mpl-toolbar';\n",
       "    this.root.appendChild(toolbar);\n",
       "\n",
       "    function on_click_closure(name) {\n",
       "        return function (_event) {\n",
       "            return fig.toolbar_button_onclick(name);\n",
       "        };\n",
       "    }\n",
       "\n",
       "    function on_mouseover_closure(tooltip) {\n",
       "        return function (event) {\n",
       "            if (!event.currentTarget.disabled) {\n",
       "                return fig.toolbar_button_onmouseover(tooltip);\n",
       "            }\n",
       "        };\n",
       "    }\n",
       "\n",
       "    fig.buttons = {};\n",
       "    var buttonGroup = document.createElement('div');\n",
       "    buttonGroup.classList = 'mpl-button-group';\n",
       "    for (var toolbar_ind in mpl.toolbar_items) {\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) {\n",
       "            /* Instead of a spacer, we start a new button group. */\n",
       "            if (buttonGroup.hasChildNodes()) {\n",
       "                toolbar.appendChild(buttonGroup);\n",
       "            }\n",
       "            buttonGroup = document.createElement('div');\n",
       "            buttonGroup.classList = 'mpl-button-group';\n",
       "            continue;\n",
       "        }\n",
       "\n",
       "        var button = (fig.buttons[name] = document.createElement('button'));\n",
       "        button.classList = 'mpl-widget';\n",
       "        button.setAttribute('role', 'button');\n",
       "        button.setAttribute('aria-disabled', 'false');\n",
       "        button.addEventListener('click', on_click_closure(method_name));\n",
       "        button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n",
       "\n",
       "        var icon_img = document.createElement('img');\n",
       "        icon_img.src = '_images/' + image + '.png';\n",
       "        icon_img.srcset = '_images/' + image + '_large.png 2x';\n",
       "        icon_img.alt = tooltip;\n",
       "        button.appendChild(icon_img);\n",
       "\n",
       "        buttonGroup.appendChild(button);\n",
       "    }\n",
       "\n",
       "    if (buttonGroup.hasChildNodes()) {\n",
       "        toolbar.appendChild(buttonGroup);\n",
       "    }\n",
       "\n",
       "    var fmt_picker = document.createElement('select');\n",
       "    fmt_picker.classList = 'mpl-widget';\n",
       "    toolbar.appendChild(fmt_picker);\n",
       "    this.format_dropdown = fmt_picker;\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = document.createElement('option');\n",
       "        option.selected = fmt === mpl.default_extension;\n",
       "        option.innerHTML = fmt;\n",
       "        fmt_picker.appendChild(option);\n",
       "    }\n",
       "\n",
       "    var status_bar = document.createElement('span');\n",
       "    status_bar.classList = 'mpl-message';\n",
       "    toolbar.appendChild(status_bar);\n",
       "    this.message = status_bar;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', { width: x_pixels, height: y_pixels });\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.send_message = function (type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function () {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_save = function (fig, _msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function (fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1], msg['forward']);\n",
       "        fig.send_message('refresh', {});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n",
       "    var x0 = msg['x0'] / fig.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n",
       "    var x1 = msg['x1'] / fig.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0,\n",
       "        0,\n",
       "        fig.canvas.width / fig.ratio,\n",
       "        fig.canvas.height / fig.ratio\n",
       "    );\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch (cursor) {\n",
       "        case 0:\n",
       "            cursor = 'pointer';\n",
       "            break;\n",
       "        case 1:\n",
       "            cursor = 'default';\n",
       "            break;\n",
       "        case 2:\n",
       "            cursor = 'crosshair';\n",
       "            break;\n",
       "        case 3:\n",
       "            cursor = 'move';\n",
       "            break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_message = function (fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n",
       "    for (var key in msg) {\n",
       "        if (!(key in fig.buttons)) {\n",
       "            continue;\n",
       "        }\n",
       "        fig.buttons[key].disabled = !msg[key];\n",
       "        fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n",
       "    if (msg['mode'] === 'PAN') {\n",
       "        fig.buttons['Pan'].classList.add('active');\n",
       "        fig.buttons['Zoom'].classList.remove('active');\n",
       "    } else if (msg['mode'] === 'ZOOM') {\n",
       "        fig.buttons['Pan'].classList.remove('active');\n",
       "        fig.buttons['Zoom'].classList.add('active');\n",
       "    } else {\n",
       "        fig.buttons['Pan'].classList.remove('active');\n",
       "        fig.buttons['Zoom'].classList.remove('active');\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function () {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message('ack', {});\n",
       "};\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function (fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = 'image/png';\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src\n",
       "                );\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data\n",
       "            );\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        } else if (\n",
       "            typeof evt.data === 'string' &&\n",
       "            evt.data.slice(0, 21) === 'data:image/png;base64'\n",
       "        ) {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig['handle_' + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\n",