{ "cells": [ { "cell_type": "markdown", "metadata": { "cell_id": "00000-cd1cd682-a752-4f3e-bffa-7bcff78dd3f3", "deepnote_cell_type": "markdown", "tags": [] }, "source": [ "## Note: Sorry this notebook isn't working right now and is under development.\n", "\n", "# Berkeley Air Quality Notebook" ] }, { "cell_type": "markdown", "metadata": { "cell_id": "00001-eea90949-a595-4c05-9627-1be57f450aa2", "deepnote_cell_type": "markdown", "tags": [] }, "source": [ "**Welcome to our notebook on Berkeley Air Quality!** \n", "\n", "In this notebook we will be looking at Air Quality Index (AQI) scores in the surrounding Berkeley, CA area. With so many pollutants in the air, especially as we head into the annual fire season, AQI becomes something we check on daily. For many of us, this AQI map is all too familiar. Throughout this module we will discuss how data can be used to visualize and uncover underlying trends in the world.\n", "\n", "**Let's get started!**" ] }, { "cell_type": "markdown", "metadata": { "cell_id": "00002-489baeae-20ba-44bf-bac5-3e7931d8c9b5", "deepnote_cell_type": "markdown", "tags": [] }, "source": [ "

\n", " \n", "

" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "## Introduction to Jupyter Notebook" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Before we get started with the data, let's talk about what Jupyter Notebook is. This lab is set up in a Jupyter Notebook. Notebooks can contain anything from live code, to written text, equations or visualizations. The content of notebooks are written into rectangular sections called **cells**. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Types of Cells\n", "There are two types of cells in Jupyter, **code** cells and **markdown** cells. **Code cells**, as you can imagine, contain code in Python, the programming language that we will be using throughout this notebook. **Markdown cells**, such as this one, contain written text. You can select any cell by clicking on it one. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Running Cells\n", "'Running' a cell is similar to pressing 'Enter' on a calculator once you've typed in an expression; it computes all of the expressions contained within the cell.\n", "\n", "To run a cell, you can do one of the following:\n", "\n", "- press **Shift + Enter**\n", "- click the **Run** button on the top tool bar\n", "\n", "Running a markdown cell will embed the text into the notebook and running a code cell will evaluate the code and display its output under the cell. \n", "\n", "Let's try it! **Run the code cell below.**" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello World!\n" ] } ], "source": [ "print(\"Hello World!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Editing and Saving\n", "\n", "- To **edit** a cell, simply double click on the desired cell and begin typing. The cell that you are currently working in will be highlighted by a green box.\n", "- To **save** the notebook, either click *Ctl + S* or navigate to the \"File\" dropdown and select \"Save and Checkpoint\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Adding Cells\n", "You can add a cell by clicking Insert > Insert Cell Below and choose the cell type in the drop down menu. Try adding a cell below to type in your name!\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Deleting Cells \n", "To delete a cell, click on the scissors at the top or Edit > Cut Cells. Delete the cell below." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Delete this!\n" ] } ], "source": [ "print(\"Delete this!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Important Tip**: Everytime you open a Jupyter notebook, it is extremely important to run all the cells from the beginning in order for the notebook to work. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that we have had a brief crash course on Jupyter Notebooks, let's dive into Berkeley AQI!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "## Introduction to the Data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this notebook we will look at data collected from PrupleAir, a company that manages a network of air quality sensors. The data from these sensors are then collected to create maps like the one displayed above that depicts an intuitive visualization of the air quality in a specific region. In the dataframe below, you will find several metrics that help us do this.\n", "\n", "**Before we begin:**\n", "\n", "- Click on Cell in the top toolbar \n", "- Click on Run All in the drop down\n", "- Scroll back up to begin going through the notebook!" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import purpleair\n", "import folium\n", "import ipywidgets as widgets\n", "from ipywidgets import interact, interactive, fixed, interact_manual\n", "from datetime import datetime\n", "from IPython.display import clear_output" ] }, { "cell_type": "markdown", "metadata": { "cell_id": "00001-72481491-c164-40df-858a-6759fca18e03", "deepnote_cell_type": "markdown", "tags": [] }, "source": [ "
\n", "\n", "# PurpleAir Data" ] }, { "cell_type": "markdown", "metadata": { "cell_id": "00003-aac88ff5-4bf9-442e-90fc-78b5b0e31a8f", "deepnote_cell_type": "markdown", "tags": [] }, "source": [ "Before we begin looking at data collected from PurpleAir sensors, lets first take a look at what a sensor is, and what it measures. \n", "\n", "\n", "> Below is a picture of a real PurpleAir Air Quality Sensor. These sensor can be mounted both indoors or outdoors, and it tracks airborne particulate matter(PM) in real time using PMSX003 laser counters. Particulate matter can include things like dust, smoke, dirt and any other organic or inorganic particles in the air. With multiple sensors mounted in a region, PurpleAir can create a relatively accurate measure of AQI throughout the day as the air quality changes. \n", "\n", "For more information on how sensors work, take a look at the official PurpleAir website [here](https://www2.purpleair.com/community/faq#hc-what-do-the-numbers-on-the-purpleair-map-mean-1)!" ] }, { "cell_type": "markdown", "metadata": { "cell_id": "00005-3bf891ed-400f-4d50-bcc9-2c60b10d2130", "deepnote_cell_type": "markdown", "tags": [] }, "source": [ "

\n", " \n", "

" ] }, { "cell_type": "markdown", "metadata": { "cell_id": "00007-e6e85ff5-acd5-4266-ba7c-be37a447c88d", "deepnote_cell_type": "markdown", "tags": [] }, "source": [ "In order to work with the data, we need to pull it into our workspace. Fortunately, PurpleAir has created an API that allows users to pull in and work with their AQI data. In the code cell below we will import the purpleair API and use it to create a dataframe of data from all PurpleAir sensors, which is roughly ~20,000!\n", "\n", "**Run the code cell below!**" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "cell_id": "00002-01085d88-3421-49bd-b74e-05511d2afc01", "deepnote_cell_type": "code", "deepnote_to_be_reexecuted": false, "execution_millis": 8000, "execution_start": 1639017393509, "source_hash": "5c996320", "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Initialized 22,479 sensors!\n" ] } ], "source": [ "from purpleair.network import SensorList\n", "p = SensorList()\n", "df = p.to_dataframe(sensor_filter='all',\n", " channel='parent')" ] }, { "cell_type": "markdown", "metadata": { "cell_id": "00009-648d6ac3-7625-4663-bbe3-458b23ef4493", "deepnote_cell_type": "markdown", "tags": [] }, "source": [ "The dataframe below contains all the sensor data as of the latest update. It contains data on everything from the geograohical latitude and longitude of the sensor to data on the last time that sensor measured airborne PM." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "cell_id": "00003-4a5c5d0c-9e21-4990-8509-faddb3670857", "deepnote_cell_type": "code", "deepnote_to_be_reexecuted": false, "execution_millis": 257, "execution_start": 1639017401517, "scrolled": true, "source_hash": "f804c160", "tags": [] }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
parentlatlonnamelocation_typepm_2.5temp_ftemp_chumiditypressure...last_update_checkcreateduptimeis_owner10min_avg30min_avg1hour_avg6hour_avg1day_avg1week_avg
id
14633None37.275561-121.964134Hazelwood canaryoutside0.8165.018.33333354.01008.57...NoneNoneNoneFalse0.884.108.2617.7221.8015.42
25999None30.053808-95.494643Villages of Bridgestone AQIoutside35.1670.021.11111170.01011.86...NoneNoneNoneFalse34.7133.5932.7726.0316.3014.88
14091None37.883620-122.070087WC Hillsideoutside1.0063.017.22222257.01003.26...NoneNoneNoneFalse2.282.723.1616.2525.8022.91
108226None38.573703-121.439113\"C\" Street Air Shelterinside4.7678.025.55555645.01015.66...NoneNoneNoneFalse4.774.494.153.964.755.46
49409None18.75918299.017172\"First's Place\"outside40.8387.030.55555637.0986.64...NoneNoneNoneFalse45.7049.9850.0850.6146.3232.53
..................................................................
64085None36.785883127.157040청룡동행정복지센터outside40.3554.012.22222246.01027.31...NoneNoneNoneFalse37.2438.7841.6657.5963.4045.20
64995None36.691324126.585255한서대학교outside40.6463.017.22222233.01017.02...NoneNoneNoneFalse45.0345.9743.9639.6837.2228.38
64093None36.710720126.548390해미읍성outside62.0761.016.11111144.01027.89...NoneNoneNoneFalse57.9356.0652.5143.1940.9831.83
29747None36.761236127.395300화덕보건진료소outside34.7762.016.66666736.01018.16...NoneNoneNoneFalse40.3649.2952.2649.0246.3138.67
98309None36.718003126.926841화천1리마을회관outside47.4160.015.55555641.01023.32...NoneNoneNoneFalse46.7748.1047.7948.1247.7935.08
\n", "

22479 rows × 43 columns

\n", "
" ], "text/plain": [ " parent lat lon name \\\n", "id \n", "14633 None 37.275561 -121.964134 Hazelwood canary \n", "25999 None 30.053808 -95.494643 Villages of Bridgestone AQI \n", "14091 None 37.883620 -122.070087 WC Hillside \n", "108226 None 38.573703 -121.439113 \"C\" Street Air Shelter \n", "49409 None 18.759182 99.017172 \"First's Place\" \n", "... ... ... ... ... \n", "64085 None 36.785883 127.157040 청룡동행정복지센터 \n", "64995 None 36.691324 126.585255 한서대학교 \n", "64093 None 36.710720 126.548390 해미읍성 \n", "29747 None 36.761236 127.395300 화덕보건진료소 \n", "98309 None 36.718003 126.926841 화천1리마을회관 \n", "\n", " location_type pm_2.5 temp_f temp_c humidity pressure ... \\\n", "id ... \n", "14633 outside 0.81 65.0 18.333333 54.0 1008.57 ... \n", "25999 outside 35.16 70.0 21.111111 70.0 1011.86 ... \n", "14091 outside 1.00 63.0 17.222222 57.0 1003.26 ... \n", "108226 inside 4.76 78.0 25.555556 45.0 1015.66 ... \n", "49409 outside 40.83 87.0 30.555556 37.0 986.64 ... \n", "... ... ... ... ... ... ... ... \n", "64085 outside 40.35 54.0 12.222222 46.0 1027.31 ... \n", "64995 outside 40.64 63.0 17.222222 33.0 1017.02 ... \n", "64093 outside 62.07 61.0 16.111111 44.0 1027.89 ... \n", "29747 outside 34.77 62.0 16.666667 36.0 1018.16 ... \n", "98309 outside 47.41 60.0 15.555556 41.0 1023.32 ... \n", "\n", " last_update_check created uptime is_owner 10min_avg 30min_avg \\\n", "id \n", "14633 None None None False 0.88 4.10 \n", "25999 None None None False 34.71 33.59 \n", "14091 None None None False 2.28 2.72 \n", "108226 None None None False 4.77 4.49 \n", "49409 None None None False 45.70 49.98 \n", "... ... ... ... ... ... ... \n", "64085 None None None False 37.24 38.78 \n", "64995 None None None False 45.03 45.97 \n", "64093 None None None False 57.93 56.06 \n", "29747 None None None False 40.36 49.29 \n", "98309 None None None False 46.77 48.10 \n", "\n", " 1hour_avg 6hour_avg 1day_avg 1week_avg \n", "id \n", "14633 8.26 17.72 21.80 15.42 \n", "25999 32.77 26.03 16.30 14.88 \n", "14091 3.16 16.25 25.80 22.91 \n", "108226 4.15 3.96 4.75 5.46 \n", "49409 50.08 50.61 46.32 32.53 \n", "... ... ... ... ... \n", "64085 41.66 57.59 63.40 45.20 \n", "64995 43.96 39.68 37.22 28.38 \n", "64093 52.51 43.19 40.98 31.83 \n", "29747 52.26 49.02 46.31 38.67 \n", "98309 47.79 48.12 47.79 35.08 \n", "\n", "[22479 rows x 43 columns]" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Displaying dataframe with all the PurpleAir Sensor data\n", "df" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here is a breakdown of the dataframe above and what each column represents. \n", "\n", "\n", "|Column Name | Description |\n", "|--------------|---------|\n", "|lat |The latitude coordinate of the location |\n", "|lon | The longitude coordinate of the location |\n", "|name | The name of the location|\n", "|location_type | The nature of the location (ie. inside or outside) |\n", "|pm_2.5 | The level of fine particulate matter in the air of that location |\n", "|temp_f | The temperature of the location in degrees Farenheit|\n", "|temp_c | The temperature of the location in degrees Celsius|\n", "|humidity | The humidity percentage of the location|\n", "|pressure | The pressure index of the location (in millibars)|\n", "|last_seen | The last seen date and timestamp in UTC |\n", "|model | Model of the specific sensor |\n", "|flagged | Whether or not the channel was marked as flagged (usually based on a fault)|\n", "|age | Sensor data age (when data was last received) |\n", "|10min_avg | Average PM 2.5 AQI over the last 10 minutes |\n", "|30min_avg | Average PM 2.5 AQI over the last 30 minutes |\n", "|1hour_avg | Average PM 2.5 AQI over the last hour|\n", "|6hour_avg | Average PM 2.5 AQI over the last 6 hours|\n", "|1day_avg | Average PM 2.5 AQI over the last day |\n", "|1week_avg | Average PM 2.5 AQI over the last week|" ] }, { "cell_type": "markdown", "metadata": { "cell_id": "00011-c8766e48-014a-44d5-8139-e880413d49f9", "deepnote_cell_type": "markdown", "tags": [] }, "source": [ "
\n", "\n", "### Airborne Particulate Matter (PM) 2.5 \n", "While many of the column names are relatively straightforward, such as the \"name\" column (which displays the set name of the particular sensor), the \"location_type\" column (which indicates whether it is an indoor or outdoor sensor), etc., we would like to draw your attention to the \"pm_2.5\" column. \n", "\n", ">The \"pm_2.5\" column represents the count of airborne pm that is larger than 2.5um/dl, in otherwords, airborne particles that have a diameter of 2.5 micrometers or less. In high levels, PM 2.5 particles can reduce visibility and cause the air to appear hazy. Tracking PM 2.5 is important because prolonged exposure to high levels of PM 2.5 particles can cause adverse US Environmental Protection Agency (EPA) use to calculate the local Air Quality Index (AQI)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**QUESTION: Which item or object is closest to 1 micrometer?**\n", "\n", "a) The length of an ant\n", "\n", "b) The diameter of a spider web\n", "\n", "c) The length of a grain of rice" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**ANSWER**\n", "\n", "a) The length of an ant is typically 1 millimeter, which is 1,000 micrometers\n", "\n", "**b) The diameter of a spider web is typically between 8 to 10 micrometers**\n", "\n", "c) The length of a grain of rice is typically 6 millimeters which is 6,000 micrometers." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you go to the PurpleAir website [here](https://map.purpleair.com/1/mAQI/a10/p604800/cC0#14.67/37.87206/-122.26187), it should navigate you to a map of the surrounding Berkeley area. If you click on the some of the sensored located on UC Berkeley campus, you'll find that one of them is named \"Le Conte Hall\". \n", "\n", "Let's take a closer look at the Le Conte Hall Sensor! In the dataframe below we filter the dataframe by the sensor name (\"Le Conte Hall\") to pick out the row that corresponds to the specific sensor we are looking for. " ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
parentlatlonnamelocation_typepm_2.5temp_ftemp_chumiditypressure...last_update_checkcreateduptimeis_owner10min_avg30min_avg1hour_avg6hour_avg1day_avg1week_avg
id
77905None37.872589-122.257219Le Conte Hallinside0.3578.025.55555632.01004.55...NoneNoneNoneFalse0.280.180.34.6711.989.49
\n", "

1 rows × 43 columns

\n", "
" ], "text/plain": [ " parent lat lon name location_type pm_2.5 \\\n", "id \n", "77905 None 37.872589 -122.257219 Le Conte Hall inside 0.35 \n", "\n", " temp_f temp_c humidity pressure ... last_update_check created \\\n", "id ... \n", "77905 78.0 25.555556 32.0 1004.55 ... None None \n", "\n", " uptime is_owner 10min_avg 30min_avg 1hour_avg 6hour_avg 1day_avg \\\n", "id \n", "77905 None False 0.28 0.18 0.3 4.67 11.98 \n", "\n", " 1week_avg \n", "id \n", "77905 9.49 \n", "\n", "[1 rows x 43 columns]" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df[df['name'] == \"Le Conte Hall\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "The row above gives us loads of information on the state of the AQI in Le Conte Hall at the present moment, but it would be nice to see the AQI information over time. Below is a dataframe that contains information about the Le Conte Hall sensor roughly over the last 7 days. We can do this by filtering the times each entry was created at." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
created_at0.3um/dl0.5um/dl1.0um/dl2.5um/dl5.0um/dl10.0um/dlPM1.0 (CF=ATM) ug/m3PM10 (CF=ATM) ug/m3Date
entry_id
2952902021-12-01 00:00:48+00:001496.78369.3443.393.250.940.656.379.4001-Dec-2021
2952912021-12-01 00:02:48+00:001557.96405.9551.844.380.720.437.2010.4401-Dec-2021
2952922021-12-01 00:04:48+00:001430.91364.1651.113.240.480.436.719.7401-Dec-2021
2952932021-12-01 00:06:48+00:001539.02386.5160.633.500.820.436.9610.4501-Dec-2021
2952942021-12-01 00:08:48+00:001392.07347.6737.491.350.460.226.328.1201-Dec-2021
.................................
3003242021-12-07 23:50:34+00:00964.21210.0623.831.350.220.003.654.7807-Dec-2021
3003252021-12-07 23:52:34+00:00966.82214.6425.290.370.000.003.434.5007-Dec-2021
3003262021-12-07 23:54:35+00:00945.52202.4015.530.220.000.002.883.7107-Dec-2021
3003272021-12-07 23:56:34+00:00943.25209.6720.720.960.210.213.444.5607-Dec-2021
3003282021-12-07 23:58:34+00:00987.24222.5331.911.320.220.223.895.3907-Dec-2021
\n", "

5039 rows × 10 columns

\n", "
" ], "text/plain": [ " created_at 0.3um/dl 0.5um/dl 1.0um/dl 2.5um/dl \\\n", "entry_id \n", "295290 2021-12-01 00:00:48+00:00 1496.78 369.34 43.39 3.25 \n", "295291 2021-12-01 00:02:48+00:00 1557.96 405.95 51.84 4.38 \n", "295292 2021-12-01 00:04:48+00:00 1430.91 364.16 51.11 3.24 \n", "295293 2021-12-01 00:06:48+00:00 1539.02 386.51 60.63 3.50 \n", "295294 2021-12-01 00:08:48+00:00 1392.07 347.67 37.49 1.35 \n", "... ... ... ... ... ... \n", "300324 2021-12-07 23:50:34+00:00 964.21 210.06 23.83 1.35 \n", "300325 2021-12-07 23:52:34+00:00 966.82 214.64 25.29 0.37 \n", "300326 2021-12-07 23:54:35+00:00 945.52 202.40 15.53 0.22 \n", "300327 2021-12-07 23:56:34+00:00 943.25 209.67 20.72 0.96 \n", "300328 2021-12-07 23:58:34+00:00 987.24 222.53 31.91 1.32 \n", "\n", " 5.0um/dl 10.0um/dl PM1.0 (CF=ATM) ug/m3 PM10 (CF=ATM) ug/m3 \\\n", "entry_id \n", "295290 0.94 0.65 6.37 9.40 \n", "295291 0.72 0.43 7.20 10.44 \n", "295292 0.48 0.43 6.71 9.74 \n", "295293 0.82 0.43 6.96 10.45 \n", "295294 0.46 0.22 6.32 8.12 \n", "... ... ... ... ... \n", "300324 0.22 0.00 3.65 4.78 \n", "300325 0.00 0.00 3.43 4.50 \n", "300326 0.00 0.00 2.88 3.71 \n", "300327 0.21 0.21 3.44 4.56 \n", "300328 0.22 0.22 3.89 5.39 \n", "\n", " Date \n", "entry_id \n", "295290 01-Dec-2021 \n", "295291 01-Dec-2021 \n", "295292 01-Dec-2021 \n", "295293 01-Dec-2021 \n", "295294 01-Dec-2021 \n", "... ... \n", "300324 07-Dec-2021 \n", "300325 07-Dec-2021 \n", "300326 07-Dec-2021 \n", "300327 07-Dec-2021 \n", "300328 07-Dec-2021 \n", "\n", "[5039 rows x 10 columns]" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "## data from Le Conte Hall sensor from the past week\n", "from purpleair.sensor import Sensor\n", "se = Sensor(77905)\n", "le_conte = se.parent.get_historical(weeks_to_get=1,thingspeak_field='secondary')\n", "le_conte['Date'] = [i.date().strftime(\"%d-%b-%Y\") for i in le_conte['created_at']]\n", "le_conte" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see from the \"created_at\" column, the AQI was taken every two minutes over the past 7 days. The data frame also contains information on PM paticules of different diameters such as 0.3, 0.5, 1.0, 2.5, 5.0 and 10.0.\n", "\n", "
\n", "\n", "While this dataframe is useful, there are too many rows of data (~5000) to look at! Below is a widget that plots a line graph of the PM 2.5 measure over a specific day. \n", "\n", "**The drop down bar allows you to pick which day you would like graphed, so go ahead and pick a day!**" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "d7d586a1a63b49e992ee064bdc80f579", "version_major": 2, "version_minor": 0 }, "text/plain": [ "interactive(children=(Dropdown(description='date', options=('01-Dec-2021', '02-Dec-2021', '03-Dec-2021', '04-D…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "def f(date):\n", " fig = plt.figure(figsize=(13,3))\n", " plt.plot(le_conte['created_at'].loc[le_conte['Date'] == date], le_conte[\"2.5um/dl\"].loc[le_conte['Date'] == date])\n", " plt.xlabel('Time')\n", " plt.ylabel('PM 2.5 Particle Count')\n", " plt.title('Le Conte Hall Sensor PM 2.5')\n", " plt.rcParams[\"figure.figsize\"] = (20,3)\n", " \n", "interact(f, date = list(le_conte['Date'].unique()));" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The line plots above displays the date and hour along the x-axis and the PM 2.5 Particle count along the y-axis.\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**QUESTION: What is the highest index reading on the first time series plot?**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Your answer here*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "**QUESTION: What trends do you notice about the line plot?**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Your answer here*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "**QUESTION: Why do you think the index readings fluctuate from point to point?**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Your answer here*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "While the line plots do show us a trend in the PM2.5 count over time, we still have not clue how that translates to the API Index. The next section will discuss what AQI is and how it is calculated." ] }, { "cell_type": "markdown", "metadata": { "cell_id": "00005-53b10aac-3e5e-4217-bcb3-6690c972591c", "deepnote_cell_type": "markdown", "tags": [] }, "source": [ "### API Index\n", "The API Index contains 6 categories that air quality can fall into. Each category contains a range of index values from 0 - 500 that is calculated from the regions PM 2.5 measure. The chart below is provided by the US Environmental Protection Agency (EPA) and shows the official AQI Index (these breakpoints were revised in 2012). \n", "\n", "For more information on how AQI Index is calculated, take a look at the AQI Index Factsheet provided by the EPA [here](https://www.epa.gov/sites/default/files/2016-04/documents/2012_aqi_factsheet.pdf)!" ] }, { "cell_type": "markdown", "metadata": { "cell_id": "00014-fee8340f-6600-4ded-ad5b-e4a94875ab2b", "deepnote_cell_type": "markdown", "tags": [] }, "source": [ "

\n", " \n", "

" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "**QUESTION: What is the difference between the original and revised breakpoints?**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Your answer here*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "**QUESTION: At 3:00 on November 30th, 2021 the PM 2.5 reading is 12.5. What category does it fall into?**\n", "\n", "a) Good\n", "\n", "b) Moderate\n", "\n", "c) Unhealthy for Sensitive Groups" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**ANSWER: The category is Moderate because it falls into the 12.1 - 35.4 range.**" ] }, { "cell_type": "markdown", "metadata": { "cell_id": "00021-ef970a60-ed6d-47ac-a766-91488428efe1", "deepnote_cell_type": "markdown", "tags": [] }, "source": [ "Now that we know how sesors work, what they measure and how AQI Indexes are calculated, let's see if we can create a visualization of AQI Indexes that are a little closer to home!\n", "\n", "First, let's find a group of sensors that are near UC Berkeley. The code cell below does just that. We use a range of longitude and latitude coordinates to decide whether to include or exclude a sensor. " ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "cell_id": "00007-d6fc7d9e-a424-41a8-970c-0cefe2e863e8", "deepnote_cell_type": "code", "deepnote_to_be_reexecuted": false, "execution_millis": 80, "execution_start": 1639017445924, "source_hash": "b079f629", "tags": [] }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
latlonnamelocation_typepm_2.5temp_fhumiditypressure
id
2074737.838977-122.2054891000ft Montclairoutside1.1656.078.0979.92
8167737.889085-122.2643271044 Keith Ave, Berkeleyoutside3.6759.064.0995.76
7912537.882941-122.2880171094 Tevlin Stinside0.3575.040.01013.70
7768537.801872-122.27458210th and Washingtoninside31.8377.037.01014.57
3797137.883729-122.2903621128 Key Route Blvd, Albany CAoutside2.0460.061.01014.15
...........................
2697737.813427-122.282483Xanaduinside0.0084.032.01015.51
5628137.813500-122.282971Xanaduoutside0.8958.067.01015.93
6261937.886177-122.272443Yoloinside0.0090.022.01006.36
7522337.827560-122.205627Zinn Driveoutside2.0256.068.0986.10
12320137.869390-122.245792zzinside0.0775.033.0988.19
\n", "

817 rows × 8 columns

\n", "
" ], "text/plain": [ " lat lon name location_type \\\n", "id \n", "20747 37.838977 -122.205489 1000ft Montclair outside \n", "81677 37.889085 -122.264327 1044 Keith Ave, Berkeley outside \n", "79125 37.882941 -122.288017 1094 Tevlin St inside \n", "77685 37.801872 -122.274582 10th and Washington inside \n", "37971 37.883729 -122.290362 1128 Key Route Blvd, Albany CA outside \n", "... ... ... ... ... \n", "26977 37.813427 -122.282483 Xanadu inside \n", "56281 37.813500 -122.282971 Xanadu outside \n", "62619 37.886177 -122.272443 Yolo inside \n", "75223 37.827560 -122.205627 Zinn Drive outside \n", "123201 37.869390 -122.245792 zz inside \n", "\n", " pm_2.5 temp_f humidity pressure \n", "id \n", "20747 1.16 56.0 78.0 979.92 \n", "81677 3.67 59.0 64.0 995.76 \n", "79125 0.35 75.0 40.0 1013.70 \n", "77685 31.83 77.0 37.0 1014.57 \n", "37971 2.04 60.0 61.0 1014.15 \n", "... ... ... ... ... \n", "26977 0.00 84.0 32.0 1015.51 \n", "56281 0.89 58.0 67.0 1015.93 \n", "62619 0.00 90.0 22.0 1006.36 \n", "75223 2.02 56.0 68.0 986.10 \n", "123201 0.07 75.0 33.0 988.19 \n", "\n", "[817 rows x 8 columns]" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "## UC Berkeley,CA - Lat: 37.871666 / Lon: -122.272781\n", "\n", "berkeleyData = df.loc[(df[\"lat\"] >= 37.8) & (df[\"lat\"] <= 37.9) & (df[\"lon\"] >= -122.3) & (df[\"lon\"] <= -122.2)]\n", "berkeleyData = berkeleyData[[\"lat\", \"lon\", \"name\", \"location_type\", \"pm_2.5\", \"temp_f\", \"humidity\", \"pressure\"]]\n", "berkeleyData" ] }, { "cell_type": "markdown", "metadata": { "cell_id": "00022-1b077f57-02a3-4169-8666-8efb05b6494f", "deepnote_cell_type": "markdown", "tags": [] }, "source": [ "Now that we have a smaller subset of data to work with, the next step is to use the PM 2.5 measures to assign each sensor to an AQI Index Category and corresponding color. " ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "cell_id": "00023-9e7df17a-8886-4a93-a868-1958b7034700", "deepnote_cell_type": "code", "deepnote_to_be_reexecuted": false, "execution_millis": 4, "execution_start": 1639017449838, "source_hash": "44ccc048", "tags": [] }, "outputs": [], "source": [ "#creating a column that indicates the AQI code name\n", "color_code = []\n", "for i in berkeleyData[\"pm_2.5\"].to_list():\n", " if i <= 12.0:\n", " color_code.append('green')\n", " elif (i < 12) & (i <=35.4):\n", " color_code.append('yellow')\n", " elif (i < 35.5) & (i <=55.4):\n", " color_code.append('orange') \n", " elif (i < 55.5) & (i <=150.4):\n", " color_code.append('red')\n", " elif (i < 150.5) & (i <=250.4):\n", " color_code.append('purple')\n", " else:\n", " color_code.append('darkpurple')\n", "\n", "\n", "berkeleyData['code'] = color_code" ] }, { "cell_type": "markdown", "metadata": { "cell_id": "00024-f6a8c9ff-3808-4bc8-bea0-e6bd7bac0551", "deepnote_cell_type": "markdown", "tags": [] }, "source": [ "
\n", "\n", "Our last step is to use the longitude and latitude coordinates to map the relative location of the sensor with is corresponding AQI Index color! The widget below contains two sliders. One represents the Latitude value and the other is the Longitude value. \n", "\n", "**Slide the sliders left and right to display a mapping of the sensors in that latitude and longitude region, or use your cursor to drag the mapping area.**\n", "\n", "**Hint: Berkeley, CA - Lat: 37.871666 / Lon: -122.272781** " ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "cell_id": "00007-881b9fa0-5a2d-4db9-bab9-0425aa2abcd1", "deepnote_cell_type": "code", "deepnote_to_be_reexecuted": false, "execution_millis": 599, "execution_start": 1639017450813, "source_hash": "723c9c1e", "tags": [] }, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "b386343ab00443c28032c51571da83be", "version_major": 2, "version_minor": 0 }, "text/plain": [ "interactive(children=(FloatSlider(value=37.0, description='Latitude', max=38.0, min=36.0, step=0.001), FloatSl…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "def map(Latitude ,Longitude):\n", " m = folium.Map(width=500, height=400, location=[Latitude, Longitude])\n", " \n", " for i in np.arange(len(berkeleyData) - 1):\n", " folium.Marker(\n", " location=[berkeleyData.iloc[i]['lat'], berkeleyData.iloc[i]['lon']],\n", " popup=berkeleyData.iloc[i]['name'],\n", " icon=folium.Icon(color=berkeleyData.iloc[i]['code']),\n", " ).add_to(m)\n", " display(m)\n", " \n", "interact(map, Latitude = (36, 38, 0.001) , Longitude = (-123, -121, 0.001));\n", "## UC Berkeley,CA - Lat: 37.871666 / Lon: -122.272781" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that we have created a map we can easily see what the AQI index is across the city! " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "**QUESTION: What do you notice about the map?**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Your answer here*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "Developed By: Melisa Esqueda, Maham Bawaney & Karalyn Chong" ] } ], "metadata": { "deepnote": { "is_reactive": false }, "deepnote_execution_queue": [], "deepnote_notebook_id": "d9e66c1e-6ca7-430c-be17-2f7b5174ecd9", "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.8.8" } }, "nbformat": 4, "nbformat_minor": 2 }