python-awips/examples/notebooks/METAR_Station_Plot_with_MetPy.ipynb

524 lines
544 KiB
Text
Raw Permalink Normal View History

2018-10-04 17:25:20 -06:00
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a name=\"top\"></a>\n",
"<div style=\"width:1000 px\">\n",
"\n",
"<div style=\"float:right; width:98 px; height:98px;\">\n",
"<img src=\"https://docs.unidata.ucar.edu/images/logos/unidata_logo_vertical_150x150.png\" alt=\"Unidata Logo\" style=\"height: 98px;\">\n",
"</div>\n",
"\n",
"# METAR Staion Plot with MetPy\n",
"**Python-AWIPS Tutorial Notebook**\n",
"\n",
"<div style=\"clear:both\"></div>\n",
"</div>\n",
"\n",
"---\n",
"\n",
"<div style=\"float:right; width:250 px\"><img src=\"../images/metars_preview.png\" alt=\"Sample image of the METARS station plot map produced with this notebook\" style=\"height: 300px;\"></div>\n",
"\n",
"\n",
"# Objectives\n",
"\n",
"* Use python-awips to connect to an edex server\n",
"* Define and filter data request for METAR surface obs\n",
"* Extract necessary data and reformat it for plotting\n",
"* Stylize and plot METAR station data using Cartopy, Matplotlib, and MetPy\n",
"\n",
"---"
]
},
{
"cell_type": "markdown",
"metadata": {
"toc": true
},
"source": [
"<h1>Table of Contents<span class=\"tocSkip\"></span></h1>\n",
"<div class=\"toc\"><ul class=\"toc-item\"><li><span><a href=\"#Imports\" data-toc-modified-id=\"Imports-1\"><span class=\"toc-item-num\">1&nbsp;&nbsp;</span>Imports</a></span></li><li><span><a href=\"#Function:-get_cloud_cover()\" data-toc-modified-id=\"Function:-get_cloud_cover()-2\"><span class=\"toc-item-num\">2&nbsp;&nbsp;</span>Function: get_cloud_cover()</a></span></li><li><span><a href=\"#Initial-Setup\" data-toc-modified-id=\"Initial-Setup-3\"><span class=\"toc-item-num\">3&nbsp;&nbsp;</span>Initial Setup</a></span><ul class=\"toc-item\"><li><span><a href=\"#Initial-EDEX-Connection\" data-toc-modified-id=\"Initial-EDEX-Connection-3.1\"><span class=\"toc-item-num\">3.1&nbsp;&nbsp;</span>Initial EDEX Connection</a></span></li><li><span><a href=\"#Setting-Connection-Location-Names\" data-toc-modified-id=\"Setting-Connection-Location-Names-3.2\"><span class=\"toc-item-num\">3.2&nbsp;&nbsp;</span>Setting Connection Location Names</a></span></li></ul></li><li><span><a href=\"#Filter-by-Time\" data-toc-modified-id=\"Filter-by-Time-4\"><span class=\"toc-item-num\">4&nbsp;&nbsp;</span>Filter by Time</a></span></li><li><span><a href=\"#Use-the-Data!\" data-toc-modified-id=\"Use-the-Data!-5\"><span class=\"toc-item-num\">5&nbsp;&nbsp;</span>Use the Data!</a></span><ul class=\"toc-item\"><li><span><a href=\"#Get-the-Data!\" data-toc-modified-id=\"Get-the-Data!-5.1\"><span class=\"toc-item-num\">5.1&nbsp;&nbsp;</span>Get the Data!</a></span></li><li><span><a href=\"#Extract-all-Parameters\" data-toc-modified-id=\"Extract-all-Parameters-5.2\"><span class=\"toc-item-num\">5.2&nbsp;&nbsp;</span>Extract all Parameters</a></span></li><li><span><a href=\"#Populate-the-Data-Dictionary\" data-toc-modified-id=\"Populate-the-Data-Dictionary-5.3\"><span class=\"toc-item-num\">5.3&nbsp;&nbsp;</span>Populate the Data Dictionary</a></span></li></ul></li><li><span><a href=\"#Plot-the-Data!\" data-toc-modified-id=\"Plot-the-Data!-6\"><span class=\"toc-item-num\">6&nbsp;&nbsp;</span>Plot the Data!</a></span></li><li><span><a href=\"#See-Also\" data-toc-modified-id=\"See-Also-7\"><span class=\"toc-item-num\">7&nbsp;&nbsp;</span>See Also</a></span><ul class=\"toc-item\"><li><span><a href=\"#Related-Notebooks\" data-toc-modified-id=\"Related-Notebooks-7.1\"><span class=\"toc-item-num\">7.1&nbsp;&nbsp;</span>Related Notebooks</a></span></li><li><span><a href=\"#Additional-Documentation\" data-toc-modified-id=\"Additional-Documentation-7.2\"><span class=\"toc-item-num\">7.2&nbsp;&nbsp;</span>Additional Documentation</a></span></li></ul></li></ul></div>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Imports\n",
"\n",
"The imports below are used throughout the notebook. Note the first two imports are coming directly from python-awips and allow us to connect to an EDEX server, and define a timrange used for filtering the data. The subsequent imports are for data manipulation and visualization. "
2018-10-04 17:25:20 -06:00
]
},
{
"cell_type": "code",
"execution_count": 1,
2018-10-04 17:25:20 -06:00
"metadata": {},
"outputs": [],
"source": [
"from awips.dataaccess import DataAccessLayer\n",
"from dynamicserialize.dstypes.com.raytheon.uf.common.time import TimeRange\n",
"from datetime import datetime, timedelta, UTC\n",
2018-10-04 17:25:20 -06:00
"import numpy as np\n",
"import cartopy.crs as ccrs\n",
"import cartopy.feature as cfeature\n",
"import matplotlib.pyplot as plt\n",
"from metpy.calc import wind_components\n",
"from metpy.plots import StationPlot, StationPlotLayout, sky_cover\n",
"from metpy.units import units"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a href=\"#top\">Top</a>\n",
"\n",
"---"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Function: get_cloud_cover()\n",
2018-10-04 17:25:20 -06:00
"\n",
"Returns the cloud coverage values as integer codes (0 through 8)."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
2018-10-04 17:25:20 -06:00
"def get_cloud_cover(code):\n",
" if 'OVC' in code:\n",
" return 8\n",
2018-10-04 17:25:20 -06:00
" elif 'BKN' in code:\n",
" return 6\n",
2018-10-04 17:25:20 -06:00
" elif 'SCT' in code:\n",
" return 4\n",
2018-10-04 17:25:20 -06:00
" elif 'FEW' in code:\n",
" return 2\n",
2018-10-04 17:25:20 -06:00
" else:\n",
" return 0"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a href=\"#top\">Top</a>\n",
2018-10-04 17:25:20 -06:00
"\n",
"---"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Initial Setup"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Initial EDEX Connection\n",
"\n",
"First we establish a connection to Unidata's public EDEX server. With that connection made, we can create a [new data request object](http://unidata.github.io/python-awips/api/IDataRequest.html) and set the data type to ***obs***.\n",
"\n",
"Then, because we're going to uses MetPy's [StationPlot](https://unidata.github.io/MetPy/latest/api/generated/metpy.plots.StationPlot.html) and [StationPlotLayout](https://unidata.github.io/MetPy/latest/api/generated/metpy.plots.StationPlotLayout.html) we need to define several parameters, and then set them on the data request object."
2018-10-04 17:25:20 -06:00
]
},
{
"cell_type": "code",
"execution_count": 3,
2018-10-04 17:25:20 -06:00
"metadata": {},
"outputs": [],
"source": [
"# EDEX Request\n",
"edexServer = \"edex-cloud.unidata.ucar.edu\"\n",
2018-10-04 17:25:20 -06:00
"DataAccessLayer.changeEDEXHost(edexServer)\n",
"request = DataAccessLayer.newDataRequest(\"obs\")\n",
"\n",
"# define desired parameters\n",
"single_value_params = [\"stationName\", \"longitude\", \"latitude\", \n",
2018-10-04 17:25:20 -06:00
" \"temperature\", \"dewpoint\", \"windDir\",\n",
" \"windSpeed\"]\n",
"multi_value_params = [\"skyCover\"]\n",
"\n",
2018-10-04 17:25:20 -06:00
"params = single_value_params + multi_value_params\n",
"\n",
"# set all parameters on the request\n",
"request.setParameters(*(params))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Setting Connection Location Names\n",
"\n",
"We are also going to define specific station IDs so that our plot is not too cluttered. "
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"# Define a list of station IDs to plot\n",
"selected = ['KPDX', 'KOKC', 'KICT', 'KGLD', 'KMEM', 'KBOS', 'KMIA', 'KMOB', 'KABQ', 'KPHX', 'KTTF',\n",
" 'KORD', 'KBIL', 'KBIS', 'KCPR', 'KLAX', 'KATL', 'KMSP', 'KSLC', 'KDFW', 'KNYC', 'KPHL',\n",
" 'KPIT', 'KIND', 'KOLY', 'KSYR', 'KLEX', 'KCHS', 'KTLH', 'KHOU', 'KGJT', 'KLBB', 'KLSV',\n",
" 'KGRB', 'KCLT', 'KLNK', 'KDSM', 'KBOI', 'KFSD', 'KRAP', 'KRIC', 'KJAN', 'KHSV', 'KCRW',\n",
" 'KSAT', 'KBUY', 'K0CO', 'KZPC', 'KVIH', 'KBDG', 'KMLF', 'KELY', 'KWMC', 'KOTH', 'KCAR',\n",
" 'KLMT', 'KRDM', 'KPDT', 'KSEA', 'KUIL', 'KEPH', 'KPUW', 'KCOE', 'KMLP', 'KPIH', 'KIDA', \n",
" 'KMSO', 'KACV', 'KHLN', 'KBIL', 'KOLF', 'KRUT', 'KPSM', 'KJAX', 'KTPA', 'KSHV', 'KMSY',\n",
" 'KELP', 'KRNO', 'KFAT', 'KSFO', 'KNYL', 'KBRO', 'KMRF', 'KDRT', 'KFAR', 'KBDE', 'KDLH',\n",
" 'KHOT', 'KLBF', 'KFLG', 'KCLE', 'KUNV']\n",
"\n",
"# set the location names to the desired station IDs \n",
2018-10-04 17:25:20 -06:00
"request.setLocationNames(*(selected))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a href=\"#top\">Top</a>\n",
"\n",
"---"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Filter by Time\n",
"\n",
"Here we decide how much data we want to pull from EDEX. By default we'll request 1 hour, but that value can easily be modified by [adjusting the `timedelta(hours = 1)`](https://docs.python.org/3/library/datetime.html#timedelta-objects) in line `2`. The more data we request, the longer this section will take to run."
2018-10-04 17:25:20 -06:00
]
},
{
"cell_type": "code",
"execution_count": 5,
2018-10-04 17:25:20 -06:00
"metadata": {},
"outputs": [],
"source": [
"# Time range\n",
"lastHourDateTime = datetime.now(UTC) - timedelta(hours = 1)\n",
2018-10-04 17:25:20 -06:00
"start = lastHourDateTime.strftime('%Y-%m-%d %H')\n",
"beginRange = datetime.strptime( start + \":00:00\", \"%Y-%m-%d %H:%M:%S\")\n",
"endRange = datetime.strptime( start + \":59:59\", \"%Y-%m-%d %H:%M:%S\")\n",
"timerange = TimeRange(beginRange, endRange)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a href=\"#top\">Top</a>\n",
"\n",
"---"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Use the Data!\n",
"### Get the Data!\n",
2018-10-04 17:25:20 -06:00
"\n",
"Now that we have our `request` and TimeRange `timerange` objects ready, we're ready to get the data array from EDEX."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"# Get response\n",
2018-10-04 17:25:20 -06:00
"response = DataAccessLayer.getGeometryData(request,timerange)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Extract all Parameters\n",
"\n",
"In this section we start gathering all the information we'll need to properly display our data. First we create an empty dictionary and array to keep track of all data and unique station IDs. We also create a boolean to help us only grab the first entry for `skyCover` related to a station id.\n",
"\n",
"<br>\n",
"<div class=\"alert-info\">\n",
" <b>Note:</b> The way the data responses are returned, we recieve many <code>skyCover</code> entries for each station ID, but we only want to keep track of the most recent one (first one returned).\n",
"</div>\n",
"\n",
"After defining these variables, we are ready to start looping through our response data. If the response is an entry of `skyCover`, and this is a new station id, then set the skyCover value in the obs dictionary. If this is not a skyCover entry, then explicitly set the `timeObs` variable (because we have to manipulate it slightly), and dynamically set all the remaining parameters."
]
},
2018-10-04 17:25:20 -06:00
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
2022-06-13 16:18:57 -04:00
"outputs": [],
2018-10-04 17:25:20 -06:00
"source": [
"# define a dictionary and array that will be populated from our for loop below\n",
"obs = dict({params: [] for params in params})\n",
2018-10-04 17:25:20 -06:00
"station_names = []\n",
"time_title = \"\"\n",
"i = 0\n",
"\n",
"# cycle through all the data in the response, in reverse order to get the most recent data first\n",
"for ob in reversed(response):\n",
" avail_params = ob.getParameters()\n",
2022-06-13 16:18:57 -04:00
" #print(avail_params)\n",
" # if it has cloud information, we want the last of the 6 entries (most recent)\n",
" if \"skyCover\" in avail_params:\n",
" if i == 5:\n",
" # store the associated cloud cover int for the skyCover string\n",
" obs['skyCover'].append(get_cloud_cover(ob.getString(\"skyCover\")))\n",
" i = i + 1\n",
" elif \"stationName\" in avail_params:\n",
2018-10-04 17:25:20 -06:00
" # If we already have a record for this stationName, skip\n",
" if ob.getString('stationName') not in station_names:\n",
" station_names.append(ob.getString('stationName'))\n",
" i = 0\n",
" if time_title == \"\":\n",
" time_title = str(ob.getDataTime())\n",
2018-10-04 17:25:20 -06:00
" for param in single_value_params: \n",
" if param in avail_params:\n",
" try:\n",
" obs[param].append(ob.getNumber(param))\n",
" except TypeError:\n",
" obs[param].append(ob.getString(param))\n",
2018-10-04 17:25:20 -06:00
" else:\n",
" obs[param].append(None)"
2018-10-04 17:25:20 -06:00
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Populate the Data Dictionary\n",
2018-10-04 17:25:20 -06:00
"\n",
"Next grab the variables out of the obs dictionary we just populated, attach correct units, (calculate their components, in the instance of wind) and put them into a new dictionary that we will hand the plotting function later."
2018-10-04 17:25:20 -06:00
]
},
{
"cell_type": "code",
"execution_count": 8,
2018-10-04 17:25:20 -06:00
"metadata": {},
"outputs": [],
"source": [
"data = dict()\n",
"data['stid'] = np.array(obs['stationName'])\n",
"data['latitude'] = np.array(obs['latitude'])\n",
"data['longitude'] = np.array(obs['longitude'])\n",
"data['air_temperature'] = np.array(obs['temperature'], dtype=float)* units.degC\n",
"data['dew_point_temperature'] = np.array(obs['dewpoint'], dtype=float)* units.degC\n",
"\n",
"direction = np.array(obs['windDir'])\n",
"direction[direction == -9999.0] = 'nan'\n",
"\n",
"u, v = wind_components(np.array(obs['windSpeed']) * units('knots'),\n",
" direction * units.degree)\n",
"data['eastward_wind'], data['northward_wind'] = u, v\n",
"data['cloud_coverage'] = np.array(obs['skyCover'])"
2018-10-04 17:25:20 -06:00
]
},
2018-10-05 17:09:43 -06:00
{
"cell_type": "markdown",
2018-10-05 17:09:43 -06:00
"metadata": {},
"source": [
"<a href=\"#top\">Top</a>\n",
"\n",
"---"
2018-10-05 17:09:43 -06:00
]
},
2018-10-04 17:25:20 -06:00
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Plot the Data!\n",
"\n",
"Now we have all the data we need to create our plot! First we'll assign a projection and create our figure and axes.\n",
"\n",
"Next, we use Cartopy to add common features (land, ocean, lakes, borders, etc) to help give us a more contextual map of the United States to plot the METAR stations on. We create and add a title for our figure as well.\n",
"\n",
"Additionally, we use [MetPy's StationPlotLayout](https://unidata.github.io/MetPy/latest/api/generated/metpy.plots.StationPlotLayout.html) to instantiate a custom layout and define all the attributes we want displayed. We need to then set the data dictionary (containing all of our data values) on the custom layout so it knows what to draw.\n",
"\n",
"Finally, we display the plot!"
2018-10-04 17:25:20 -06:00
]
},
{
"cell_type": "code",
"execution_count": 9,
2018-10-04 17:25:20 -06:00
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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
2018-10-04 17:25:20 -06:00
"text/plain": [
"<Figure size 2000x1000 with 1 Axes>"
2018-10-04 17:25:20 -06:00
]
},
"metadata": {},
2018-10-04 17:25:20 -06:00
"output_type": "display_data"
}
],
"source": [
"proj = ccrs.LambertConformal(central_longitude=-95, central_latitude=35,\n",
" standard_parallels=[35])\n",
2018-10-05 17:09:43 -06:00
"# Create the figure\n",
2018-10-04 17:25:20 -06:00
"fig = plt.figure(figsize=(20, 10))\n",
"ax = fig.add_subplot(1, 1, 1, projection=proj)\n",
2018-10-05 17:09:43 -06:00
"\n",
"# Add various map elements\n",
2018-10-04 17:25:20 -06:00
"ax.add_feature(cfeature.LAND)\n",
"ax.add_feature(cfeature.OCEAN)\n",
"ax.add_feature(cfeature.LAKES)\n",
"ax.add_feature(cfeature.COASTLINE)\n",
"ax.add_feature(cfeature.STATES)\n",
"ax.add_feature(cfeature.BORDERS, linewidth=2)\n",
2018-10-05 17:09:43 -06:00
"\n",
2018-10-04 17:25:20 -06:00
"# Set plot bounds\n",
"ax.set_extent((-118, -73, 23, 50))\n",
"ax.set_title(time_title + \" | METAR | \" + edexServer)\n",
2018-10-04 17:25:20 -06:00
"\n",
"# Winds, temps, dewpoint, station id\n",
"custom_layout = StationPlotLayout()\n",
"custom_layout.add_barb('eastward_wind', 'northward_wind', units='knots')\n",
"custom_layout.add_value('NW', 'air_temperature', fmt='.0f', units='degF', color='darkred')\n",
"custom_layout.add_value('SW', 'dew_point_temperature', fmt='.0f', units='degF', color='darkgreen')\n",
"custom_layout.add_symbol('C', 'cloud_coverage', sky_cover)\n",
"\n",
2018-10-04 17:25:20 -06:00
"stationplot = StationPlot(ax, data['longitude'], data['latitude'], clip_on=True,\n",
" transform=ccrs.PlateCarree(), fontsize=10)\n",
"stationplot.plot_text((2, 0), data['stid'])\n",
2018-10-05 17:09:43 -06:00
"custom_layout.plot(stationplot, data)\n",
"\n",
2018-10-05 17:09:43 -06:00
"plt.show()"
2018-10-04 17:25:20 -06:00
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a href=\"#top\">Top</a>\n",
"\n",
"---"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## See Also\n",
"\n",
"- [Aviation Weather Center Static METAR Plots Information](https://www.aviationweather.gov/metar/help?page=plot)\n",
"\n",
"### Related Notebooks\n",
"\n",
"- [Grid Levels and Parameters](http://unidata.github.io/python-awips/examples/generated/Grid_Levels_and_Parameters.html)\n",
"- [Colored Surface Temperature Plot](http://unidata.github.io/python-awips/examples/generated/Colored_Surface_Temperature_Plot.html)\n",
"\n",
"### Additional Documentation\n",
"\n",
"**python-awips:**\n",
"\n",
"- [DataAccessLayer.changeEDEXHost()](http://unidata.github.io/python-awips/api/DataAccessLayer.html#awips.dataaccess.DataAccessLayer.changeEDEXHost)\n",
"- [DataAccessLayer.newDataRequest()](http://unidata.github.io/python-awips/api/DataAccessLayer.html#awips.dataaccess.DataAccessLayer.newDataRequest)\n",
"- [IDataRequest](http://unidata.github.io/python-awips/api/IDataRequest.html)\n",
"- [DataAccessLayer.getGeometryData](http://unidata.github.io/python-awips/api/PyGeometryData.html)\n",
"\n",
"**datetime:**\n",
"\n",
"- [datetime.datetime](https://docs.python.org/3/library/datetime.html#datetime-objects)\n",
"- [datetime.now(UTC)](https://docs.python.org/3/library/datetime.html?#datetime.datetime.utcnow)\n",
"- [datetime.timedelta](https://docs.python.org/3/library/datetime.html#timedelta-objects)\n",
"- [datetime.strftime() and datetime.strptime()](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior)\n",
"\n",
"**numpy:**\n",
"\n",
"- [np.array](https://numpy.org/doc/stable/reference/generated/numpy.array.html)\n",
"\n",
"**cartopy:**\n",
"\n",
"- [cartopy projection list](https://scitools.org.uk/cartopy/docs/v0.14/crs/projections.html?#cartopy-projection-list)\n",
"- [cartopy feature interface](https://scitools.org.uk/cartopy/docs/v0.14/matplotlib/feature_interface.html)\n",
"\n",
"**matplotlib:**\n",
"\n",
"- [matplotlib.pyplot()](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.html)\n",
"- [matplotlib.pyplot.figure()](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.figure.html)\n",
"- [matplotlib.pyplot.figure.add_subplot](https://matplotlib.org/stable/api/figure_api.html#matplotlib.figure.Figure.add_subplot)\n",
"- [ax.set_extent](https://matplotlib.org/stable/api/image_api.html?highlight=set_extent#matplotlib.image.AxesImage.set_extent)\n",
"- [ax.set_title](https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.set_title.html)\n",
"\n",
"\n",
"**metpy:**\n",
"\n",
"- [metpy.calc.wind_components](https://unidata.github.io/MetPy/latest/api/generated/metpy.calc.wind_components.html)\n",
"- [metpy.plots.StationPlot()](https://unidata.github.io/MetPy/latest/api/generated/metpy.plots.StationPlot.html)\n",
"- [metpy.plots.StationPlotLayout()](https://unidata.github.io/MetPy/latest/api/generated/metpy.plots.StationPlotLayout.html)\n",
"- [metpy.units](https://unidata.github.io/MetPy/latest/api/generated/metpy.units.html)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a href=\"#top\">Top</a>\n",
"\n",
"---"
]
2018-10-04 17:25:20 -06:00
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
2018-10-04 17:25:20 -06:00
"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.13.1"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": true,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": true,
"toc_position": {
"height": "calc(100% - 180px)",
"left": "10px",
"top": "150px",
"width": "287.9779357910156px"
},
"toc_section_display": true,
"toc_window_display": true
2018-10-04 17:25:20 -06:00
}
},
"nbformat": 4,
"nbformat_minor": 4
2018-10-04 17:25:20 -06:00
}