{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Basic plotting with pandas and Matplotlib\n", "\n", "```{attention}\n", "Finnish university students are encouraged to use the CSC Notebooks platform.
\n", " \n", "\n", "Others can follow the lesson and fill in their student notebooks using Binder.
\n", " \n", "```\n", "\n", "As we're now familiar with some of the features of [pandas](https://pandas.pydata.org/), we will wade into visualizing our data in Python using the built-in plotting options available directly in pandas. Much like the case of pandas being built upon [NumPy](https://numpy.org/), plotting in pandas takes advantage of plotting features from the [Matplotlib](https://matplotlib.org/) plotting library. Plotting in pandas provides a basic framework for visualizing our data, but as you'll see we will sometimes need to also use features from Matplotlib to enhance our plots. In particular, we will use features from the the `pyplot` module in Matplotlib, which provides [MATLAB](https://www.mathworks.com/products/matlab.html)-like plotting.\n", "\n", "Toward the end of the lesson we will also briefly explore creating interactive plots using the [Pandas-Bokeh](https://github.com/PatrikHlobil/Pandas-Bokeh) plotting backend, which allows us to produce plots similar to those available in the [Bokeh plotting library](https://docs.bokeh.org/en/latest/index.html) using plotting syntax similar to that used normally in pandas. This is an optional part of the lesson, but will allow you to see an example for further exploration of interactive plotting using Pandas-Bokeh." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Input data\n", "\n", "In the lesson this week we are using some of the same weather observation data from Finland [downloaded from NOAA](https://www7.ncdc.noaa.gov/CDO/cdopoemain.cmd?datasetabbv=DS3505&countryabbv=&georegionabbv=&resolution=40) that we used in Lesson 6. In this case we'll focus on weather observation station data from the Helsinki-Vantaa airport.\n", "\n", "## Downloading the data\n", "\n", "```{attention}\n", "It is recommended to use the Geo-Python Lite blueprint for this lesson.\n", "```\n", "\n", "Just like last week, the first step for today's lesson is to get the data. Unlike last week, we'll all download and use the same data.\n", "\n", "You can download the data by opening a new terminal window in Jupyter Lab by going to **File** -> **New** -> **Terminal** in the JupyterLab menu bar. Once the terminal is open, you will need to navigate to the directory for Lesson 7 by typing\n", "\n", "```bash\n", "cd notebooks/L7/\n", "```\n", "\n", "or the equivalent command to navigate to the location of the Lesson 7 files on your computer (for those running Jupyter on their own computers).\n", "\n", "\n", "You can now confirm you're in the correct directory by typing\n", "\n", "```bash\n", "ls\n", "```\n", "\n", "You should see something like the following output:\n", "\n", "```bash\n", "advanced-plotting.ipynb matplotlib.ipynb\n", "img metadata\n", "```\n", "\n", "If so, you're in the correct directory and you can download the data files by typing\n", "\n", "```bash\n", "wget https://davewhipp.github.io/data/Finland-weather-data-L7.tar.gz\n", "```\n", "\n", "After the download completes, you can extract the data files by typing\n", "\n", "```bash\n", "tar zxvf Finland-weather-data-L7.tar.gz\n", "```\n", "\n", "At this stage you should have a new directory called `data` that contains the data for this week's lesson. You can confirm this by typing\n", "\n", "```bash\n", "ls data\n", "```\n", "\n", "You should see something like the following:\n", "\n", "```bash\n", "029740.txt 6367598020644inv.txt\n", "3505doc.txt 6367598020644stn.txt\n", "```\n", "\n", "Now you should be all set to proceed with the lesson!\n", "\n", "### Binder users\n", "\n", "It is not recommended to complete this lesson using Binder.\n", "\n", "## About the data\n", "\n", "As part of the download there are a number of files that describe the weather data. These *metadata* files include:\n", "\n", "- A list of stations: [data/6367598020644stn.txt](metadata/6367598020644stn.txt)\n", "- Details about weather observations at each station: [data/6367598020644inv.txt](metadata/6367598020644inv.txt)\n", "- A data description (i.e., column names): [data/3505doc.txt](metadata/3505doc.txt)\n", "\n", "The input data for this week are separated with varying number of spaces (i.e., fixed width). The first lines and columns of the data look like following:\n", "\n", "``` \n", " USAF WBAN YR--MODAHRMN DIR SPD GUS CLG SKC L M H VSB MW MW MW MW AW AW AW AW W TEMP DEWP SLP ALT STP MAX MIN PCP01 PCP06 PCP24 PCPXX SD\n", "029740 99999 195201010000 200 23 *** 15 OVC 7 2 * 5.0 63 ** ** ** ** ** ** ** 6 36 32 989.2 ***** ****** *** *** ***** ***** ***** ***** **\n", "029740 99999 195201010600 220 18 *** 8 OVC 7 2 * 2.2 63 ** ** ** ** ** ** ** 6 37 37 985.9 ***** ****** *** 34 ***** ***** ***** ***** **\n", "029740 99999 195201011200 220 21 *** 5 OVC 7 * * 3.8 59 ** ** ** ** ** ** ** 5 39 36 988.1 ***** ****** *** *** ***** ***** ***** ***** **\n", "029740 99999 195201011800 250 16 *** 722 CLR 0 0 0 12.5 02 ** ** ** ** ** ** ** 5 36 27 991.9 ***** ****** 39 *** ***** ***** ***** ***** **\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Getting started\n", "\n", "Let's start by importing Pandas and reading our data file." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pandas as pd" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```{admonition} Datetime in Python\n", "For the lesson this week we will be using a datetime index for our weather observations.\n", "We did not cover the datetime data type in detail in Lesson 6, but you can find [a brief introduction to datetime in the Lesson 6 materials](https://geo-python-site.readthedocs.io/en/latest/notebooks/L6/advanced-data-processing-with-pandas.html#datetime-optional-for-lesson-6).\n", "```\n", "\n", "Just as we did last week, we'll read our data file by passing a few parameters to the Pandas `read_csv()` function. In this case, however, we'll include a few additional parameters in order to read the data with a *datetime index*. Let's read the data first, then see what happened." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fp = r\"data/029740.txt\"\n", "\n", "data = pd.read_csv(\n", " fp,\n", " delim_whitespace=True,\n", " na_values=[\"*\", \"**\", \"***\", \"****\", \"*****\", \"******\"],\n", " usecols=[\"YR--MODAHRMN\", \"TEMP\", \"MAX\", \"MIN\"],\n", " parse_dates=[\"YR--MODAHRMN\"],\n", " index_col=\"YR--MODAHRMN\",\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So what's different here? Well, we have added two new parameters: `parse_dates` and `index_col`.\n", "\n", "- `parse_dates` takes a Python list of column name(s) containing date data that Pandas will parse and convert to the *datetime* data type. For many common date formats this parameter will automatically recognize and convert the date data.\n", "- `index_col` is used to state a column that should be used to index the data in the DataFrame. In this case, we end up with our date data as the DataFrame index. This is a very useful feature in Pandas as we'll see below.\n", "\n", "Having read in the data, let's have a quick look at what we have using `data.head()`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "data.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As mentioned above, you can now see that the index column for our DataFrame (the first column) contains date values related to each row in the DataFrame." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Basic x-y plot\n", "\n", "Now we're ready for our first plot. We can start by using the basic line plot in Pandas to look at our temperature data." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ax = data.plot()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If all goes well, you should see the plot above.\n", "\n", "OK, so what happened here?\n", "\n", "1. We first created the plot object using the `plot()` method of the `data` DataFrame. Without any parameters given, this makes the plot of all columns in the DataFrame as lines of different color on the y-axis with the index, time in this case, on the x-axis.\n", "2. In case we want to be able to modify the plot or add anything, we assign the plot object to the variable `ax`. We can check its type below.\n", "\n", "In fact, let's check the type of the `ax` variable now." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "type(ax)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "OK, so it looks like we have some kind of plot data type that is part of Matplotlib. Clearly, pandas is using Matplotlib for generating our plots.\n", "\n", "### Selecting our plotted data\n", "\n", "Now, let's make a few small changes to our plot and plot the data again. First, let's only plot the observed temperatures in the `data['TEMP']` column, and let's restrict ourselves to observations from the afternoon of October 1, 2019 (the last day in our dataset). We can do this by selecting the desired data column and date range first, then plotting our selection." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "oct1_temps = data[\"TEMP\"].loc[data.index >= \"201910011200\"]\n", "ax = oct1_temps.plot()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So, what did we change?\n", "\n", "1. Well, we selected only the `'TEMP'` column now by using `data['TEMP']` instead of `data`.\n", "2. We've added a restriction to the date range using `loc[]` to select only rows where the index value `data.index` is greater than `'201910011200'`. In that case, the number in the string is in the format `'YYYYMMDDHHMM'`, where `YYYY` is the year, `MM` is the month, `DD` is the day, `HH` is the hour, and `MM` is the minute. Now we have all observations from noon onward on October 1, 2019.\n", "3. By saving this selection to the DataFrame `oct1_temps` we're able to now use `oct1_temps.plot()` to plot only our selection. This is cool, but we can do even better..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Basic plot formatting\n", "\n", "We can make our plot look a bit nicer and provide more information by using a few additional plotting options to pandas/Matplotlib." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Change line and symbol format, and add axis labels/title\n", "ax = oct1_temps.plot(\n", " style=\"ro--\",\n", " title=\"Helsinki-Vantaa temperatures\",\n", " xlabel=\"Date\",\n", " ylabel=\"Temperature [°F]\",\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we see our temperature data as a red dashed line with circles showing the data points.\n", "This comes from the additional `style='ro--'` used with `oct1_temps.plot()`.\n", "In this case, `r` tells the `oct1_temps.plot()` function to use red color for the lines and symbols, `o` tells it to show circles at the points, and `--` says to use a dashed line.\n", "You can use `help(oct1_temps.plot)` to find out more about formatting plots or have a look at the [documentation on the pandas website](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.plot.line.html#pandas.DataFrame.plot.line).\n", "We have also added a title using the `title` parameter, and axis labels using the `xlabel` and `ylabel` parameters." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "help(oct1_temps.plot)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Embiggening\\* the plot\n", "\n", "While the plot sizes we're working with are OK, it would be nice to have them displayed a bit larger.\n", "Fortunately, there is an easy way to make the plots larger in pandas/Matplotlib.\n", "We can simply add the `figsize` parameter with the desired figure size listed as a tuple (a set of values in normal parentheses) that lists the width and height of the figure (in inches!)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Adjust the figure size\n", "ax = oct1_temps.plot(\n", " style=\"ro--\",\n", " title=\"Helsinki-Vantaa temperatures\",\n", " xlabel=\"Date\",\n", " ylabel=\"Temperature [°F]\",\n", " figsize=(12, 6),\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The cell above sets the default plot size to be 12 inches wide by 6 inches tall.\n", "Feel free to change these values if you prefer.\n", "\n", "```{note}\n", "It is also possible to change the default figure size for all figures in a Jupyter Notebook by importing the pyplot module from matplotlib (i.e., `import matplotlib.pyplot as plt`)and then defining the default figure size using `plt.rcParams['figure.figsize'] = [12, 6]`. In this case the figure size should be given as a Python list.\n", "```\n", "\n", "\\* To [embiggen](https://www.lexico.com/definition/embiggen) means to enlarge.\n", "It's a perfectly [cromulent](https://www.lexico.com/definition/cromulent) word." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Other common plot formatting operations\n", "\n", "#### Adding text to the plot\n", "\n", "Adding text to plots can be done using `ax.text()`.\n", "\n", "```python\n", "ax.text(x, y, 'Text to display')\n", "```\n", "\n", "This would display \"Text to display\" at the location *x*, *y* on the plot.\n", "We'll see how to do this in a live example in just a second.\n", "\n", "#### Changing the axis ranges\n", "\n", "Changing the plot axes can be done using the `xlim` and `ylim` parameters of the `plot()` function\n", "\n", "```python\n", "df.plot(xlim=[xmin, xmax], ylim=[ymin, ymax])\n", "```\n", "\n", "where `xmin` should be the minimum bound of the x-axis, `xmax` should be the maximum bound, and the same goes for the y-axis with `ymin` and `ymax`.\n", "\n", "#### Dealing with datetime axes\n", "\n", "One issue we will encounter with both placing text on the plot and changing the axis ranges is our datetime index for our DataFrame. In order to do either thing, we need to define x-values using a datetime object. The easiest way to do this is to use the Pandas `pd.to_datetime()` function, which converts a character string date to a datetime object. For example, we can convert 13:00 on October 1, 2019 from the character string `'201910011300'` to a datetime equivalent by typing" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pd.to_datetime(\"201910011300\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With this datetime issue in mind, let's now consider a modified version of the plot above, we can\n", "\n", "1. Limit our time range to 12:00 to 15:00 on October 1, 2019\n", "2. Only look at temperatures between 40-46° Fahrenheit\n", "3. Add text to note the coldest part of the early afternoon." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Define the start, end, and cold times\n", "start_time = pd.to_datetime(\"201910011200\")\n", "end_time = pd.to_datetime(\"201910011500\")\n", "cold_time = pd.to_datetime(\"201910011205\")\n", "\n", "# Create the plot, including the axis limits\n", "ax = oct1_temps.plot(\n", " style=\"ro--\",\n", " title=\"Helsinki-Vantaa temperatures\",\n", " xlabel=\"Date\",\n", " ylabel=\"Temperature [°F]\",\n", " figsize=(12, 6),\n", " xlim=[start_time, end_time],\n", " ylim=[40.0, 46.0],\n", ")\n", "\n", "# Add text to display the coldest temperature\n", "ax.text(cold_time, 42.0, \"<- Coldest temperature in early afternoon\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Check your understanding\n", "\n", "Create a line plot similar to our examples above with the following attributes:\n", " \n", "- Temperature data from 18:00-24:00 on October 1, 2019\n", "- A dotted black line connecting the observations (do not show the data points)\n", "- A title that reads \"Evening temperatures on October 1, Helsinki-Vantaa\"\n", "- A text label indicating the warmest temperature in the evening" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "hide-cell" ] }, "outputs": [], "source": [ "# Solution\n", "# Define start, end, and cold times\n", "start_time = pd.to_datetime(\"201910011800\")\n", "end_time = pd.to_datetime(\"201910020000\")\n", "warm_time = pd.to_datetime(\"201910012120\")\n", "\n", "# Create the plot, including the axis limits\n", "\n", "ax = oct1_temps.plot(\n", " style=\"k:\",\n", " title=\"Evening temperatures on October 1, Helsinki-Vantaa\",\n", " xlabel=\"Date\",\n", " ylabel=\"Temperature [°F]\",\n", " figsize=(12, 6),\n", " xlim=[start_time, end_time],\n", " ylim=[35.0, 44.0],\n", ")\n", "\n", "# Display text on plot\n", "ax.text(warm_time, 43.0, \"Warmest time of the evening ->\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Bar plots in pandas\n", "\n", "In addition to line plots, there are many other options for plotting in pandas. Bar plots are one option, which can be used quite similarly to line plots with the addition of the `kind=bar` parameter. Note that it is easiest to plot our selected time range for a bar plot by selecting the dates in our data series first, rather than adjusting the plot limits. pandas sees bar plot data as categorical, so the date range is more difficult to define for x-axis limits. For the y-axis, we can still define its range using the `ylim=[ymin, ymax]` parameter. Similarly, text placement on a bar plot is more difficult, and most easily done using the index value of the bar where the text should be placed." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Define time range\n", "oct1_afternoon = oct1_temps.loc[oct1_temps.index <= \"201910011500\"]\n", "\n", "# Create bar plot\n", "ax = oct1_afternoon.plot(\n", " kind=\"bar\",\n", " title=\"Helsinki-Vantaa temperatures\",\n", " xlabel=\"Date\",\n", " ylabel=\"Temperature [°F]\",\n", " figsize=(12, 6),\n", " ylim=[40, 46],\n", ")\n", "\n", "# Add plot text\n", "ax.text(0, 42.1, \"Coldest \\ntemp \\nv\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can find more about how to format bar charts on the [pandas documentation website](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.plot.bar.html)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Saving your plots as image files\n", "\n", "Saving plots created using pandas can be done in several ways. The recommendation for use outside of Jupyter notebooks is to use Matplotlib's `plt.savefig()` function. When using `plt.savefig()`, you simply give a list of commands to generate a plot and include `plt.savefig()` with some parameters as the last command in the Python cell. The file name is required, and the image format will be determined based on the listed file extension. Note that because we have not used Matplotlib for any of the earlier plots, we need to import it here first.\n", "\n", "Matplotlib plots can be saved in a number of useful file formats, including PNG, PDF, and EPS. PNG is a nice format for raster images, and EPS is probably easiest to use for vector graphics. Let's check out an example and save our lovely bar plot." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Import matplotlib\n", "import matplotlib.pyplot as plt\n", "\n", "# Create bar plot\n", "ax = oct1_afternoon.plot(\n", " kind=\"bar\",\n", " title=\"Helsinki-Vantaa temperatures\",\n", " xlabel=\"Date\",\n", " ylabel=\"Temperature [°F]\",\n", " figsize=(12, 6),\n", " ylim=[40, 46],\n", ")\n", "\n", "# Add plot text\n", "ax.text(0, 42.1, \"Coldest \\ntemp \\nv\")\n", "\n", "# Save plot to file\n", "plt.savefig(\"bar-plot.png\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you refresh your **Files** tab on the left side of the JupyterLab window you should now see `bar-plot.png` listed.\n", "We could try to save another version in higher resolution with a minor change to our plot commands above." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Create bar plot\n", "ax = oct1_afternoon.plot(\n", " kind=\"bar\",\n", " title=\"Helsinki-Vantaa temperatures\",\n", " xlabel=\"Date\",\n", " ylabel=\"Temperature [°F]\",\n", " figsize=(12, 6),\n", " ylim=[40, 46],\n", ")\n", "\n", "# Add plot text\n", "ax.text(0, 42.1, \"Coldest \\ntemp \\nv\")\n", "\n", "# Save plot to file (high resolution, PDF)\n", "plt.savefig(\"bar-plot-hi-res.pdf\", dpi=600)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Interactive plotting with Pandas-Bokeh (optional)\n", "\n", "One of the cool things in Jupyter notebooks is that our plots need not be static. We can easily create plots that are interactive, allowing us to view data values by mousing over them, or click to enable/disable plotting of some data. There are several ways we can do this, but we'll utilize the [Pandas-Bokeh plotting backend](https://github.com/PatrikHlobil/Pandas-Bokeh), which allows us to create interactive plots with little additional effort.\n", "\n", "To get started, we need to import Pandas-Bokeh and configure our notebook to use it for plotting out Pandas data." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pandas_bokeh\n", "\n", "pandas_bokeh.output_notebook()\n", "pd.set_option(\"plotting.backend\", \"pandas_bokeh\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the cell above, we import Pandas-Bokeh, and the configure two options: (1) Setting the output to be displayed in a notebook rather than in a separate window, and (2) setting the plotting backend software to use Pandas-Bokeh rather than Matplotlib.\n", "\n", "Now, we can consider an example plot similar to the one we started with, but with data for three days (September 29-October 1, 2019). Pandas-Bokeh expects a DataFrame as the source for the plot data, so we'll need to create a time slice of the `data` DataFrame containing the desired date range before making the plot. Let's generate the Pandas-Bokeh plot and the see what is different." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Define data time slice\n", "sept29_oct1_df = data.loc[data.index >= \"201909290000\"]\n", "\n", "# Define start, end times\n", "start_time = pd.to_datetime(\"201909290000\")\n", "end_time = pd.to_datetime(\"201910020000\")\n", "\n", "# Create plot\n", "ax = sept29_oct1_df.plot(\n", " title=\"Helsinki-Vantaa temperatures\",\n", " xlabel=\"Date\",\n", " ylabel=\"Temperature [°F]\",\n", " figsize=(800, 500),\n", " xlim=[start_time, end_time],\n", " ylim=[35.0, 60.0],\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So now we have a similar plot to those generated previously, but when you move the mouse along the curve you can see the temperature values at each time. We can also hide any of the lines by clicking on them in the legend, as well as use the scroll wheel/trackpad to zoom.\n", "\n", "But we did also have to make a few small changes to generate this plot:\n", "\n", "1. We need to use a DataFrame as the data source for the plot, rather than a pandas Series. Thus, `data['TEMP'].plot()` will not work with Pandas-Bokeh.\n", "2. The line color and plotting of points are not specified using the `style` keyword. Instead, the line colors could be specified using the `color` or `colormap` parameters. Plotting of the points is enabled using the `plot_data_points` parameter (see below). More information about formatting the lines can be found on the [Pandas-Bokeh website](https://github.com/PatrikHlobil/Pandas-Bokeh).\n", "3. The `figsize` parameter used here is expecting values in pixels, not inches!\n", "4. We have not included a text label on the plot, as it may not be possible to do so with Pandas-Bokeh.\n", "\n", "But otherwise, we are able to produce these cool interactive plots with minimal effort, and directly within our notebooks!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ax = sept29_oct1_df.plot(\n", " title=\"Helsinki-Vantaa temperatures\",\n", " xlabel=\"Date\",\n", " ylabel=\"Temperature [°F]\",\n", " figsize=(800, 500),\n", " xlim=[start_time, end_time],\n", " ylim=[35.0, 60.0],\n", " plot_data_points=True,\n", ")" ] } ], "metadata": { "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.10" } }, "nbformat": 4, "nbformat_minor": 4 }