{ "cells": [ { "cell_type": "markdown", "id": "0b211ca8-bd70-46fc-b7b1-7c7e6bdcfe5e", "metadata": {}, "source": [ "# Wind Rose\n", "\n", "The notebook is inspired by [this plot](https://mesonet.cdn.columbiascanner.org/onsite/windrose/IA_ASOS/PEA/PEA_yearly.png)." ] }, { "cell_type": "code", "execution_count": 1, "id": "a5ae369f-2486-4e7c-81c4-79ed555154d5", "metadata": { "execution": { "iopub.execute_input": "2024-04-26T12:21:00.879537Z", "iopub.status.busy": "2024-04-26T12:21:00.879537Z", "iopub.status.idle": "2024-04-26T12:21:01.856236Z", "shell.execute_reply": "2024-04-26T12:21:01.856236Z" } }, "outputs": [], "source": [ "import pandas as pd\n", "\n", "from lets_plot import *\n", "from lets_plot.mapping import as_discrete" ] }, { "cell_type": "code", "execution_count": 2, "id": "bfdcdd13-23ea-4a38-be8f-561192a555db", "metadata": { "execution": { "iopub.execute_input": "2024-04-26T12:21:01.856236Z", "iopub.status.busy": "2024-04-26T12:21:01.856236Z", "iopub.status.idle": "2024-04-26T12:21:01.872922Z", "shell.execute_reply": "2024-04-26T12:21:01.872013Z" } }, "outputs": [ { "data": { "text/html": [ "\n", "
\n", " \n", " " ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "LetsPlot.setup_html()" ] }, { "cell_type": "code", "execution_count": 3, "id": "ccdb0b79-81d3-4d64-a208-0cd76765df50", "metadata": { "execution": { "iopub.execute_input": "2024-04-26T12:21:01.874231Z", "iopub.status.busy": "2024-04-26T12:21:01.874231Z", "iopub.status.idle": "2024-04-26T12:21:06.874570Z", "shell.execute_reply": "2024-04-26T12:21:06.874570Z" } }, "outputs": [], "source": [ "wind_df = pd.read_csv(\"https://raw.githubusercontent.com/JetBrains/lets-plot-docs/master/data/pea_wind.csv\")\n", "records_count = wind_df.shape[0]" ] }, { "cell_type": "code", "execution_count": 4, "id": "76ab511d-673c-40ba-abc0-12b88db98f5e", "metadata": { "execution": { "iopub.execute_input": "2024-04-26T12:21:06.874570Z", "iopub.status.busy": "2024-04-26T12:21:06.874570Z", "iopub.status.idle": "2024-04-26T12:21:07.901771Z", "shell.execute_reply": "2024-04-26T12:21:07.901771Z" } }, "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", "
stationvaliddrctsped
0PEA2002-05-17 00:14330.08.05
1PEA2002-05-17 00:20330.08.05
2PEA2002-05-17 00:34340.010.35
3PEA2002-05-17 00:40340.010.35
4PEA2002-05-17 00:54340.014.95
\n", "
" ], "text/plain": [ " station valid drct sped\n", "0 PEA 2002-05-17 00:14 330.0 8.05\n", "1 PEA 2002-05-17 00:20 330.0 8.05\n", "2 PEA 2002-05-17 00:34 340.0 10.35\n", "3 PEA 2002-05-17 00:40 340.0 10.35\n", "4 PEA 2002-05-17 00:54 340.0 14.95" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def is_float(x):\n", " try:\n", " float(x)\n", " except ValueError:\n", " return False\n", " return True\n", "\n", "\n", "wind_df = wind_df[wind_df['sped'].apply(lambda x: is_float(x))]\n", "wind_df = wind_df[wind_df['drct'].apply(lambda x: is_float(x))]\n", "\n", "wind_df['sped'] = wind_df['sped'].astype(float)\n", "wind_df['drct'] = wind_df['drct'].astype(float)\n", "\n", "wind_df = wind_df[wind_df['sped'].apply(lambda x: x >= 2.0)]\n", "\n", "wind_df.head()" ] }, { "cell_type": "code", "execution_count": 5, "id": "a6731d90-ab72-466a-bd96-4f01f819bec5", "metadata": { "execution": { "iopub.execute_input": "2024-04-26T12:21:07.901771Z", "iopub.status.busy": "2024-04-26T12:21:07.901771Z", "iopub.status.idle": "2024-04-26T12:21:07.964496Z", "shell.execute_reply": "2024-04-26T12:21:07.964496Z" } }, "outputs": [], "source": [ "# Compute calm\n", "calm = 100 - wind_df.shape[0] / records_count * 100\n", "\n", "# Define the speed bins\n", "bins = [2, 5, 7, 10, 15, 20, float('inf')]\n", "bin_ids = list(range(6))\n", "\n", "wind_df['speed_group'] = pd.cut(wind_df['sped'], bins=bins, labels=bin_ids, right=False)\n", "\n", "# Group by 'drct' and 'speed_group', and count the occurrences\n", "grouped_counts = wind_df.groupby(['drct', 'speed_group'], observed=False).size().reset_index(name='count')\n", "\n", "# Calculate the total number of observations in the dataset\n", "total_observations = wind_df.shape[0]\n", "\n", "# Calculate the percentage of each speed group within each direction relative to the total number of observations\n", "grouped_counts['percentage_of_total'] = (grouped_counts['count'] / total_observations) * 100" ] }, { "cell_type": "code", "execution_count": 6, "id": "51befd5b-c99e-40a0-b968-f87d94644fab", "metadata": { "execution": { "iopub.execute_input": "2024-04-26T12:21:07.964496Z", "iopub.status.busy": "2024-04-26T12:21:07.964496Z", "iopub.status.idle": "2024-04-26T12:21:08.076095Z", "shell.execute_reply": "2024-04-26T12:21:08.075395Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", " " ], "text/plain": [ "" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ggplot(grouped_counts) + \\\n", " geom_bar(\n", " aes('drct', 'percentage_of_total', fill=as_discrete('speed_group', order=1)), \n", " size=0, width=.8,\n", " stat='identity',\n", " tooltips=layer_tooltips().format('^y', '{.2g}%').format('^x', '{}°')\n", " ) + \\\n", " geom_rect(\n", " # Visually align the width of the rectangle with the bars - widen it by 5 (half a bar width)\n", " xmin=5, xmax=365, \n", " ymin=-1, ymax=0, fill='white', size=0\n", " ) + \\\n", " geom_hline(yintercept=0, size=2) + \\\n", " geom_text(x=180, y=-1, label=f'Calm\\n{calm:.1f}%', hjust='middle', vjust='center', size='10') + \\\n", " scale_fill_manual(\n", " name='Wind Speed (mph):', \n", " values=['#002bff', '#03d3f8', '#7afe81', '#fde609', '#ff4404', '#780200'], \n", " labels={\n", " 0: '2 - 4.9', \n", " 1: '5 - 6.9', \n", " 2: '7 - 9.9', \n", " 3: '10 - 14.9', \n", " 4: '15 - 19.9', \n", " 5: '20+'\n", " },\n", " ) + \\\n", " scale_y_continuous(\n", " breaks=[0, 1, 2, 3, 4, 5], # To not add automatically generated ticks for values outside of the data range\n", " format='{}%'\n", " ) + \\\n", " scale_x_continuous(\n", " labels={\n", " 45: 'NE', \n", " 90: 'E', \n", " 135: 'SE', \n", " 180: 'S', \n", " 225: 'SW',\n", " 270: 'W',\n", " 315: 'NW',\n", " 360: 'N', \n", " },\n", " ) + \\\n", " labs(\n", " title=\"Wind rose for [PEA] PELLA\",\n", " subtitle=\"Observations from 2002 to 2024\",\n", " caption=\"Data is provided by the Iowa Environmental Mesonet of Iowa State University\"\n", " ) + \\\n", " ggsize(800, 800) + \\\n", " theme_minimal2() + \\\n", " theme(\n", " plot_title=element_text(size=24, face='bold'),\n", " plot_subtitle=element_text(size=18),\n", " panel_grid_minor_x=element_line(),\n", " panel_grid=element_line(color='#A0A0A0'),\n", " axis_ticks_y=element_line(),\n", " axis_text_x=element_text(size=18),\n", " axis_title=element_blank()\n", " ) + \\\n", " coord_polar(\n", " ylim=[-1, None], # -1 is to make inner circle\n", " start=(3.14 * 2) / 36 / 2 # Divide by 2 (i.e. rotate by half a bar width) to make the N-S axis perpendicular\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.10.13" } }, "nbformat": 4, "nbformat_minor": 5 }