In [1]:
pip install windrose
Requirement already satisfied: windrose in /opt/conda/lib/python3.11/site-packages (1.9.2)
Requirement already satisfied: matplotlib>=3 in /opt/conda/lib/python3.11/site-packages (from windrose) (3.10.0)
Requirement already satisfied: numpy>=1.21 in /opt/conda/lib/python3.11/site-packages (from windrose) (1.26.4)
Requirement already satisfied: contourpy>=1.0.1 in /opt/conda/lib/python3.11/site-packages (from matplotlib>=3->windrose) (1.3.1)
Requirement already satisfied: cycler>=0.10 in /opt/conda/lib/python3.11/site-packages (from matplotlib>=3->windrose) (0.12.1)
Requirement already satisfied: fonttools>=4.22.0 in /opt/conda/lib/python3.11/site-packages (from matplotlib>=3->windrose) (4.55.3)
Requirement already satisfied: kiwisolver>=1.3.1 in /opt/conda/lib/python3.11/site-packages (from matplotlib>=3->windrose) (1.4.8)
Requirement already satisfied: packaging>=20.0 in /opt/conda/lib/python3.11/site-packages (from matplotlib>=3->windrose) (23.2)
Requirement already satisfied: pillow>=8 in /opt/conda/lib/python3.11/site-packages (from matplotlib>=3->windrose) (11.1.0)
Requirement already satisfied: pyparsing>=2.3.1 in /opt/conda/lib/python3.11/site-packages (from matplotlib>=3->windrose) (3.2.1)
Requirement already satisfied: python-dateutil>=2.7 in /opt/conda/lib/python3.11/site-packages (from matplotlib>=3->windrose) (2.8.2)
Requirement already satisfied: six>=1.5 in /opt/conda/lib/python3.11/site-packages (from python-dateutil>=2.7->matplotlib>=3->windrose) (1.16.0)
Note: you may need to restart the kernel to use updated packages.
In [2]:
import pandas as pd
import matplotlib.pyplot as plt
import bqplot as bq
import ipywidgets as widgets
import numpy as np
import requests
import os
from windrose import WindroseAxes

# Creating buttons for downloading and refreshing plots
download_button = widgets.Button(description="Download Dataset")
refresh_button = widgets.Button(description="Refresh Plots")
In [ ]:
 
In [3]:
# Working on requesting and saving data from APi

def fetch_and_save_data(_):
    api_url = "https://api.open-meteo.com/v1/forecast?latitude=41.88&longitude=-87.63&hourly=temperature_2m,relative_humidity_2m,precipitation,cloudcover,windspeed_10m&timezone=auto"
    
    try:
        response = requests.get(api_url)
        response.raise_for_status()
        data = response.json()  # we need to Parse JSON
        
        # and then transform to csv
        hourly = data['hourly']
        df = pd.DataFrame(hourly)
        df.to_csv("chicago_weather_data.csv", index=False)
        print("Weather data Refreshed and downloaded")
        
    except Exception as e:
        pass
    
def refresh_plots(_):
    try:
        # Read from the saved CSV file
        df_raw = pd.read_csv("chicago_weather_data.csv")
        
        # Convert to proper types
        df_raw['time'] = pd.to_datetime(df_raw['time'], errors='coerce')
        for col in ['temperature_2m', 'relative_humidity_2m', 'precipitation', 'cloudcover', 'windspeed_10m']:
            df_raw[col] = pd.to_numeric(df_raw[col], errors='coerce')
        
        # Assign to global variable so that we can use it else where in the notebook
        global df
        df = df_raw

        print("Plots refreshed")
        
    except Exception as e:
        pass

    
In [4]:
#Let us download and refresh the data before starting the plots.
fetch_and_save_data(None)
refresh_plots(None)
df.head()
Weather data Refreshed and downloaded
Plots refreshed
Out[4]:
time temperature_2m relative_humidity_2m precipitation cloudcover windspeed_10m
0 2025-05-05 00:00:00 7.7 85 0.0 100 21.6
1 2025-05-05 01:00:00 7.7 88 0.4 100 21.6
2 2025-05-05 02:00:00 7.8 87 0.0 100 29.1
3 2025-05-05 03:00:00 8.0 87 0.0 100 19.5
4 2025-05-05 04:00:00 8.1 92 0.0 100 21.8
In [5]:
#This can be also done by clicking the buttons below.
print('The data and visuals has already been refreshed, but adding buttons for clarity')
download_button.on_click(fetch_and_save_data)
refresh_button.on_click(refresh_plots)
widgets.HBox([download_button, refresh_button])
The data and visuals has already been refreshed, but adding buttons for clarity
Out[5]:
HBox(children=(Button(description='Download Dataset', style=ButtonStyle()), Button(description='Refresh Plots'…
In [6]:
#Plot3


variable_labels = {
    'temperature_2m': 'Temperature',
    'relative_humidity_2m': 'Humidity',
    'precipitation': 'Precipitation',
    'cloudcover': 'Cloud Cover'
}

dropdown = widgets.Dropdown(
    options=[(label, key) for key, label in variable_labels.items()],
    value='temperature_2m',
    description='Select Variable:',
    style={'description_width': 'initial'}
)

x_sc = bq.DateScale()
y_sc = bq.LinearScale()

line = bq.Lines(
    x=df['time'],
    y=df['temperature_2m'],
    scales={'x': x_sc, 'y': y_sc},
    display_legend=False,
    stroke_width=2
)


x_ax = bq.Axis(label='Time', scale=x_sc)
y_ax = bq.Axis(label='Value', scale=y_sc, orientation='vertical')


fig = bq.Figure(
    title='Chicago Temperature Over Time',
    marks=[line],
    axes=[x_ax, y_ax],
    legend_location='top-left'
)


def update_plot(change):
    var = dropdown.value
    line.y = df[var]
    line.x = df['time']
    fig.title = f"{variable_labels[var]} Over Time"

dropdown.observe(update_plot, names='value')

# Show widgets
widgets.VBox([dropdown, fig])
Out[6]:
VBox(children=(Dropdown(description='Select Variable:', options=(('Temperature', 'temperature_2m'), ('Humidity…

This interactive line chart displays weather trends in Chicago over time. Users can dynamically switch between variables—Temperature, Humidity, Precipitation, and Cloud Cover—using the dropdown menu. The x-axis represents time, while the y-axis shows the corresponding value of the selected variable.

This tool allows for quick comparison of different weather factors and can help identify short-term trends and patterns in local atmospheric conditions

In [7]:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# Assuming your DataFrame is named 'df'
fig = plt.figure(figsize=(12, 6))
ax = fig.add_subplot(111, projection='3d')

# Color-coding by temperature
scatter = ax.scatter(df['temperature_2m'], df['precipitation'], df['windspeed_10m'],
                    c=df['temperature_2m'], cmap='coolwarm', s=20, marker='o', alpha=0.7, edgecolors='black')

ax.set_xlabel('Temperature (°C)')
ax.set_ylabel('Precipitation')
ax.set_zlabel('Windspeed (km/h)')
plt.title('3D Scatterplot of Temperature, Precipitation, and Windspeed')

# Add a colorbar
cbar = plt.colorbar(scatter, ax=ax, label='Temperature (°C)')

plt.show()
No description has been provided for this image

The 3D scatterplot provides a multidimensional view of Chicago's hourly weather patterns, showing the relationship between Temperature (°C), Precipitation (mm), and Windspeed (km/h). The color gradient adds an extra layer of insight by mapping temperature values to a blue-to-red scale.

🟦 Key Observations: Temperature vs Precipitation (X-Y Plane):

Higher precipitation values (> 0.2 mm) are observed mostly when temperatures are moderate (8–14°C).

Drier conditions are associated with warmer temperatures (18–22°C), indicating possible clear sky days.

Temperature vs Windspeed (X-Z Plane):

Most high wind speeds (>20 km/h) occur during cooler temperatures (<12°C).

Low wind conditions are more frequent during warmer periods, suggesting calmer, stable air masses in warmer weather.

Precipitation vs Windspeed (Y-Z Plane):

As precipitation increases, wind speed generally decreases.

Suggests a likely presence of light rain or drizzle conditions rather than thunderstorms or severe weather events.

🌡️ Color Coding Insight: Points shaded red represent higher temperatures, typically aligned with low wind and dry conditions.

Points shaded blue represent colder readings and correlate with greater wind activity and varied precipitation, consistent with frontal weather systems or early spring/fall patterns.

In [8]:
def fetch_and_save_data1(_):
    api_url = "https://api.open-meteo.com/v1/forecast?latitude=41.88&longitude=-87.63&hourly=temperature_2m,relative_humidity_2m,precipitation,cloudcover,windspeed_10m,winddirection_10m&timezone=auto"  # Updated API URL

    try:
        response = requests.get(api_url)
        response.raise_for_status()
        data = response.json()  # we need to Parse JSON

        # and then transform to csv
        hourly = data['hourly']
        df1 = pd.DataFrame(hourly)
        df1.to_csv("chicago_weather_data1.csv", index=False)

        #Return the dataframe
        return df1 # Return the dataframe



    except Exception as e:
        pass

from windrose import WindroseAxes
import matplotlib.pyplot as plt

# Assuming your DataFrame is named 'df' and has 'windspeed_10m' and 'winddirection_10m' columns
# Fetch the data and assign it to df1
df1 = fetch_and_save_data1(None)

# Use df1 for plotting
ax = WindroseAxes.from_ax()
ax.bar(df1['winddirection_10m'], df1['windspeed_10m'], normed=True, opening=0.8, edgecolor='white') # Use df1 instead of df
ax.set_legend()
plt.show()
No description has been provided for this image

Wind Direction Insights: The dominant wind direction is from the North (N) and North-Northeast (N-NE).

The N direction accounts for the highest frequency of winds, contributing over 25% of all wind events recorded.

The N-NE sector follows closely, confirming a strong north-to-southeast air mass flow, typical for spring frontal systems or lake-influenced weather near Lake Michigan.

Winds from southerly and easterly directions are rare, suggesting low influence from southern warm fronts or tropical air masses during this period.

🌬️ Wind Speed Breakdown: Wind speeds are categorized into six bins:

Most common winds fall in the 7.8 – 18.5 km/h range (blue to green).

Higher wind speeds >23.8 km/h are rare and mostly associated with NE wind events, likely representing strong cold fronts or stormy weather.

A single yellow sliver in the N-NE sector shows winds exceeding 29.1 km/h, which is notable — this likely corresponds to isolated high-intensity gusts or storms.

📌 Summary: Prevailing winds are from the North to N-NE, consistent with regional climatology and Great Lakes influence.

Wind strength is mostly moderate, with occasional gusty conditions, especially from the NE quadrant.

This pattern is typical of early spring or late fall in Chicago when polar air masses dominate and pressure gradients create sustained northerly flows.

In [9]:
df.head()
Out[9]:
time temperature_2m relative_humidity_2m precipitation cloudcover windspeed_10m
0 2025-05-05 00:00:00 7.7 85 0.0 100 21.6
1 2025-05-05 01:00:00 7.7 88 0.4 100 21.6
2 2025-05-05 02:00:00 7.8 87 0.0 100 29.1
3 2025-05-05 03:00:00 8.0 87 0.0 100 19.5
4 2025-05-05 04:00:00 8.1 92 0.0 100 21.8
In [10]:
#Making sure the the format is correct. I might have done it before but I like to make sure of it.
df['time'] = pd.to_datetime(df['time'], errors='coerce')
df['temperature_2m'] = pd.to_numeric(df['temperature_2m'], errors='coerce')

# we will extract date only and then group by 
df['date_only'] = df['time'].dt.date

grouped = df.groupby('date_only')['temperature_2m'].agg(['min', 'mean', 'max']).reset_index()

slider = widgets.IntSlider(
    value=0,
    min=0,
    max=len(grouped) - 1,
    step=1,
    description='Day Index:',
    continuous_update=False
)

x_scale = bq.OrdinalScale()
y_scale = bq.LinearScale()

bar = bq.Bars(
    x=['Min', 'Mean', 'Max'],
    y=grouped.iloc[slider.value, 1:].tolist(),
    scales={'x': x_scale, 'y': y_scale},
    colors=['#76b5c5'],
)

x_axis = bq.Axis(scale=x_scale, label='Temperature Summary')
y_axis = bq.Axis(scale=y_scale, orientation='vertical', label='Temperature (°C)')

fig = bq.Figure(
    marks=[bar],
    axes=[x_axis, y_axis],
    title=f"Temperature Summary for {grouped['date_only'].iloc[slider.value]}"
)

def update_bar(change):
    i = slider.value
    bar.y = grouped.iloc[i, 1:].tolist()
    fig.title = f"Temperature Summary for {grouped['date_only'].iloc[i]}"

slider.observe(update_bar, names='value')

widgets.VBox([slider, fig])
Out[10]:
VBox(children=(IntSlider(value=0, continuous_update=False, description='Day Index:', max=6), Figure(axes=[Axis…

This interactive bar chart presents a temperature summary for a selected day based on hourly weather forecast data. It displays three key metrics for that day:

Minimum temperature Average (mean) temperature Maximum temperature All values are shown in degrees Celsius (°C), providing a concise yet informative snapshot of how temperatures fluctuate throughout the day.

The user can adjust the day using the slider above the chart. As the slider value changes, the chart dynamically updates to reflect the corresponding temperature statistics for the newly selected day. This allows for quick comparisons across different dates and helps identify patterns such as warming or cooling trends over the forecast period.

This visualization is particularly useful for identifying temperature ranges and daily variability, making it ideal for planning activities or analyzing weather patterns in a user-friendly format.

In [ ]: