{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Explore Gulf of Mexico SSTs during the 2020 hurricane season \n", "### Search by user-defined geographic region of interest using shapefiles\n", "\n", "The evolution of sea surface temperature (SST) anomalies in the Gulf of Mexico before, during, and after Tropical Cyclones can be explored using NASA remote sensing data, such as the MODIS Aqua SST dataset. An ocean response can often be seen in the wake of hurricane tracks, with cold wakes, or areas of cooler water, along or to the right of hurricane tracks in association with wind-induced water column mixing that brings cooler waters at depth to the surface. These cold signatures are generally patchy and spatially confined. This notebook walks through the discovery, download, and plotting of remote sensing ocean SST level 2 (L2) data over a geographic region of interest defined by the user (through the use of common GIS files such as .shp) during the 2020 hurricane season. \n", "\n", "As NASA Earthdata transitions to the Earthdata Cloud, it will be common for some data to exist in a traditional on-premise storage system, accessed by direct download to a local environment, while other data will have migrated to the cloud system. This need to access and customize data from the Earthdata Cloud, and work with it locally is described in this notebook. This access workflow of cloud-based data and downloding to local environment for further analysis or use is presented in the Introduction (Part I) of the workshop, and described again here:\n", "\n", "\n", "\n", "\n", "\n", "*Note: In this example we are using a shape file to search on ocean remote sensing data, although one can do this type of search on any Earthdata (DAAC) data. For example, of particular interest could be using shape files to search on terrestrial hydrology data (e.g watersheds), globally (depending on data availability of course).* \n", "\n", "*Note: Here we show a programmatic way of completing this type of workflow, but this can also be accomplished through the Earthdata Search user interface. For a video tutorial on how to do this, please see https://www.youtube.com/watch?v=d1BR8w3u0dI&list=PLDWiCz1Ka4kSbqkoeOcPXGAv0gp8OS1Ah&index=7, and disregard the comments about 'UAT' (test environment), as this capability has since become operational. Use https://search.earthdata.nasa.gov/ and follow the rest of the steps in the video tutorial. Search by HUC (hydrologic unit code) capability is also available, if you are working with data over the United States. A video tutorial on how to search by HUC in Earthdata Search is available here: https://www.youtube.com/watch?v=8TLJOFe7XPw&list=PLDWiCz1Ka4kSbqkoeOcPXGAv0gp8OS1Ah&index=6*\n", "\n", "#### Learning Objectives:\n", "0. Earthdata Login Authentication (for download access from Earthdata data archive)\n", "1. Search CMR for collection and granule IDs, using the collection shortname and provider\n", "2. Download a file from the PO.DAAC (Earthdata) cloud archive to local computer and preview the data\n", "3. Search a collection by user-provided shapefile (ESRI shp) and temporal range\n", "4. Donwload the first file (from the PO.DAAC cloud archive to local computer) and preview subset\n", "4. Download all data (from the PO.DAAC cloud archive to local computer) based on shp and time search criteria \n", "\n", "----- " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## First, import needed packages and libraries" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2020-12-04T06:29:48.865985Z", "start_time": "2020-12-04T06:29:47.947204Z" } }, "outputs": [], "source": [ "%matplotlib inline\n", "\n", "from os.path import join, expanduser, basename\n", "from urllib import request, parse\n", "import requests\n", "import shutil\n", "import json\n", "from http.cookiejar import CookieJar\n", "from getpass import getpass\n", "from netrc import netrc\n", "import pprint\n", "from platform import system\n", "\n", "import xarray as xr\n", "import matplotlib.pyplot as plt\n", "import cartopy.crs as ccrs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Earthdata Login Authentication\n", "\n", "An Earthdata Login account is required to access data, as well as discover restricted data, from the NASA Earthdata system. Please visit https://urs.earthdata.nasa.gov to register and manage your Earthdata Login account. This account is free to create and only takes a moment to set up.\n", "\n", "At this point in time (as we are still transitioning to a cloud environment), in order to access data from the Earthdata Cloud, you need special, early access, persmissions. For the workshop today, you have already been added to the list and the Earthdata login you provided prior to the workshop has been granted this access." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `setup_earthdata_login_auth` function will allow Python scripts to log into any Earthdata Login application programmatically. To avoid being prompted for\n", "credentials every time you run and also allow clients such as curl to log in, you can add the following\n", "to a `.netrc` (`_netrc` on Windows) file in your home directory:\n", "\n", "```\n", "machine urs.earthdata.nasa.gov\n", " login \n", " password \n", "```\n", "\n", "Make sure that this file is only readable by the current user or you will receive an error stating\n", "\"netrc access too permissive.\"\n", "\n", "`$ chmod 0600 ~/.netrc` " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2020-12-04T06:33:37.797905Z", "start_time": "2020-12-04T06:33:37.789707Z" } }, "outputs": [], "source": [ "from netrc import netrc\n", "from platform import system\n", "from getpass import getpass\n", "from http.cookiejar import CookieJar\n", "from os.path import join, expanduser\n", "\n", "TOKEN_DATA = (\"\"\n", " \"%s\"\n", " \"%s\"\n", " \"PODAAC CMR Client\"\n", " \"%s\"\n", " \"\")\n", "\n", "\n", "def setup_cmr_token_auth(endpoint: str='cmr.earthdata.nasa.gov'):\n", " ip = requests.get(\"https://ipinfo.io/ip\").text.strip()\n", " return requests.post(\n", " url=\"https://%s/legacy-services/rest/tokens\" % endpoint,\n", " data=TOKEN_DATA % (input(\"Username: \"), getpass(\"Password: \"), ip),\n", " headers={'Content-Type': 'application/xml', 'Accept': 'application/json'}\n", " ).json()['token']['id']\n", "\n", "\n", "def setup_earthdata_login_auth(endpoint: str='urs.earthdata.nasa.gov'):\n", " netrc_name = \"_netrc\" if system()==\"Windows\" else \".netrc\"\n", " try:\n", " username, _, password = netrc(file=join(expanduser('~'), netrc_name)).authenticators(endpoint)\n", " except (FileNotFoundError, TypeError):\n", " print('Please provide your Earthdata Login credentials for access.')\n", " print('Your info will only be passed to %s and will not be exposed in Jupyter.' % (endpoint))\n", " username = input('Username: ')\n", " password = getpass('Password: ')\n", " manager = request.HTTPPasswordMgrWithDefaultRealm()\n", " manager.add_password(None, endpoint, username, password)\n", " auth = request.HTTPBasicAuthHandler(manager)\n", " jar = CookieJar()\n", " processor = request.HTTPCookieProcessor(jar)\n", " opener = request.build_opener(auth, processor)\n", " request.install_opener(opener)\n", "\n", "\n", "# Get your authentication token for searching restricted records in the CMR:\n", "_token = setup_cmr_token_auth(endpoint=\"cmr.earthdata.nasa.gov\")\n", "\n", "# Start authenticated session with URS to allow restricted data downloads:\n", "setup_earthdata_login_auth(endpoint=\"urs.earthdata.nasa.gov\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "-----------\n", "### Explore data availability using the Common Metadata Repository\n", "The Common Metadata Repository (CMR) is a robust metadata system that catalogs Earth Science data and associated service metadata records. CMR supports data search and discovery through an Application Programming Interface, or API, enabling reproducible data product and data file searches using a number of helpful variables, including geographic area, keyword, provider, and time.\n", "\n", "General CMR API documentation: https://cmr.earthdata.nasa.gov/search/site/docs/search/api.html" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. Identify a data collection of interest\n", "\n", "Data sets are selected by data set IDs (e.g. `MODIS_T-JPL-L2P-v2019.0`). In the CMR API documentation, a data set id is referred to as a \"short name\". These short names are located at the top of each NSIDC data set landing page in gray above the full title: \n", "\n", "- go to https://search.earthdata.nasa.gov/search and type *POCLOUD* in the search box (which is the PO.DAAC data provider for cloud-based Pathfinder datasets in a restricted operational cloud environment). Click on the (i) next to the data collection of interest; the `shortname` is the subheader name below the main full data collection name.\n", "\n", "\n", " \n", "ShortName:\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this tutorial we will be using the **MODIS-Terra SST L2 collection**, with the shortname: `MODIS_T-JPL-L2P-v2019.0`. Data from our collection of interest can be obtained from the PO.DAAC (Earthdata) cloud archive.\n", "\n", "### Search by dataset shortname and provider\n", "\n", "To search Earthdata from this notebook, we can use the following code with key words that describe our dataset (also knows as a **collection**). This snippet of Python code uses the `requests` module to get collection metadata from the CMR, with our dataset of interset *shortname* and *provider* as search criteria (parameters). In subsequet notebooks (demos), this functionality is hidden in a package called `tutorial_helper_functions` for ease of use. For now, this code snippet will help us understand how the request is made:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2020-12-04T06:29:54.594592Z", "start_time": "2020-12-04T06:29:54.153543Z" } }, "outputs": [], "source": [ "modis_coll = requests.get(\n", " url=\"https://cmr.earthdata.nasa.gov/search/collections.umm_json\", # CMR API url\n", " params={'ShortName': \"MODIS_T-JPL-L2P-v2019.0\", # dataset collection shortname\n", " 'provider': \"POCLOUD\", # data provider\n", " 'token': _token},\n", ").json()\n", "\n", "modis_coll['items'][0]['meta'] # print collection metadata" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Based on the metadata retrieved above, we now know the collection ID (*concept-id*) is `C1940475563-POCLOUD`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To **retrieve the *granule (file)* metadata** from the CMR:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2020-12-04T06:29:55.049622Z", "start_time": "2020-12-04T06:29:54.597071Z" } }, "outputs": [], "source": [ "modis_gran = requests.get(\n", " url=\"https://cmr.earthdata.nasa.gov/search/granules.umm_json\", \n", " params={'ShortName': \"MODIS_T-JPL-L2P-v2019.0\", \n", " 'provider': \"POCLOUD\",\n", " 'token': _token, },\n", ").json()\n", "\n", "modis_gran['items'][0]['meta']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this case, the metadata tells us that the the granule ID is `G1967602341-POCLOUD`, among other information." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Download a granule & preview data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Access & preview the data - get a quick feel for what the data looks like\n", "Here is how to list the URLs needed to access the data:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2020-12-04T06:29:55.055258Z", "start_time": "2020-12-04T06:29:55.051358Z" } }, "outputs": [], "source": [ "modis_gran['items'][0]['umm']['RelatedUrls']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To access POCLOUD Earthdata Cloud data, you want URLs with 'Type': `'GET DATA'` and host `https://archive.podaac.earthdata.nasa.gov`.\n", "\n", "**Lesson note:** This URL points you to data that is archived in a cloud environment, namely, PO.DAAC's 'space' in the AWS S3 storage environment." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2020-12-04T06:29:55.060096Z", "start_time": "2020-12-04T06:29:55.056913Z" } }, "outputs": [], "source": [ "modis_url = modis_gran['items'][0]['umm']['RelatedUrls'][1]['URL']\n", "modis_url" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Download a file and open to explore" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Download the file and open it with xarray in memory. This will pull cloud-stored data onto local space. *Note there is no charge for the user in doing so.* \n", "\n", "**Lesson note:** You are now downloading a file to your local space, from the PO.DAAC (Earthdata) archive that lives in the AWS cloud. Again, there is no charge for the user in doing so." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2020-12-04T06:30:04.410453Z", "start_time": "2020-12-04T06:29:55.061545Z" } }, "outputs": [], "source": [ "with request.urlopen(modis_url) as response, open('tutorial1_data_MODIS.nc', 'wb') as out_file:\n", " print('Content Size:', response.headers['Content-length'])\n", " shutil.copyfileobj(response, out_file)\n", " print(\"Downloaded request to tutorial1_data_MODIS.nc\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can preview metadata of the downloaded file with `ncdump`, or open in memory wtih `xarray`." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2020-12-04T06:30:04.421842Z", "start_time": "2020-12-04T06:30:04.418347Z" } }, "outputs": [], "source": [ "#!ncdump -h tutorial1_data_MODIS.nc" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2020-12-04T06:30:04.487207Z", "start_time": "2020-12-04T06:30:04.426000Z" } }, "outputs": [], "source": [ "import xarray as xr\n", "ds_MODIS = xr.open_dataset('tutorial1_data_MODIS.nc')\n", "print(ds_MODIS)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's do a **quick plot** of the `sea_surface_temperature` variable:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2020-12-04T06:30:06.085479Z", "start_time": "2020-12-04T06:30:04.488930Z" } }, "outputs": [], "source": [ "from pandas.plotting import register_matplotlib_converters\n", "\n", "ds_MODIS.sea_surface_temperature.plot()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can see the time this file represents, and the variable, but it's hard to tell where on the map we are. Let's try it another way:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2020-12-04T06:30:48.432752Z", "start_time": "2020-12-04T06:30:06.086741Z" } }, "outputs": [], "source": [ "import cartopy.crs as ccrs\n", "import matplotlib.pyplot as plt\n", "\n", "from mpl_toolkits.axes_grid1 import make_axes_locatable\n", "import numpy as np\n", "\n", "ax = plt.axes(projection=ccrs.PlateCarree())\n", "ax.coastlines()\n", "\n", "plt.scatter(ds_MODIS.lon, ds_MODIS.lat, lw=2, c=ds_MODIS.sea_surface_temperature)\n", "plt.colorbar()\n", "#plt.clim(-0.3, 0.3)\n", "\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. Select data with shp file using the CMR API" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that we've previewed the data and decided we'd like to request more, specific to our use case, we can pass the collection ID into the CMR API to *search* by geographic shape file. The service to *subset by shp* is also in development and should be available in 2021." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Access data selected by geographic shapefile\n", "We will request data overlapping the Gulf of Mexico by uploading a shape file with that boundary. This shape file can be one that you created, shred by a collaborator, or any other user, as long as it follows shape file convention. In essence, this following service allows the user to bring their own shape file to do a data search. The returned response will provide a list of data files from the cloud-based PO.DAAC archive (Earthdata Cloud) that intersect this given shape file.\n", "\n", "This requires the use of a multipart/form-data POST request. Supported shapefile formats include ESRI, GeoJSON, and KML. The associated mime-types are as follows:\n", "\n", "| Shapefile Format | mime-type |\n", "|:-----------------|----------:|\n", "| ESRI | application/shapefile+zip |\n", "| GeoJSON | application/geo+json |\n", "| KML | application/vnd.google-earth.kml+xml |\n", "\n", "ESRI shapefiles must be uploaded as a single .zip file." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Example with ESRI .shp file:\n", "\n", "We will now search with an ESRI shapefile and a temporal bound to limit our space and time region of interest. Namely, we will search over the Gulf of Mexico during the period 1 Aug 2020 to 1 Nov 2020, since we are interested in exploring how SST responds during the latest Atlantic hurricane season. Our spatial boundary shape file to search with over the Gulf of Mexico looks like this (previewed in QGIS):\n", "\n", "\n", "\n", "**Lesson Note:** You are once again searching on data that lives in the PO.DAAC (Earthdata) cloud archive, from your local computer, by running this next code snippet. You are searching spatially (via shape file boundaries) and temporally.\n", "\n", "(Side note: the token parameter below allows you to access this new PO.DAAC cloud operational archive space. This is possible because your Earthdata login was added to a list of early access users to this cloud environment, for this workshop.)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2020-12-04T06:30:49.137241Z", "start_time": "2020-12-04T06:30:48.434222Z" }, "scrolled": false }, "outputs": [], "source": [ "# the URL of the CMR searvice\n", "url = 'https://cmr.earthdata.nasa.gov/search/granules.umm_json'\n", "\n", "#The shapefile we want to use in our search\n", "shp_file = open('resources/gulf_shapefile.zip', 'rb')\n", "\n", "#need to declare the file and the type we are uploading\n", "files = {'shapefile':('gulf_shapefile.zip',shp_file, 'application/shapefile+zip')}\n", "\n", "#used to define parameters such as the concept-id and things like temporal searches\n", "parameters = {'collection_concept_id':'C1940475563-POCLOUD', 'token': _token, 'temporal':'2020-08-23T00:00:00Z,2020-08-29T00:00:00Z', 'page_size':33}\n", "\n", "response = requests.post(url, files=files, params=parameters)\n", "results = response.json()\n", "\n", "print(json.dumps(results['items'][0], indent=2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4. Preview the selected data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `POCLOUD` in the response above signals that you are indeed searching on data from the PO.DAAC archive within the Earthdata Cloud.\n", "\n", "Let's take a look at the url for the first data file this spatial and temporal search result returned:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**For now, let's plot the first files** from our query, that we have downloaded to your local space." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2020-12-04T06:30:49.142051Z", "start_time": "2020-12-04T06:30:49.138854Z" } }, "outputs": [], "source": [ "new_modis_url = results['items'][0]['umm']['RelatedUrls'][1]['URL']\n", "new_modis_url" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2020-12-04T06:30:52.119682Z", "start_time": "2020-12-04T06:30:49.143442Z" } }, "outputs": [], "source": [ "with request.urlopen(new_modis_url) as response, open('tutorial1_data_MODIS_from_shp.nc', 'wb') as out_file:\n", " print('Content Size:', response.headers['Content-length'])\n", " shutil.copyfileobj(response, out_file)\n", " print(\"Downloaded request to tutorial1_data_MODIS_from_shp.nc\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2020-12-04T06:30:52.172485Z", "start_time": "2020-12-04T06:30:52.121636Z" } }, "outputs": [], "source": [ "ds_MODIS_shp = xr.open_dataset('tutorial1_data_MODIS_from_shp.nc')\n", "ds_MODIS_shp" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2020-12-04T06:31:34.060892Z", "start_time": "2020-12-04T06:30:52.174585Z" } }, "outputs": [], "source": [ "import cartopy.crs as ccrs\n", "import matplotlib.pyplot as plt\n", "\n", "from mpl_toolkits.axes_grid1 import make_axes_locatable\n", "import numpy as np\n", "\n", "ax = plt.axes(projection=ccrs.PlateCarree())\n", "ax.coastlines()\n", "\n", "plt.scatter(ds_MODIS_shp.lon, ds_MODIS_shp.lat, lw=2, c=ds_MODIS_shp.sea_surface_temperature)\n", "plt.colorbar()\n", "#plt.clim(-0.3, 0.3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This file from 23 Aug 2020 intersects our shp boundary. In this case, overlap is not extensive, but it does overlap. We can confirm our search by doing the same query in the **Earthdata Search** portal mentioned at the beginning of the tutorial. https://search.earthdata.nasa.gov/search/granules?projectId=8195834861\n", "\n", "Indeed the granule returned matches the search boundary: \n", "\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 5. Download after spatial and temporal selection\n", "Loop to download the MODIS SST files based on our search criteria (those that intersect the region of interest (defined by our shapefile here) and our period of interest." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2020-12-04T06:31:34.072671Z", "start_time": "2020-12-04T06:31:34.068053Z" } }, "outputs": [], "source": [ "urls = []\n", "for r in results['items']:\n", " for u in r['umm']['RelatedUrls']:\n", " if u['URL'].startswith(\"https://archive.podaac\") and u['Type']==\"GET DATA\":\n", " urls.append(u['URL'])\n", "urls" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2020-12-04T06:33:08.813653Z", "start_time": "2020-12-04T06:31:34.074108Z" } }, "outputs": [], "source": [ "for u in urls:\n", " print(u)\n", " with request.urlopen(u) as response, open(f'{basename(u)}', 'wb') as out_file:\n", " print('Content Size:', response.headers['Content-length'])\n", " shutil.copyfileobj(response, out_file)\n", " print(f\"Downloaded request to {basename(u)}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We now have the 33 files from our search downloaded to our local environment (computer). \n", "\n", "\n", "\n", "## Tutorial Summary & Discusssion\n", "\n", "We've searched for a dataset of interest that is archived in the PO.DAAC Earthdata Cloud, previewed it, selected certain data granules that matched a user-defined spatial area (by using .shp files) and temporal range, and downloaded the serach results to our local computer, from the Earthdata Cloud.\n", "\n", "From here, additional analysis can be done. In future guiding notebooks, handling of MODIS L2 SST data into analysis ready data (ARD) 'data cubes'or 'time series stacks' will also be made available, which further shows how to treat level 2 data to create level 3-like uniformly gridded L2 data to your exact specifications. \n", "\n", "And as mentioned at the beginning of the notebook, in this example we are using a shape file to search on ocean remote sensing data, although one can do this type of search on any Earthdata (DAAC) data. For example, of particular interest could be using shape files to search on terrestrial hydrology data (e.g watersheds), globally (depending on data availability of course), or other coastal regions with unique boundaries." ] } ], "metadata": { "hide_input": false, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.7" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": false, "sideBar": false, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": false, "toc_window_display": false } }, "nbformat": 4, "nbformat_minor": 4 }