diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/Dockerfile b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/Dockerfile new file mode 100644 index 000000000..2c2ac6cf4 --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/Dockerfile @@ -0,0 +1,32 @@ +FROM python:3.11-slim + +ENV DEBIAN_FRONTEND=noninteractive + +# Install system dependencies. +# Java JDK (not just JRE) is required by PyFlink because the pemja C extension +# needs JNI headers at compile time. gcc and build-essential are needed to +# compile the native extension. +RUN apt-get update && apt-get install -y \ + ca-certificates \ + default-jdk-headless \ + gcc \ + build-essential \ + && rm -rf /var/lib/apt/lists/* + +ENV JAVA_HOME=/usr/lib/jvm/default-java + +RUN mkdir -p /install +COPY requirements.txt /install/requirements.txt +RUN pip install --upgrade pip && \ + pip install --no-cache-dir jupyterlab jupyterlab_vim jupytext -r /install/requirements.txt + +COPY etc_sudoers /install/ +COPY etc_sudoers /etc/sudoers +COPY bashrc /root/.bashrc + +COPY version.sh /install/ +RUN /install/version.sh 2>&1 | tee /install/version.log + +EXPOSE 8888 + +CMD ["/bin/bash"] diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/README.md b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/README.md new file mode 100644 index 000000000..b931f3c5b --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/README.md @@ -0,0 +1,166 @@ +# Apache Flink Real-Time Weather Anomaly Detection + +**Course:** DATA605 Big Data Systems, Spring 2026 +**Author:** Rajesh Easwaramoorthy +**UMD ID:** 122242479 +**GitHub Issue:** [#459](https://github.com/gpsaggese/umd_classes/issues/459) + +--- + +## Overview + +This project builds a real-time streaming analytics application using **Apache Flink** (via PyFlink) that ingests live weather data from the OpenWeatherMap API and detects anomalies in temperature, humidity, and pressure readings. + +Anomaly detection uses two methods together: + +1. **City-specific rolling baseline** - flags sudden deviations relative to each city's recent history. +2. **Isolation Forest** (scikit-learn) - a global unsupervised model that identifies statistically rare readings across all cities. + +When an anomaly is detected the system logs it, writes a time-series point to InfluxDB, and sends an email alert. + +--- + +## Technology Stack + +| Component | Role | +|---|---| +| **Apache Flink / PyFlink** | Stream processing engine, DataStream pipeline | +| **OpenWeatherMap API** | Live weather data source (4 cities) | +| **Isolation Forest** | Unsupervised anomaly detection model | +| **InfluxDB** | Time-series data storage | +| **Grafana** | Real-time visualisation dashboard | +| **Docker / Docker Compose** | Containerised deployment | + +--- + +## Project Structure + +``` +UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/ +--- Dockerfile # Python 3.11 + Java JDK + PyFlink +--- docker_build.sh # Build Docker image +--- docker_jupyter.sh # Launch Jupyter Lab +--- docker_name.sh # Docker image naming variables +--- docker_bash.sh # Interactive shell in container +--- docker_clean.sh # Remove Docker images +--- requirements.txt # Python dependencies +--- flink_weather_anomaly_utils.py # Shared utility functions +--- flink_weather_anomaly.API.ipynb # PyFlink API tutorial notebook +--- flink_weather_anomaly.example.ipynb # End-to-end example notebook +--- data/ + --- weather_data.csv # Training dataset (1200 rows) + --- weather_logs_sample.csv # Sample collected event log +--- models/ + --- isolation_forest.pkl # Pre-trained model +--- pipeline/ # Live streaming pipeline + --- openweather_stream_job.py # PyFlink job: API polling, detection, InfluxDB, email + --- train_model.py # Model training script + --- test_model.py # Model evaluation script + --- docker-compose.yml # Starts app + InfluxDB + Grafana + --- .env.example # Template for required credentials +``` + +--- + +## Notebooks + +### `flink_weather_anomaly.API.ipynb` +This notebook covers the PyFlink DataStream API. It walks through creating a `StreamExecutionEnvironment`, building streams with `from_collection()`, applying `map`, `filter`, and custom `MapFunction` classes, understanding the PyFlink type system (`Types.STRING`, `Types.INT`, etc.), and executing Flink jobs. + +### `flink_weather_anomaly.example.ipynb` +This notebook runs through the full weather anomaly detection system, from loading and exploring the training data to training the Isolation Forest model, running records through a PyFlink DataStream pipeline, evaluating detection performance (precision, recall, F1, accuracy), and visualising anomaly events, deviation histograms, and the confusion matrix. + +--- + +## Quick Start + +### Prerequisites +- Docker installed and running +- Git repository cloned (`gpsaggese/umd_classes`) + +### 1. Build the Docker image + +```bash +cd class_project/DATA605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection +bash docker_build.sh +``` + +### 2. Launch Jupyter Lab + +```bash +bash docker_jupyter.sh -p 8888 +``` + +Open your browser at **http://localhost:8888** and run the notebooks in order: +1. `flink_weather_anomaly.API.ipynb` +2. `flink_weather_anomaly.example.ipynb` + +--- + +## Live Pipeline + +The `pipeline/` folder contains the full production streaming system. It runs +continuously, polls OpenWeatherMap every 10 minutes, and feeds each reading +through the PyFlink anomaly detection pipeline. + +### Services started by Docker Compose + +| Service | Port | Role | +|---|---|---| +| `weather-anomaly-app` | - | PyFlink job: fetches API data, detects anomalies, writes to InfluxDB, sends email alerts | +| `influxdb` | 8086 | Time-series storage for all weather readings | +| `grafana` | 3000 | Real-time dashboard showing temperature, alerts, and deviations per city | + +### Credentials (.env file) + +The live pipeline requires a `.env` file in the `pipeline/` directory. Copy +`.env.example` and fill in your values: + +``` +OPENWEATHER_API_KEY=your_openweathermap_api_key +GMAIL_USER=your_gmail_address@gmail.com +GMAIL_APP_PASSWORD=your_16_character_app_password +ALERT_RECIPIENT=recipient_email@gmail.com +``` + +**OPENWEATHER_API_KEY** - free API key from https://openweathermap.org/api (sign up, free tier is enough). + +**GMAIL_APP_PASSWORD** - not your regular Gmail password. Go to Google Account, Security, 2-Step Verification, then App Passwords and generate a 16-character password for this app. This is required because Gmail blocks direct password login for third-party apps. + +**ALERT_RECIPIENT** - the email address that receives anomaly alerts. Can be the same as GMAIL_USER. + +The `.env` file is never committed to the repository because it contains secrets. + +### Running the live pipeline + +```bash +cd pipeline +cp .env.example .env +# fill in your credentials in .env +docker compose up --build +``` + +Once running, open **http://localhost:3000** in your browser to view the Grafana dashboard. + +--- + +## Key Design Decisions + +**Why PyFlink instead of raw Python?** +PyFlink provides the DataStream abstraction that separates *what to compute* from *how to execute it*. The same `map()` function can run locally (parallelism=1) for development or on a distributed Flink cluster for production without code changes. + +**Why Isolation Forest?** +Weather anomaly detection is an unsupervised problem, since there are no labelled anomaly examples at runtime. Isolation Forest efficiently identifies outliers by randomly partitioning the feature space; anomalies are isolated in fewer splits than normal points. + +**Why dual detection?** +The Isolation Forest is trained on a global dataset and may miss city-specific anomalies (e.g., an unusual cold snap in a normally warm city). The rolling city baseline catches localised deviations that the global model overlooks. + +--- + +## References + +- [Apache Flink Documentation](https://flink.apache.org/docs/stable/) +- [PyFlink DataStream API Guide](https://nightlies.apache.org/flink/flink-docs-stable/docs/dev/python/datastream_tutorial/) +- [OpenWeatherMap API](https://openweathermap.org/api) +- [Isolation Forest, Liu et al. (2008)](https://cs.nju.edu.cn/zhouzh/zhouzh.files/publication/icdm08b.pdf) +- [scikit-learn IsolationForest](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.IsolationForest.html) diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/bashrc b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/bashrc new file mode 100644 index 000000000..4b7ff4c49 --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/bashrc @@ -0,0 +1 @@ +set -o vi diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/data/weather_data.csv b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/data/weather_data.csv new file mode 100644 index 000000000..0b89afde1 --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/data/weather_data.csv @@ -0,0 +1,1201 @@ +temperature,humidity,pressure +15.05,82.27,1016.09 +1.6,49.66,1009.53 +19.5,73.64,1020.65 +21.41,61.71,1009.23 +-7.51,64.39,1009.18 +-1.02,54.47,1016.25 +13.28,61.66,1017.58 +8.84,78.0,1015.69 +11.83,70.83,1002.89 +3.47,82.32,1016.23 +20.79,76.77,1006.79 +19.78,69.39,1014.41 +12.66,82.44,1004.46 +23.27,69.39,1015.68 +16.68,73.28,1008.16 +3.41,62.03,1005.3 +15.69,65.67,1017.28 +2.41,58.03,1014.45 +20.78,74.9,1009.32 +11.5,53.22,1021.71 +10.15,72.82,1010.36 +5.19,63.09,1013.19 +24.23,76.71,1014.61 +10.45,72.51,1009.28 +7.72,83.21,1015.83 +8.48,72.31,1009.8 +17.32,49.28,1010.53 +15.65,64.33,1021.18 +16.13,53.28,1006.76 +16.31,59.82,998.52 +33.42,80.11,1022.67 +7.94,71.38,1028.3 +6.88,58.01,1010.57 +3.86,54.86,1001.38 +18.16,65.33,1011.14 +23.29,52.83,1011.28 +10.86,58.29,1011.86 +3.6,68.12,1006.32 +3.76,76.55,1016.48 +18.51,71.09,1016.15 +19.43,42.09,1004.03 +17.43,68.04,1017.2 +5.34,65.72,1025.32 +14.32,69.14,1014.03 +13.17,81.16,1010.98 +14.19,44.37,1012.15 +20.71,59.09,1016.69 +14.24,70.91,1002.62 +18.79,49.18,1013.99 +12.68,79.76,1010.66 +14.89,68.68,1024.09 +18.31,73.47,1011.95 +-2.57,59.29,1023.01 +8.8,73.14,1006.38 +7.3,75.68,1016.52 +5.61,67.33,1014.92 +9.25,67.34,1007.79 +26.95,67.7,1014.06 +3.34,56.37,1020.28 +21.68,63.52,1011.06 +-4.83,63.47,1002.85 +8.65,68.83,1012.89 +13.63,75.0,1007.59 +17.86,54.41,1010.95 +19.11,63.75,1012.51 +19.93,79.81,1002.77 +8.51,57.56,1003.31 +7.38,56.78,1015.89 +20.58,67.02,1009.86 +10.09,73.44,997.61 +-0.76,65.11,1017.71 +0.67,78.29,1014.63 +2.81,73.57,1008.72 +16.97,73.42,1005.1 +13.42,70.54,1018.01 +18.9,88.28,1015.1 +7.73,62.95,1027.3 +13.59,44.96,1015.52 +18.26,81.04,1015.33 +8.91,60.42,1012.0 +16.57,66.08,1017.9 +5.38,78.1,1016.75 +8.37,48.98,1020.51 +8.18,52.48,1009.87 +0.04,48.99,1010.39 +16.87,57.06,1010.13 +7.31,69.4,1017.74 +12.12,70.24,1021.99 +16.81,67.76,1010.25 +16.47,50.87,1010.45 +18.65,41.9,1014.88 +11.02,65.54,1011.53 +7.77,60.28,1018.71 +11.2,69.59,999.49 +-4.87,72.02,1008.04 +-2.47,66.38,1008.31 +-1.23,72.6,999.08 +2.03,67.29,1007.22 +16.0,70.3,1007.51 +2.95,57.95,1011.79 +8.22,63.2,1019.68 +24.99,66.97,1011.53 +8.44,73.21,1006.82 +19.38,61.06,1012.66 +2.66,70.21,1019.3 +9.95,62.34,1007.14 +2.5,63.82,1007.54 +8.61,73.3,1016.35 +20.4,45.07,1011.67 +-5.27,52.04,1016.88 +16.34,50.18,1012.92 +14.38,41.66,1017.21 +6.06,58.22,1006.79 +-2.46,72.49,1012.93 +12.72,62.15,1011.74 +6.71,66.98,1005.7 +14.33,75.89,1003.62 +12.22,78.28,1017.11 +28.02,64.31,1010.89 +9.61,78.54,1006.87 +1.77,65.92,1012.42 +13.79,56.63,1019.77 +14.2,59.06,999.32 +25.59,50.19,1004.02 +20.35,56.12,1007.46 +15.57,61.42,1021.77 +26.63,73.04,1014.7 +0.11,82.21,1017.6 +5.6,51.18,1006.16 +2.73,68.93,1006.28 +8.1,54.59,1015.69 +-1.77,69.75,1013.35 +18.35,63.69,1016.29 +9.78,46.69,1011.87 +-2.71,74.28,1014.67 +1.84,58.95,1013.95 +15.14,59.66,1017.67 +20.38,54.3,1017.84 +31.97,58.46,1003.28 +41.14,69.28,999.52 +16.14,63.11,1019.01 +2.1,68.29,1020.13 +-9.32,68.62,1006.88 +14.68,78.21,1001.84 +3.87,61.57,1013.59 +7.85,50.23,1018.59 +5.88,75.67,1023.79 +10.59,61.69,1016.1 +22.66,76.15,1010.77 +13.57,68.83,1007.64 +10.41,63.69,1013.07 +1.64,68.49,1011.2 +-4.75,84.51,1006.91 +7.14,85.77,1025.29 +11.46,65.69,1023.71 +29.68,66.6,1019.82 +13.3,75.76,1007.47 +21.83,56.54,1018.13 +7.01,68.33,1016.84 +0.15,64.74,1015.66 +2.35,68.14,1020.5 +4.75,56.67,1016.81 +33.28,49.1,1017.44 +3.79,44.27,1016.82 +20.38,53.83,1015.04 +2.97,60.41,1002.3 +21.32,62.07,1013.5 +15.85,84.37,1009.66 +10.43,76.06,1005.32 +11.59,55.38,1023.09 +5.45,68.48,1023.37 +16.46,60.93,1021.16 +7.45,62.16,1014.53 +-0.26,66.85,1021.1 +-0.78,71.19,1013.07 +13.73,61.61,1014.22 +27.79,75.64,1006.44 +13.6,53.58,1015.38 +10.81,65.06,1013.36 +14.86,90.98,1005.18 +25.06,67.23,1012.69 +14.19,79.33,1012.52 +7.89,65.92,1023.79 +23.06,70.81,1018.37 +16.29,64.43,1013.07 +27.36,63.3,1014.49 +13.83,57.21,1013.27 +-0.24,69.3,1011.78 +-1.68,56.48,1006.51 +28.51,71.66,1012.09 +29.24,75.85,1008.52 +10.2,68.67,1005.5 +8.17,62.14,1016.07 +26.61,69.54,1015.35 +0.93,61.91,1002.28 +3.05,74.36,1012.26 +18.43,46.69,1018.97 +8.05,61.64,1019.36 +11.95,45.09,1019.16 +10.37,50.05,1013.23 +15.38,78.64,1007.93 +26.07,73.95,1006.5 +12.91,57.81,1015.07 +18.44,49.97,1015.28 +-8.5,35.35,1020.72 +11.51,59.57,1019.6 +3.57,89.2,1012.21 +-0.19,69.35,1005.53 +3.22,59.4,1011.09 +8.66,69.65,1014.3 +21.16,49.39,1011.79 +-1.26,62.03,1009.53 +12.31,65.99,1014.52 +7.16,64.14,1009.98 +8.72,72.91,1009.23 +22.03,68.45,1014.87 +17.38,71.68,1010.59 +25.37,58.12,1014.46 +10.45,73.98,1014.64 +5.04,81.29,1006.16 +9.76,55.3,1010.11 +14.42,56.12,1021.63 +13.77,78.36,1006.03 +1.16,63.09,1000.3 +12.9,79.04,1001.83 +14.28,60.57,1013.17 +37.17,79.55,1013.19 +30.77,66.31,1012.29 +3.47,67.58,1020.29 +9.13,80.65,996.96 +-2.63,61.38,1015.38 +6.09,55.59,1022.37 +15.16,60.51,1006.23 +24.06,69.52,1010.72 +4.71,49.34,1008.48 +5.46,71.37,1007.63 +-9.47,59.61,1011.04 +10.37,76.48,1021.56 +1.38,41.06,1024.02 +6.71,57.13,1010.98 +3.23,48.14,1024.43 +11.06,56.74,1013.21 +-5.58,67.48,1023.52 +-2.67,63.21,1012.44 +33.29,62.47,1013.79 +-0.87,63.41,1015.19 +1.03,67.03,1032.07 +30.37,54.91,1018.11 +41.05,72.07,1008.76 +0.28,71.63,1018.81 +8.32,68.85,1010.83 +15.42,70.57,1010.06 +29.29,67.96,1018.45 +2.13,85.35,1013.19 +9.55,64.13,1014.67 +19.77,61.93,1013.08 +16.35,57.46,1015.02 +8.24,54.68,1015.55 +10.66,52.56,1001.38 +-1.75,56.11,1017.0 +9.62,64.29,1007.11 +9.34,68.34,1004.35 +14.32,65.51,1012.65 +6.45,57.34,1013.5 +16.72,74.0,1008.84 +22.13,72.39,1017.99 +13.55,63.4,1004.95 +15.52,58.47,1010.56 +12.53,70.48,1009.49 +12.0,66.88,1012.72 +4.78,50.52,1014.67 +15.16,64.32,1006.95 +11.03,67.62,1017.35 +32.93,56.0,1013.38 +27.73,66.9,1001.65 +15.86,50.45,1001.25 +4.37,78.36,1012.93 +0.88,77.48,1011.67 +23.91,62.47,1012.38 +14.63,68.63,1012.83 +16.8,40.9,1014.35 +-5.45,53.44,1018.69 +21.27,62.06,1006.33 +16.54,54.28,1005.97 +0.9,72.14,1006.44 +7.28,84.97,1014.73 +14.64,53.23,1020.47 +12.52,56.63,1010.41 +9.08,67.35,997.99 +10.97,81.11,1002.78 +9.48,52.78,1008.0 +13.53,67.49,1009.65 +26.71,83.21,1010.55 +-13.67,48.48,1013.23 +9.63,52.19,1011.13 +13.77,60.76,1019.3 +14.96,59.79,1008.94 +8.28,73.13,1007.83 +-5.57,67.42,1015.87 +15.28,47.25,1003.79 +19.95,39.13,1012.73 +18.51,49.17,1012.5 +17.26,60.04,1020.3 +25.94,53.77,1016.32 +14.89,47.19,1014.15 +28.3,46.91,1016.99 +16.87,46.45,1006.57 +11.61,44.45,1014.83 +18.35,82.38,1022.99 +12.62,58.57,1015.53 +14.24,65.64,1015.27 +19.99,49.13,1017.19 +20.78,52.77,1018.58 +14.89,46.44,1007.01 +22.94,23.18,1008.27 +23.79,38.46,1015.84 +25.18,33.27,1012.14 +20.65,28.0,1020.25 +24.82,40.66,1013.74 +8.6,70.9,1007.26 +16.41,54.47,1009.93 +13.66,70.49,1004.0 +18.6,59.93,1014.06 +15.14,64.39,1014.25 +17.17,44.19,1013.3 +27.41,61.29,1019.32 +17.15,63.74,1019.79 +20.07,48.08,1014.32 +16.84,61.71,1006.46 +18.38,61.79,1017.47 +18.03,48.41,1017.22 +20.24,41.53,1016.24 +23.83,40.79,1018.93 +26.24,56.04,1010.34 +19.55,58.92,1023.04 +20.95,45.49,1011.16 +12.25,55.32,1018.0 +17.56,61.85,1008.25 +22.7,62.42,1011.28 +22.33,73.62,1021.07 +19.06,70.32,1013.96 +22.43,42.98,1018.36 +20.45,75.2,1015.95 +24.0,48.63,1014.58 +19.45,67.54,1020.46 +16.22,55.82,1014.05 +19.68,50.05,1007.88 +3.35,33.31,1011.79 +19.91,52.94,1017.2 +-0.24,36.29,1016.58 +9.38,66.61,1008.73 +20.26,73.2,1012.1 +20.39,45.44,1012.67 +12.19,58.62,1013.54 +14.44,46.3,1009.5 +24.85,47.46,1012.49 +15.58,64.29,1012.09 +29.21,54.54,1018.09 +17.99,75.88,1018.24 +20.04,49.21,1014.42 +26.08,66.99,1013.43 +18.66,56.17,1015.56 +12.99,64.55,1011.73 +17.45,49.63,1010.41 +17.82,54.41,1011.24 +11.18,53.61,1012.46 +16.72,44.97,1010.35 +14.29,62.94,1015.65 +22.62,62.8,1017.92 +18.17,62.12,1008.65 +16.59,73.44,1015.1 +17.47,72.39,1014.12 +19.12,50.5,1016.37 +21.08,59.48,1014.95 +13.0,47.52,1009.19 +12.79,55.28,1009.95 +23.52,47.8,1015.12 +15.94,74.44,1019.12 +10.92,59.36,1006.14 +20.22,52.38,1016.38 +20.32,69.32,1019.76 +10.35,28.95,1014.39 +19.15,30.83,1017.2 +21.68,64.55,1012.19 +19.87,55.73,1014.52 +21.16,58.01,1008.52 +10.98,39.08,1005.62 +19.66,54.72,1018.77 +16.49,79.04,1011.06 +15.59,64.89,1013.18 +22.35,57.04,1009.45 +25.4,49.73,1009.36 +26.97,50.27,1014.3 +24.57,29.45,1012.08 +17.45,58.09,1016.51 +19.76,65.15,1016.81 +21.83,36.09,1016.28 +18.61,47.06,1015.95 +18.65,41.11,1021.31 +22.12,43.42,1010.73 +17.7,55.64,1014.78 +14.35,29.99,1019.53 +15.93,62.37,1020.06 +21.17,64.05,1009.31 +18.01,51.99,1013.75 +19.7,25.23,1020.43 +21.35,43.04,1017.03 +16.13,69.79,1015.78 +21.78,21.66,1014.13 +19.89,50.83,1013.46 +11.83,40.84,1016.15 +25.21,64.65,1013.99 +15.5,46.9,1014.83 +9.72,59.85,1015.31 +12.77,61.79,1012.29 +12.89,77.03,1017.13 +18.26,52.56,1026.66 +16.63,59.51,1007.89 +16.32,37.18,1016.3 +21.1,69.28,1011.96 +19.7,45.87,1017.31 +19.58,48.27,1015.19 +20.05,54.73,1018.05 +21.08,36.25,1019.7 +7.46,57.75,1015.77 +16.18,66.61,1019.53 +7.1,58.52,1005.62 +18.18,35.93,1021.36 +17.98,53.51,1016.5 +23.23,63.9,1019.31 +23.94,32.46,1010.12 +19.01,42.1,1011.68 +15.5,65.52,1015.58 +20.43,58.24,1014.93 +15.36,59.94,1016.13 +17.99,77.31,1019.34 +22.93,60.62,1007.94 +15.21,62.89,1018.9 +22.03,87.53,1015.03 +21.39,52.43,1014.18 +13.23,45.43,1009.12 +22.87,60.13,1017.01 +21.49,60.27,1005.34 +18.51,48.68,1017.59 +14.19,42.61,1009.03 +13.7,82.72,1015.21 +15.31,58.92,1014.52 +20.71,46.21,1019.68 +13.22,65.99,1014.29 +20.19,53.69,1012.22 +11.79,51.97,1012.97 +16.98,61.75,1020.09 +18.55,39.5,1017.15 +30.23,59.23,1013.46 +11.11,77.96,1014.05 +25.36,60.59,1008.73 +18.75,50.49,1016.55 +20.06,48.91,1007.96 +18.59,50.08,1018.01 +20.22,51.95,1015.51 +17.23,50.47,1024.55 +25.27,66.0,1010.6 +15.72,34.2,1014.59 +23.66,65.34,1022.19 +14.78,50.44,1019.77 +17.7,59.52,1012.22 +12.64,41.48,1015.9 +20.28,70.94,1015.01 +25.23,69.95,1016.13 +17.61,66.52,1012.21 +17.02,36.82,1014.64 +12.43,64.95,1008.61 +16.85,59.86,1015.54 +10.04,35.74,1016.8 +13.44,54.71,1014.09 +19.13,59.37,1008.54 +24.6,61.67,1018.95 +32.05,57.13,1016.46 +15.07,58.49,1012.1 +25.18,72.68,1013.88 +19.22,69.71,1009.0 +17.24,20.6,1010.9 +20.16,51.19,1012.85 +18.31,53.02,1019.91 +18.55,33.97,1012.44 +15.96,56.13,1017.56 +11.01,69.99,1012.85 +10.28,41.96,1015.99 +21.27,59.04,1014.43 +16.62,44.01,1018.49 +15.02,46.94,1012.86 +18.04,72.73,1008.71 +21.97,48.49,1009.47 +18.9,60.55,1012.87 +14.72,32.55,1010.0 +24.13,77.35,1016.64 +25.9,62.23,1013.14 +20.47,52.81,1024.5 +22.87,61.91,1008.05 +24.21,38.08,1015.81 +23.65,69.19,1010.49 +21.07,51.11,1013.03 +20.99,52.55,1015.57 +20.6,49.1,1005.7 +12.51,48.03,1015.95 +21.5,63.34,1022.61 +11.22,58.07,1013.19 +14.03,62.82,1016.04 +24.52,54.89,1019.1 +22.2,61.16,1014.34 +25.44,49.97,1012.68 +16.64,81.76,1005.9 +12.24,36.18,1017.56 +16.8,70.13,1014.55 +18.51,71.41,1010.95 +18.39,44.77,1015.51 +23.67,81.75,1012.67 +16.19,66.95,1012.89 +19.76,66.65,1017.43 +13.06,55.34,1009.65 +20.25,52.39,1019.19 +18.02,52.49,1009.41 +14.25,59.69,1012.35 +16.82,71.81,1022.69 +17.08,57.18,1019.51 +16.65,44.06,1015.39 +26.86,64.89,1011.24 +17.51,66.05,1012.88 +16.78,62.86,1015.29 +7.51,63.95,1015.15 +13.53,51.75,1014.64 +16.68,43.85,1014.88 +14.57,44.58,1014.14 +24.91,35.37,1009.23 +17.18,57.84,1012.93 +24.44,54.0,1018.66 +18.31,49.72,1009.53 +18.19,19.53,1014.24 +17.56,40.03,1011.72 +18.02,68.45,1015.45 +26.59,47.02,1013.45 +6.4,59.33,1020.91 +7.99,38.31,1017.03 +15.28,72.26,1009.75 +18.07,53.27,1017.68 +21.45,58.48,1011.9 +20.37,82.91,1012.69 +16.08,73.2,1016.26 +23.1,51.32,1013.03 +23.15,47.88,1018.57 +18.92,51.09,1011.9 +22.81,61.12,1012.52 +19.36,53.74,1012.37 +15.19,50.26,1012.74 +21.49,72.61,1019.4 +18.55,53.63,1017.52 +18.01,82.6,1021.84 +25.36,56.09,1024.07 +5.75,76.42,1010.88 +10.91,59.29,1015.28 +12.06,66.56,1010.3 +16.18,46.3,1011.89 +16.73,44.75,1012.48 +10.46,60.52,1010.77 +13.07,66.86,1010.79 +13.7,55.48,1014.42 +30.29,39.56,1010.13 +27.01,38.68,1018.37 +15.94,37.87,1011.33 +16.18,33.79,1026.24 +12.25,56.31,1013.19 +8.46,68.5,1015.29 +17.42,54.84,1009.82 +13.01,27.88,1021.82 +17.58,52.71,1019.48 +10.0,53.05,1016.19 +14.19,80.15,1009.61 +18.74,37.51,1015.84 +19.83,50.31,1010.51 +20.09,75.47,1020.85 +11.4,60.4,1018.24 +22.27,61.76,1015.38 +14.0,56.38,1005.25 +21.16,58.96,1011.07 +17.95,64.33,1017.75 +11.12,45.29,1019.85 +16.42,65.54,1019.44 +19.83,38.52,1008.63 +21.06,58.94,1016.21 +17.3,57.14,1019.06 +25.66,47.6,1008.37 +23.04,54.73,1014.97 +16.72,47.53,1016.1 +21.75,42.14,1013.8 +27.67,50.76,1007.59 +27.8,41.75,1013.7 +11.86,58.87,1013.69 +13.37,41.2,1014.41 +25.42,63.54,1012.75 +12.71,40.82,1013.23 +9.52,72.57,1006.88 +15.31,52.86,1016.86 +6.35,62.86,1011.97 +5.08,77.72,1005.12 +11.11,67.2,1003.94 +-13.05,64.88,998.47 +15.72,84.01,1012.04 +9.99,59.53,1007.52 +10.48,62.02,993.39 +21.53,72.11,1006.25 +-13.43,73.9,1003.69 +-13.69,74.96,1021.03 +13.29,79.11,998.08 +9.91,71.99,999.91 +12.04,74.89,1017.77 +4.45,67.41,1019.5 +17.97,70.63,1004.79 +21.19,71.08,1005.58 +15.81,72.15,1014.31 +6.87,77.73,993.71 +-15.29,75.55,996.97 +19.23,70.44,1008.03 +24.66,61.91,1020.85 +17.73,70.68,1006.95 +-0.56,69.72,1010.71 +18.09,71.93,1017.94 +20.33,92.07,1024.75 +28.71,64.76,1005.96 +-0.51,74.22,1009.88 +7.28,80.68,1022.75 +18.97,70.98,999.34 +10.69,93.38,1017.19 +-3.41,66.22,1002.87 +14.98,73.24,1010.72 +16.94,62.01,1008.3 +11.06,66.48,1003.29 +12.64,72.49,1003.12 +6.9,78.46,1011.93 +27.74,73.19,1016.97 +-0.81,82.59,1009.09 +2.95,69.05,1010.49 +10.13,66.68,1014.0 +20.55,97.04,1003.2 +-7.97,73.94,1015.18 +-5.72,62.81,1015.74 +-14.45,63.06,1028.23 +10.39,60.57,1007.37 +-3.42,79.37,1006.79 +5.39,66.36,1015.38 +17.94,71.27,1015.73 +2.3,69.36,1006.25 +12.78,77.31,1007.18 +6.41,61.53,1013.79 +18.07,59.24,1004.21 +15.7,72.24,1007.13 +1.7,76.01,1010.63 +7.98,73.33,1016.97 +22.52,61.91,1005.77 +14.72,76.77,997.1 +-6.76,62.56,996.58 +17.87,64.78,1001.18 +-3.02,60.56,1000.81 +26.95,83.01,1008.92 +22.29,76.33,1001.68 +13.48,76.05,1005.08 +11.89,82.47,1004.58 +7.49,98.13,1006.92 +18.14,52.91,1019.26 +5.55,62.62,1014.63 +12.83,63.84,1010.11 +20.46,61.49,1006.77 +3.26,59.22,1006.97 +2.61,67.0,1012.29 +-4.33,57.83,1014.09 +-2.77,73.35,1003.65 +-11.94,70.52,1010.26 +18.76,74.81,995.78 +31.71,75.08,1011.48 +27.1,76.03,1015.34 +-9.51,66.27,1008.04 +18.7,91.21,1022.72 +27.07,72.34,1003.83 +4.52,51.38,1011.52 +11.22,59.98,1009.75 +-14.09,52.59,1009.3 +13.2,70.17,1009.2 +-2.76,68.78,1010.75 +12.4,65.32,1015.94 +-8.73,90.11,1019.64 +16.51,75.49,1009.98 +22.29,77.26,1015.71 +0.6,60.69,1005.09 +-3.64,60.01,1015.36 +-15.87,70.37,1004.67 +6.8,71.73,1017.63 +3.02,51.44,1025.47 +-8.61,68.2,1009.15 +15.44,70.14,994.65 +4.6,67.85,1006.16 +-2.91,61.87,1018.62 +3.35,64.6,1013.87 +22.86,68.23,1011.74 +17.24,70.91,1017.06 +8.44,76.05,1013.39 +17.43,80.81,1018.52 +-3.93,67.06,1016.44 +10.39,65.44,1020.65 +11.77,74.38,1012.51 +4.36,76.11,1016.34 +15.23,65.66,1007.85 +0.63,77.9,1005.79 +16.12,79.8,1021.95 +16.62,82.92,1011.51 +-25.01,87.78,1015.63 +24.28,61.34,1010.87 +-26.57,70.14,1005.89 +2.61,70.11,1010.08 +22.3,69.02,1020.05 +6.19,63.51,1013.89 +-7.59,55.14,998.02 +-3.12,70.93,1008.49 +22.32,62.32,1022.48 +-13.06,70.86,1008.5 +23.55,62.06,997.4 +9.34,59.0,1023.25 +-4.31,72.34,1014.78 +3.0,71.51,1009.51 +-2.21,75.79,1007.4 +20.39,91.86,1017.58 +18.79,72.77,1006.21 +-10.63,74.85,1018.04 +5.16,59.22,1013.8 +-0.36,57.13,1011.71 +25.52,85.83,1011.02 +-6.2,60.77,1008.78 +29.25,59.56,1017.32 +9.6,69.61,1003.94 +-3.94,69.1,998.65 +1.87,60.97,1014.3 +0.38,66.95,1014.42 +-5.3,82.29,1013.36 +11.14,61.94,1012.85 +-4.73,56.06,1007.76 +-16.16,79.43,1015.51 +5.44,74.92,1009.68 +-11.63,63.79,1006.59 +0.97,54.18,1007.82 +-0.91,62.55,1024.14 +-0.45,69.45,997.86 +34.01,81.66,1013.5 +10.68,59.63,998.25 +12.94,69.15,1009.24 +-11.32,76.75,1006.96 +2.15,46.19,995.4 +12.58,80.34,1012.72 +-2.29,58.18,1007.34 +20.9,73.32,995.31 +-4.43,72.47,1015.88 +6.56,80.85,1020.05 +2.97,75.51,1020.43 +11.97,46.23,1012.51 +2.31,62.15,1010.32 +6.75,71.38,1019.3 +7.17,68.05,1021.78 +1.19,74.78,1017.93 +15.82,77.89,1003.41 +12.86,76.08,1011.41 +8.94,67.86,1013.08 +4.83,51.54,997.33 +19.56,65.45,1013.29 +7.17,74.16,1025.91 +6.32,59.1,1014.48 +11.26,76.31,1020.31 +30.36,79.08,1011.28 +-6.08,71.27,1009.49 +19.24,39.36,1012.67 +-12.72,57.22,1004.29 +12.61,88.86,1003.25 +28.9,64.53,1018.86 +15.86,50.16,1019.61 +12.05,82.35,1003.06 +3.27,70.13,1016.78 +10.37,81.05,1009.39 +6.98,72.77,1012.38 +0.57,74.59,1013.22 +6.52,73.63,1016.96 +12.38,76.97,1010.58 +0.79,92.77,1019.87 +0.32,79.71,1012.17 +-6.32,54.2,1014.37 +14.69,74.47,1004.17 +16.54,81.41,1025.11 +12.78,82.03,1013.29 +21.58,65.73,1014.99 +11.05,81.65,998.74 +14.7,79.28,1004.24 +13.65,95.73,1018.67 +8.9,62.5,1012.01 +3.63,64.93,996.6 +36.57,66.94,1017.27 +4.58,65.1,1006.19 +2.01,61.1,1019.88 +15.21,42.37,1009.39 +13.02,65.65,1011.43 +-6.86,73.69,1018.25 +23.47,65.07,1003.72 +8.87,89.35,1003.05 +-3.33,70.13,1008.62 +24.27,82.91,1015.79 +17.51,72.61,1000.68 +-5.17,78.26,998.32 +31.33,72.79,1015.77 +17.91,83.69,1005.78 +9.88,70.56,1007.25 +29.08,74.96,997.0 +11.91,73.66,1005.25 +9.66,67.12,1009.95 +2.08,74.05,1016.51 +16.98,92.34,1004.6 +15.72,67.3,1021.6 +10.18,74.19,1005.21 +0.52,82.58,1005.22 +11.56,81.4,1020.76 +9.16,70.12,1011.48 +-6.0,46.17,1006.14 +-7.66,75.17,1008.91 +16.78,47.81,1019.47 +-21.22,84.7,1020.26 +12.46,71.69,1004.28 +4.26,80.0,1009.7 +25.7,60.98,1010.63 +4.08,54.47,1003.1 +17.91,72.61,1009.61 +5.24,69.66,1010.04 +11.36,65.08,1017.98 +18.84,70.85,1018.45 +4.45,97.67,1007.7 +21.96,78.35,1008.5 +-1.92,80.49,1014.65 +13.54,78.44,1005.9 +21.07,84.79,1014.12 +17.92,68.1,1009.38 +33.0,49.45,1003.21 +0.84,68.11,1003.3 +-6.55,78.12,1020.13 +-1.93,60.3,1008.91 +17.93,68.24,1021.48 +15.98,63.08,1006.4 +11.23,85.02,1012.04 +-3.48,77.49,1026.75 +19.2,76.52,1002.6 +-4.7,76.22,996.72 +23.91,69.63,1019.01 +44.29,71.98,1004.21 +14.4,58.15,1009.86 +17.0,48.14,1005.9 +4.09,71.5,1011.88 +-11.72,73.24,1003.35 +2.77,82.09,1008.74 +-3.11,85.71,1013.15 +-7.36,85.7,1002.94 +30.67,60.02,1005.1 +13.27,66.48,1007.57 +14.56,74.8,1000.0 +6.29,95.32,1007.04 +7.93,84.8,1010.83 +21.57,81.19,1007.2 +9.61,66.12,1008.1 +16.37,61.17,1009.86 +7.65,77.89,1005.41 +5.89,58.99,1014.84 +-6.88,67.62,1020.05 +-13.73,68.85,1006.75 +6.86,85.93,1013.4 +10.68,66.45,1008.32 +-5.43,45.61,1012.56 +9.59,75.04,1020.96 +10.65,63.01,1013.56 +-15.87,70.43,997.59 +-0.98,69.13,1013.18 +14.89,67.26,1018.63 +14.64,77.9,1008.45 +-3.84,54.67,1016.97 +11.9,64.83,1005.17 +22.27,76.84,999.6 +19.32,63.86,1010.99 +8.86,53.91,997.7 +4.38,76.69,1008.32 +2.45,81.23,1019.01 +15.61,69.55,996.63 +12.34,50.39,1001.04 +17.55,77.06,1002.78 +11.58,53.4,1009.56 +19.83,73.09,1007.44 +11.14,61.13,1019.79 +19.42,58.4,1016.68 +20.45,65.58,1013.47 +0.35,68.17,1011.02 +-11.7,63.56,1012.75 +28.52,75.89,1003.97 +28.86,87.49,1014.75 +13.1,81.88,1022.31 +28.68,94.66,1016.7 +31.49,59.52,1016.88 +23.69,72.35,1027.44 +20.59,70.37,1014.58 +25.2,55.26,1014.42 +-0.89,85.6,1015.52 +24.42,72.3,1006.96 +21.09,82.4,1009.78 +21.59,98.6,1009.47 +27.65,71.47,1015.24 +21.4,70.79,1011.13 +12.88,76.19,1012.69 +20.87,84.87,1017.44 +22.54,69.33,1016.32 +22.92,68.69,1011.19 +23.3,85.57,1008.65 +30.31,78.79,1017.68 +13.31,54.55,1015.87 +23.41,62.75,1003.97 +32.32,90.01,1010.43 +8.12,79.02,1012.83 +23.44,67.38,1015.89 +17.32,73.3,1016.55 +19.03,75.13,1013.42 +14.76,82.36,1014.93 +20.64,61.42,1012.32 +36.73,66.59,1008.63 +11.88,77.73,1010.69 +12.64,74.0,1012.09 +11.58,80.72,1011.83 +35.82,62.7,1002.57 +18.3,78.57,1010.01 +21.79,85.92,1012.28 +18.78,79.87,1010.59 +14.44,76.39,1010.77 +20.12,74.04,1014.44 +41.67,66.65,1006.61 +33.14,74.28,1003.86 +15.18,87.6,1010.26 +27.18,73.81,1015.82 +15.35,66.1,1020.44 +7.79,70.08,1015.91 +30.16,77.01,1004.66 +31.28,71.26,1010.84 +30.37,79.85,1013.28 +18.24,80.23,1012.89 +20.67,83.89,1014.7 +28.0,89.24,1007.41 +25.13,84.96,1012.32 +18.85,65.66,1014.1 +9.05,65.96,1016.13 +22.91,74.24,1007.97 +30.63,70.49,1010.27 +12.42,63.62,998.48 +27.11,70.58,1008.89 +24.65,70.25,1009.38 +28.0,68.28,1006.9 +28.98,71.84,1012.83 +16.44,86.78,1011.72 +9.06,76.99,1000.7 +17.73,99.72,1013.08 +29.11,76.71,1007.36 +17.66,72.1,1014.43 +16.45,75.04,1005.59 +7.43,77.56,1012.15 +36.02,79.35,1010.58 +28.61,95.77,1013.52 +28.16,66.55,1012.62 +29.74,83.59,1010.72 +26.61,77.08,1004.32 +20.05,59.29,1004.61 +20.69,82.71,1009.08 +18.58,87.68,1011.96 +43.08,66.43,1008.96 +26.45,74.91,1015.13 +18.29,57.75,1010.6 +25.72,62.41,1007.17 +13.91,85.48,1010.61 +34.04,70.34,1013.13 +16.71,63.91,1013.62 +26.01,77.29,1021.39 +30.53,69.78,1008.66 +25.08,70.29,1011.63 +22.76,82.39,1014.13 +22.09,63.38,1011.88 +21.55,65.95,1009.5 +17.39,76.54,1004.25 +18.4,84.69,1009.45 +33.84,87.38,1006.63 +21.13,68.35,1003.37 +15.71,84.37,1008.02 +17.87,67.16,1006.76 +30.8,93.26,1014.92 +27.4,63.66,1015.01 +26.48,88.28,1007.48 +19.94,68.44,1010.7 +26.59,95.13,1002.71 +16.85,80.91,1009.53 +19.92,64.84,1013.99 +29.76,69.78,1016.12 +28.99,82.81,1008.43 +14.14,85.53,1017.26 +15.16,74.29,1004.42 +22.87,75.45,1005.76 +35.81,82.58,1015.55 +6.68,76.32,1020.36 +24.23,62.06,1014.47 +10.99,66.87,1011.8 +16.93,77.35,1001.02 +19.08,87.15,1001.89 +18.64,91.16,1012.65 +17.73,82.8,1010.21 +22.06,77.87,1006.47 +17.72,82.49,1012.33 +10.3,68.33,1012.58 +28.24,73.47,1016.72 +28.02,66.76,1012.68 +28.75,62.51,1018.14 +9.67,78.34,1012.04 +24.39,76.86,1011.42 +27.41,81.88,1017.32 +13.75,80.46,1007.11 +22.07,77.43,1026.07 +21.07,80.57,1013.88 +36.74,74.57,1013.9 +37.4,79.13,1014.63 +8.69,67.74,1021.39 +41.95,78.7,1015.96 +14.68,81.68,1003.97 +29.85,68.3,1019.02 +29.76,75.52,1010.3 +14.03,68.76,1012.54 +25.95,69.73,1005.6 +21.14,63.75,1014.63 +26.43,87.66,1008.74 +23.85,85.66,1012.5 +31.57,80.37,1010.58 +22.6,94.72,1021.63 +20.66,73.11,1021.91 +30.77,100.0,1012.23 +26.64,72.72,1012.32 +27.85,77.55,1010.13 +16.68,71.02,1017.53 +13.64,78.72,1010.36 +17.46,83.36,1009.89 +17.03,73.86,1016.89 +28.29,92.77,1009.31 +29.81,77.39,1011.81 +20.76,77.7,1004.09 +33.45,65.91,1015.52 +32.95,79.18,1021.7 +23.95,73.66,1015.61 +15.42,68.92,1019.31 +34.6,67.24,1007.77 +21.51,100.0,1013.18 +23.01,72.37,1007.28 +19.28,60.43,1010.86 +14.88,68.02,1015.86 +10.25,74.48,1012.51 +27.06,80.47,1004.94 +15.73,80.55,1005.42 +46.48,81.77,1005.09 +13.31,68.55,1014.46 +11.94,78.4,1022.32 +26.08,66.24,1018.81 +34.23,68.8,1002.52 +22.52,65.82,1019.11 +26.58,63.12,1016.68 +7.25,85.64,1004.27 +16.58,81.89,1010.48 +13.41,84.77,1017.48 +6.84,75.39,1014.75 +22.94,82.77,1005.45 +4.06,57.39,1015.05 +11.42,83.63,1001.31 +17.7,76.77,1010.89 +15.35,81.27,1017.69 +25.17,78.8,1012.09 +24.44,65.13,1009.51 +29.15,60.02,1016.45 +28.02,67.88,1016.87 +31.13,80.65,1018.55 +21.98,81.9,1016.37 +24.43,61.32,1017.41 +26.09,65.19,1015.66 +23.13,67.77,1013.33 +18.82,71.43,1012.45 +19.34,61.38,1010.81 +15.46,60.78,1005.94 +25.37,66.78,1007.22 +21.25,75.86,1010.59 +16.69,63.19,1013.21 +16.7,73.84,1019.2 +12.32,85.59,1011.79 +29.83,61.98,1017.02 +22.63,72.94,1017.4 +15.1,85.25,1010.84 +28.47,70.82,1014.97 +17.52,69.26,1010.78 +10.75,86.63,1018.61 +30.17,71.94,1015.18 +17.7,86.04,1006.05 +18.73,79.29,1012.81 +22.33,71.57,1020.24 +31.14,79.8,1011.09 +22.16,78.2,1014.24 +12.41,85.21,1014.73 +26.35,69.52,1006.82 +18.12,75.41,1014.55 +37.1,74.38,1005.03 +21.99,82.04,1008.87 +4.12,64.75,1017.38 +24.86,85.17,1013.78 +37.56,79.66,1010.48 +34.61,61.83,1014.23 +36.65,87.08,1011.7 +13.84,97.34,1012.06 +29.3,85.02,1015.28 +16.22,89.27,1008.36 +23.79,75.85,1012.2 +11.82,58.38,1010.44 +16.84,56.56,1004.07 +16.59,77.55,1007.4 +29.19,69.08,1019.84 +28.41,65.85,1013.92 +24.21,74.37,1005.38 +11.2,81.16,1004.3 +19.51,60.47,1011.44 +16.03,70.51,1019.81 +28.96,90.1,1011.07 +31.43,64.25,1018.53 +30.45,83.7,1013.76 +15.96,70.15,1014.36 +0.08,80.46,1005.32 +7.86,85.97,1016.69 +33.37,82.41,1010.33 +18.79,84.09,1016.32 +15.99,63.48,1007.93 +35.14,86.14,1012.89 +32.12,66.47,1013.76 +22.2,77.6,1011.23 +19.65,77.58,1014.02 +20.94,79.85,1010.67 +23.82,78.37,1008.75 +34.8,66.56,1020.03 +25.38,61.5,1017.24 +27.81,83.72,1008.2 +31.35,76.86,1014.84 +12.64,86.49,1008.29 +28.04,74.55,1006.08 +19.64,60.95,1017.17 +1.87,84.66,1003.48 +4.97,71.3,1016.63 +13.86,78.42,1010.74 +26.25,68.53,1010.94 +22.91,91.44,1014.05 +23.47,66.23,1014.91 +37.61,71.63,1007.87 +11.85,76.07,1015.59 +21.78,64.08,1012.57 +24.44,78.31,1005.72 +16.11,91.12,1011.15 +22.23,70.1,1018.91 +9.2,78.95,1020.38 +33.28,47.51,1015.59 +16.55,74.61,1006.05 +38.25,71.87,1014.45 +21.74,66.82,1016.25 +30.22,85.81,1015.63 +21.57,71.74,1015.92 +21.75,73.23,1013.82 +17.33,71.23,1011.64 +22.95,55.83,1008.96 +24.54,63.68,1002.51 +15.73,84.61,1013.7 +21.0,53.9,1012.93 +17.12,67.34,1014.79 +40.4,71.0,1005.31 +21.28,92.44,1015.23 +34.36,82.69,1022.27 +20.44,69.43,1012.39 +24.07,88.05,1011.69 +11.43,67.59,1013.55 +18.42,85.09,1022.16 +23.68,61.41,1006.88 +34.69,85.73,1013.02 +23.78,70.94,1008.0 +33.07,64.71,1009.26 +27.13,72.83,1011.75 +35.02,77.77,1018.74 +21.53,70.05,1021.0 +41.12,80.54,1014.24 +18.12,69.17,1010.12 +15.87,81.2,1024.46 +12.32,79.42,1009.86 +40.65,74.39,1009.14 +28.56,70.59,1003.13 +19.33,76.52,1018.98 diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/data/weather_logs_sample.csv b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/data/weather_logs_sample.csv new file mode 100644 index 000000000..88c2c70db --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/data/weather_logs_sample.csv @@ -0,0 +1,313 @@ +timestamp,city,temperature,humidity,pressure,status,temp_diff,humidity_diff,pressure_diff,iso_prediction,injected,api_calls_today +2026-05-06 08:00:00,New York,12.33,61.75,1013.2,WARMUP,0.0,0.0,0.0,0,False,1 +2026-05-06 08:00:00,Boston,12.74,55.7,1018.74,WARMUP,0.0,0.0,0.0,0,False,1 +2026-05-06 08:00:00,Chicago,5.71,65.83,1005.81,WARMUP,0.0,0.0,0.0,0,False,1 +2026-05-06 08:00:00,College Park,17.73,70.72,1018.98,WARMUP,0.0,0.0,0.0,0,False,1 +2026-05-06 08:10:00,New York,15.57,66.84,1014.31,WARMUP,0.0,0.0,0.0,0,False,1 +2026-05-06 08:10:00,Boston,13.74,61.86,1011.82,WARMUP,0.0,0.0,0.0,0,False,1 +2026-05-06 08:10:00,Chicago,9.91,66.83,1012.89,WARMUP,0.0,0.0,0.0,0,False,1 +2026-05-06 08:10:00,College Park,12.59,70.71,1014.56,WARMUP,0.0,0.0,0.0,0,False,1 +2026-05-06 08:20:00,New York,13.85,54.38,1009.56,WARMUP,0.0,0.0,0.0,0,False,1 +2026-05-06 08:20:00,Boston,15.38,69.25,1011.67,WARMUP,0.0,0.0,0.0,0,False,1 +2026-05-06 08:20:00,Chicago,12.82,75.26,1009.81,WARMUP,0.0,0.0,0.0,0,False,1 +2026-05-06 08:20:00,College Park,11.43,79.73,1016.76,WARMUP,0.0,0.0,0.0,0,False,1 +2026-05-06 08:30:00,New York,5.71,70.88,1016.07,WARMUP,0.0,0.0,0.0,0,False,1 +2026-05-06 08:30:00,Boston,13.26,65.17,1017.89,WARMUP,0.0,0.0,0.0,0,False,1 +2026-05-06 08:30:00,Chicago,4.26,78.48,1011.17,WARMUP,0.0,0.0,0.0,0,False,1 +2026-05-06 08:30:00,College Park,20.79,63.28,1016.79,WARMUP,0.0,0.0,0.0,0,False,1 +2026-05-06 08:40:00,New York,15.38,62.73,1013.05,WARMUP,0.0,0.0,0.0,0,False,1 +2026-05-06 08:40:00,Boston,8.34,71.35,1014.75,WARMUP,0.0,0.0,0.0,0,False,1 +2026-05-06 08:40:00,Chicago,6.54,72.42,1007.67,WARMUP,0.0,0.0,0.0,0,False,1 +2026-05-06 08:40:00,College Park,11.92,52.54,1009.3,WARMUP,0.0,0.0,0.0,0,False,1 +2026-05-06 08:50:00,New York,-3.51,86.74,996.8,ALERT,16.08,23.42,16.44,-1,True,1 +2026-05-06 08:50:00,Boston,6.53,63.98,1020.15,NORMAL,6.16,0.69,5.18,1,False,1 +2026-05-06 08:50:00,Chicago,12.89,73.8,1012.01,NORMAL,5.04,2.04,2.54,1,False,1 +2026-05-06 08:50:00,College Park,8.98,68.34,1012.21,NORMAL,5.91,0.94,3.07,1,False,1 +2026-05-06 09:00:00,New York,11.68,64.49,1013.76,NORMAL,0.89,1.17,0.52,1,False,1 +2026-05-06 09:00:00,Boston,10.07,59.32,1016.91,NORMAL,1.38,7.0,1.65,1,False,1 +2026-05-06 09:00:00,Chicago,2.48,72.99,1009.12,NORMAL,6.8,0.37,1.59,1,False,1 +2026-05-06 09:00:00,College Park,14.25,74.35,1016.21,NORMAL,1.11,7.43,2.29,1,False,1 +2026-05-06 09:10:00,New York,12.32,64.88,1009.81,NORMAL,0.12,1.02,3.54,1,False,1 +2026-05-06 09:10:00,Boston,9.91,77.51,1011.25,NORMAL,0.81,11.7,5.02,1,False,1 +2026-05-06 09:10:00,Chicago,-3.24,58.26,1011.31,ALERT,11.04,16.33,1.35,1,False,1 +2026-05-06 09:10:00,College Park,17.64,57.93,1012.86,NORMAL,4.17,9.72,1.39,1,False,1 +2026-05-06 09:20:00,New York,8.65,61.97,1012.61,NORMAL,3.14,1.5,0.16,1,False,1 +2026-05-06 09:20:00,Boston,13.82,57.87,1006.87,NORMAL,4.2,9.6,9.32,1,False,1 +2026-05-06 09:20:00,Chicago,6.1,79.36,1012.76,NORMAL,1.7,4.77,2.8,1,False,1 +2026-05-06 09:20:00,College Park,7.14,61.68,1015.07,ALERT,7.58,1.61,1.6,1,False,1 +2026-05-06 09:30:00,New York,13.13,67.1,1009.67,NORMAL,2.38,2.11,3.39,1,False,1 +2026-05-06 09:30:00,Boston,13.87,68.34,1012.22,NORMAL,4.14,2.33,1.77,1,False,1 +2026-05-06 09:30:00,Chicago,16.67,50.33,1015.82,ALERT,10.22,25.08,5.27,1,False,1 +2026-05-06 09:30:00,College Park,12.82,74.26,1013.67,NORMAL,1.9,10.97,0.2,1,False,1 +2026-05-06 09:40:00,New York,16.56,67.89,1010.51,NORMAL,4.33,3.66,1.27,1,False,1 +2026-05-06 09:40:00,Boston,6.5,63.09,1012.72,NORMAL,4.34,2.31,0.76,1,False,1 +2026-05-06 09:40:00,Chicago,4.66,81.54,1007.79,NORMAL,1.79,6.13,2.76,1,False,1 +2026-05-06 09:40:00,College Park,13.99,50.67,1024.47,NORMAL,0.87,14.81,11.62,1,False,1 +2026-05-06 09:50:00,New York,14.95,49.95,1012.33,NORMAL,2.48,15.32,1.06,1,False,1 +2026-05-06 09:50:00,Boston,7.97,76.86,1015.12,NORMAL,2.86,11.63,3.13,1,False,1 +2026-05-06 09:50:00,Chicago,24.53,84.3,999.64,ALERT,18.0,8.28,10.23,1,True,1 +2026-05-06 09:50:00,College Park,15.82,69.98,1016.18,NORMAL,2.28,4.87,0.3,1,False,1 +2026-05-06 10:00:00,New York,13.92,57.26,1016.89,NORMAL,0.8,5.1,5.9,1,False,1 +2026-05-06 10:00:00,Boston,4.12,68.9,1006.66,NORMAL,6.29,0.17,4.98,1,False,1 +2026-05-06 10:00:00,Chicago,8.51,64.24,1006.44,NORMAL,1.98,11.78,3.43,1,False,1 +2026-05-06 10:00:00,College Park,18.44,71.12,1017.59,NORMAL,3.54,5.68,0.91,1,False,1 +2026-05-06 10:10:00,New York,14.62,64.91,1010.13,NORMAL,1.18,4.08,2.27,1,False,1 +2026-05-06 10:10:00,Boston,8.47,60.66,1004.2,NORMAL,0.79,6.35,6.52,1,False,1 +2026-05-06 10:10:00,Chicago,9.28,79.31,1014.56,NORMAL,2.35,4.92,4.94,1,False,1 +2026-05-06 10:10:00,College Park,17.4,76.77,1011.22,NORMAL,1.66,11.98,5.73,1,False,1 +2026-05-06 10:20:00,New York,13.08,55.49,1008.66,NORMAL,1.56,5.93,3.25,1,False,1 +2026-05-06 10:20:00,Boston,8.28,71.17,1011.54,NORMAL,0.09,3.6,1.36,1,False,1 +2026-05-06 10:20:00,Chicago,9.37,70.93,1008.89,NORMAL,3.16,4.56,1.24,1,False,1 +2026-05-06 10:20:00,College Park,7.81,84.67,1015.26,ALERT,7.88,16.11,1.37,1,False,1 +2026-05-06 10:30:00,New York,27.77,86.91,998.41,ALERT,13.14,27.81,13.29,-1,True,1 +2026-05-06 10:30:00,Boston,15.97,66.88,1016.47,ALERT,8.9,1.26,6.42,1,False,1 +2026-05-06 10:30:00,Chicago,10.96,73.79,1010.58,NORMAL,3.38,1.29,0.49,1,False,1 +2026-05-06 10:30:00,College Park,1.56,84.76,1005.32,ALERT,14.13,16.2,11.31,1,True,1 +2026-05-06 10:40:00,New York,23.96,83.93,992.84,ALERT,9.33,24.83,18.86,-1,True,1 +2026-05-06 10:40:00,Boston,16.37,63.14,1013.86,ALERT,9.3,5.0,3.81,1,False,1 +2026-05-06 10:40:00,Chicago,14.51,64.42,1009.28,NORMAL,5.95,9.54,0.37,1,False,1 +2026-05-06 10:40:00,College Park,25.72,89.2,1005.19,ALERT,10.03,20.64,11.44,1,True,1 +2026-05-06 10:50:00,New York,18.53,58.78,1009.67,NORMAL,3.9,0.32,2.03,1,False,1 +2026-05-06 10:50:00,Boston,8.8,45.96,1015.9,NORMAL,1.73,22.18,5.85,1,False,1 +2026-05-06 10:50:00,Chicago,6.52,65.22,1008.6,NORMAL,4.01,5.32,1.35,1,False,1 +2026-05-06 10:50:00,College Park,6.76,63.8,1007.85,ALERT,8.93,4.76,8.78,1,False,1 +2026-05-06 11:00:00,New York,13.06,71.79,1012.03,NORMAL,1.96,14.51,0.49,1,False,1 +2026-05-06 11:00:00,Boston,13.72,76.65,1014.96,NORMAL,6.19,11.94,4.28,1,False,1 +2026-05-06 11:00:00,Chicago,-5.53,87.57,997.9,ALERT,15.66,16.84,12.48,-1,True,1 +2026-05-06 11:00:00,College Park,9.71,68.83,1016.42,NORMAL,5.98,0.27,0.21,1,False,1 +2026-05-06 11:10:00,New York,-3.48,100.0,1000.87,ALERT,18.12,38.35,10.61,-1,True,1 +2026-05-06 11:10:00,Boston,8.2,72.45,1018.87,NORMAL,0.48,7.78,8.22,1,False,1 +2026-05-06 11:10:00,Chicago,13.06,74.68,1003.37,NORMAL,2.93,3.95,7.01,1,False,1 +2026-05-06 11:10:00,College Park,8.52,83.25,1008.3,NORMAL,6.55,15.78,8.88,1,True,1 +2026-05-06 11:20:00,New York,15.49,55.85,1013.75,NORMAL,0.85,5.8,2.27,1,False,1 +2026-05-06 11:20:00,Boston,12.38,78.28,1016.51,NORMAL,2.89,12.9,3.42,1,False,1 +2026-05-06 11:20:00,Chicago,2.07,67.48,1005.78,ALERT,8.81,2.33,2.36,1,False,1 +2026-05-06 11:20:00,College Park,15.38,70.12,1008.21,NORMAL,0.31,2.65,8.97,1,False,1 +2026-05-06 11:30:00,New York,8.13,67.26,1014.43,NORMAL,6.83,5.9,3.58,1,False,1 +2026-05-06 11:30:00,Boston,15.06,81.61,1004.77,NORMAL,4.78,12.71,10.79,1,False,1 +2026-05-06 11:30:00,Chicago,11.76,64.59,1008.51,NORMAL,0.88,5.22,0.37,1,False,1 +2026-05-06 11:30:00,College Park,6.69,61.99,1009.03,ALERT,8.66,9.37,4.89,1,False,1 +2026-05-06 11:40:00,New York,12.0,66.83,1013.31,NORMAL,1.66,5.0,1.6,1,False,1 +2026-05-06 11:40:00,Boston,10.8,82.16,1008.51,NORMAL,0.83,11.17,5.69,1,False,1 +2026-05-06 11:40:00,Chicago,-9.75,87.04,995.35,ALERT,21.11,18.5,12.72,-1,True,1 +2026-05-06 11:40:00,College Park,15.88,82.94,1016.53,NORMAL,0.53,11.58,2.61,1,False,1 +2026-05-06 11:50:00,New York,3.89,54.16,1008.11,ALERT,9.55,9.94,4.53,1,False,1 +2026-05-06 11:50:00,Boston,1.95,69.15,1017.33,ALERT,10.08,9.08,4.61,1,False,1 +2026-05-06 11:50:00,Chicago,14.64,68.03,1012.02,NORMAL,3.28,0.51,3.95,1,False,1 +2026-05-06 11:50:00,College Park,16.71,72.47,1009.17,NORMAL,1.35,1.49,4.82,1,False,1 +2026-05-06 12:00:00,New York,7.72,72.2,1015.06,NORMAL,5.72,8.1,2.42,1,False,1 +2026-05-06 12:00:00,Boston,6.44,68.63,1013.98,NORMAL,5.59,9.6,1.26,1,False,1 +2026-05-06 12:00:00,Chicago,5.94,72.29,1010.94,NORMAL,6.16,4.9,2.58,1,False,1 +2026-05-06 12:00:00,College Park,13.19,70.95,1016.11,NORMAL,1.83,3.28,3.8,1,False,1 +2026-05-06 12:10:00,New York,13.65,64.37,1015.11,NORMAL,2.37,2.42,1.39,1,False,1 +2026-05-06 12:10:00,Boston,2.94,69.91,1006.33,ALERT,7.64,6.72,6.2,1,False,1 +2026-05-06 12:10:00,Chicago,5.46,75.52,1009.3,NORMAL,4.92,6.56,0.61,1,False,1 +2026-05-06 12:10:00,College Park,17.11,66.8,1006.07,NORMAL,2.94,6.26,7.22,1,False,1 +2026-05-06 12:20:00,New York,14.2,68.48,1013.64,NORMAL,2.8,3.18,0.69,1,False,1 +2026-05-06 12:20:00,Boston,11.98,61.76,1007.27,NORMAL,1.4,14.87,5.26,1,False,1 +2026-05-06 12:20:00,Chicago,5.11,66.43,1011.52,NORMAL,5.06,4.59,2.69,1,False,1 +2026-05-06 12:20:00,College Park,21.64,72.11,1006.76,NORMAL,5.99,0.55,4.46,1,False,1 +2026-05-06 12:30:00,New York,18.84,65.88,1016.03,ALERT,7.7,1.95,1.72,1,False,1 +2026-05-06 12:30:00,Boston,7.31,67.34,1011.92,NORMAL,4.02,7.15,1.71,1,False,1 +2026-05-06 12:30:00,Chicago,24.68,96.78,1001.26,ALERT,16.1,27.41,9.2,-1,True,1 +2026-05-06 12:30:00,College Park,3.8,89.13,1002.69,ALERT,13.11,16.08,8.24,1,True,1 +2026-05-06 12:40:00,New York,18.07,70.81,1014.23,NORMAL,6.93,2.98,0.08,1,False,1 +2026-05-06 12:40:00,Boston,15.83,66.8,1012.68,NORMAL,5.51,5.5,3.39,1,False,1 +2026-05-06 12:40:00,Chicago,10.95,76.36,1011.64,NORMAL,2.37,6.99,1.18,1,False,1 +2026-05-06 12:40:00,College Park,20.8,82.51,1000.89,NORMAL,3.89,9.46,10.04,1,True,1 +2026-05-06 12:50:00,New York,12.16,56.7,1019.51,NORMAL,0.97,11.84,5.24,1,False,1 +2026-05-06 12:50:00,Boston,22.58,95.48,999.52,ALERT,12.11,26.14,11.35,-1,True,1 +2026-05-06 12:50:00,Chicago,4.43,73.53,1012.61,NORMAL,3.99,1.8,1.53,1,False,1 +2026-05-06 12:50:00,College Park,11.83,76.24,1012.61,NORMAL,5.08,3.19,1.68,1,False,1 +2026-05-06 13:00:00,New York,6.36,64.78,1006.31,NORMAL,6.8,1.73,9.2,1,False,1 +2026-05-06 13:00:00,Boston,12.38,78.12,1011.84,NORMAL,1.91,8.78,0.97,1,False,1 +2026-05-06 13:00:00,Chicago,15.47,63.31,1007.84,ALERT,9.09,9.52,3.36,1,False,1 +2026-05-06 13:00:00,College Park,17.33,73.66,1016.33,NORMAL,1.23,1.95,6.19,1,False,1 +2026-05-06 13:10:00,New York,11.75,63.47,1008.43,NORMAL,1.14,1.56,5.33,1,False,1 +2026-05-06 13:10:00,Boston,18.38,80.09,1009.63,ALERT,7.59,11.56,1.91,1,False,1 +2026-05-06 13:10:00,Chicago,-1.33,61.51,1012.22,ALERT,7.71,11.32,1.02,1,False,1 +2026-05-06 13:10:00,College Park,12.79,78.13,1013.25,NORMAL,3.43,6.18,1.67,1,False,1 +2026-05-06 13:20:00,New York,11.35,69.55,1016.68,NORMAL,1.16,4.7,4.26,1,False,1 +2026-05-06 13:20:00,Boston,6.69,72.43,1007.3,NORMAL,4.1,3.9,4.24,1,False,1 +2026-05-06 13:20:00,Chicago,9.55,74.94,1003.77,NORMAL,3.17,2.11,7.43,1,False,1 +2026-05-06 13:20:00,College Park,12.25,63.13,1006.88,NORMAL,3.89,10.26,4.12,1,False,1 +2026-05-06 13:30:00,New York,7.94,72.02,1011.68,NORMAL,4.0,6.96,1.35,1,False,1 +2026-05-06 13:30:00,Boston,14.71,57.93,1007.21,NORMAL,3.87,11.36,2.99,1,False,1 +2026-05-06 13:30:00,Chicago,13.5,64.83,1009.7,NORMAL,6.4,8.53,0.07,1,False,1 +2026-05-06 13:30:00,College Park,9.32,63.8,1014.68,NORMAL,5.85,8.85,3.51,1,False,1 +2026-05-06 13:40:00,New York,0.07,89.07,1000.17,ALERT,9.84,23.77,12.35,-1,True,1 +2026-05-06 13:40:00,Boston,14.26,72.32,1013.22,NORMAL,2.88,3.8,3.03,1,False,1 +2026-05-06 13:40:00,Chicago,8.47,67.36,1012.79,NORMAL,0.24,3.86,2.94,1,False,1 +2026-05-06 13:40:00,College Park,14.63,76.6,1015.34,NORMAL,1.93,5.61,2.59,1,False,1 +2026-05-06 13:50:00,New York,6.84,72.11,1013.26,NORMAL,3.07,6.81,0.74,1,False,1 +2026-05-06 13:50:00,Boston,8.51,77.97,1017.07,NORMAL,4.26,8.45,6.62,1,False,1 +2026-05-06 13:50:00,Chicago,5.02,70.39,1007.27,NORMAL,4.36,1.01,2.83,1,False,1 +2026-05-06 13:50:00,College Park,7.88,58.09,1008.51,NORMAL,5.38,12.97,4.79,1,False,1 +2026-05-06 14:00:00,New York,13.03,54.75,1012.06,NORMAL,4.18,13.64,0.79,1,False,1 +2026-05-06 14:00:00,Boston,9.4,59.43,1009.41,NORMAL,1.91,12.32,1.92,1,False,1 +2026-05-06 14:00:00,Chicago,13.78,64.9,1001.99,NORMAL,5.59,5.31,7.24,1,False,1 +2026-05-06 14:00:00,College Park,12.95,54.82,1008.05,NORMAL,1.58,13.13,3.68,1,False,1 +2026-05-06 14:10:00,New York,-5.6,89.46,1002.27,ALERT,15.78,23.08,10.15,-1,True,1 +2026-05-06 14:10:00,Boston,11.35,58.63,1014.64,NORMAL,0.64,9.39,3.8,1,False,1 +2026-05-06 14:10:00,Chicago,19.17,75.64,1012.98,ALERT,9.11,7.16,5.88,1,False,1 +2026-05-06 14:10:00,College Park,19.83,62.23,1012.17,ALERT,8.42,1.06,1.48,1,False,1 +2026-05-06 14:20:00,New York,14.15,59.74,1013.46,NORMAL,3.97,6.64,1.04,1,False,1 +2026-05-06 14:20:00,Boston,15.63,53.83,1007.01,NORMAL,3.98,11.43,5.3,1,False,1 +2026-05-06 14:20:00,Chicago,18.19,72.33,1010.26,ALERT,8.13,3.85,3.16,1,False,1 +2026-05-06 14:20:00,College Park,17.97,65.9,1010.13,NORMAL,6.56,2.61,0.56,1,False,1 +2026-05-06 14:30:00,New York,8.14,66.71,1012.76,NORMAL,2.52,1.08,0.67,1,False,1 +2026-05-06 14:30:00,Boston,10.13,64.84,1015.46,NORMAL,1.7,0.4,3.19,1,False,1 +2026-05-06 14:30:00,Chicago,0.79,80.52,995.46,ALERT,9.27,12.04,11.64,-1,True,1 +2026-05-06 14:30:00,College Park,13.76,64.38,1016.92,NORMAL,1.21,0.54,5.58,1,False,1 +2026-05-06 14:40:00,New York,17.23,60.75,1007.18,ALERT,7.21,4.32,5.46,1,False,1 +2026-05-06 14:40:00,Boston,7.69,66.3,1018.14,NORMAL,3.31,3.36,5.42,1,False,1 +2026-05-06 14:40:00,Chicago,10.09,73.39,1004.52,NORMAL,0.03,4.91,2.58,1,False,1 +2026-05-06 14:40:00,College Park,11.37,71.83,1013.33,NORMAL,2.07,7.87,1.54,1,False,1 +2026-05-06 14:50:00,New York,12.46,60.67,1018.8,NORMAL,2.44,4.4,6.16,1,False,1 +2026-05-06 14:50:00,Boston,14.82,78.59,1009.67,NORMAL,3.98,17.98,3.26,1,False,1 +2026-05-06 14:50:00,Chicago,13.78,79.68,1004.32,NORMAL,3.61,11.51,2.93,1,False,1 +2026-05-06 14:50:00,College Park,18.05,59.83,1010.4,NORMAL,5.26,3.17,0.99,1,False,1 +2026-05-06 15:00:00,New York,27.13,84.6,1004.07,ALERT,16.21,21.8,10.0,1,True,1 +2026-05-06 15:00:00,Boston,8.32,54.43,1014.57,NORMAL,3.6,10.01,1.59,1,False,1 +2026-05-06 15:00:00,Chicago,-1.84,67.77,999.29,ALERT,12.07,3.37,6.89,1,False,1 +2026-05-06 15:00:00,College Park,2.28,80.92,1000.83,ALERT,12.54,17.57,10.94,1,True,1 +2026-05-06 15:10:00,New York,18.31,60.7,1015.57,ALERT,7.39,2.1,1.5,1,False,1 +2026-05-06 15:10:00,Boston,11.37,67.58,1008.37,NORMAL,0.05,3.98,4.6,1,False,1 +2026-05-06 15:10:00,Chicago,6.97,76.93,1016.31,NORMAL,3.26,5.79,10.13,1,False,1 +2026-05-06 15:10:00,College Park,14.51,67.4,1016.26,NORMAL,0.31,4.05,4.49,1,False,1 +2026-05-06 15:20:00,New York,6.85,64.07,1013.16,NORMAL,4.07,1.27,0.91,1,False,1 +2026-05-06 15:20:00,Boston,6.23,62.11,1014.53,NORMAL,4.24,4.24,1.29,1,False,1 +2026-05-06 15:20:00,Chicago,10.59,68.45,1009.76,NORMAL,0.66,4.61,2.88,1,False,1 +2026-05-06 15:20:00,College Park,25.79,96.29,1002.09,ALERT,10.66,30.42,11.32,-1,True,1 +2026-05-06 15:30:00,New York,15.34,64.88,1016.54,NORMAL,4.41,3.69,2.49,1,False,1 +2026-05-06 15:30:00,Boston,13.1,77.29,1009.7,NORMAL,3.41,11.49,3.36,1,False,1 +2026-05-06 15:30:00,Chicago,14.03,64.88,1004.46,NORMAL,2.99,7.79,2.92,1,False,1 +2026-05-06 15:30:00,College Park,14.04,68.41,1017.01,NORMAL,1.09,2.54,3.6,1,False,1 +2026-05-06 15:40:00,New York,12.77,73.2,1005.14,NORMAL,1.38,9.99,9.8,1,False,1 +2026-05-06 15:40:00,Boston,6.41,66.32,1016.71,NORMAL,4.36,1.68,5.34,1,False,1 +2026-05-06 15:40:00,Chicago,11.77,64.52,1011.36,NORMAL,0.68,8.15,3.49,1,False,1 +2026-05-06 15:40:00,College Park,15.97,79.15,1016.29,NORMAL,1.62,12.78,1.51,1,False,1 +2026-05-06 15:50:00,New York,11.35,67.15,1014.43,NORMAL,0.24,1.24,1.15,1,False,1 +2026-05-06 15:50:00,Boston,6.69,74.03,1005.74,NORMAL,2.4,8.48,7.04,1,False,1 +2026-05-06 15:50:00,Chicago,8.69,74.99,1006.57,NORMAL,2.74,4.1,2.67,1,False,1 +2026-05-06 15:50:00,College Park,-3.82,92.93,997.25,ALERT,18.61,23.61,17.41,-1,True,1 +2026-05-06 16:00:00,New York,7.0,53.1,1017.79,NORMAL,4.75,12.89,4.18,1,False,1 +2026-05-06 16:00:00,Boston,25.38,89.86,997.51,ALERT,16.62,20.39,13.5,-1,True,1 +2026-05-06 16:00:00,Chicago,0.28,63.49,1002.56,ALERT,10.13,6.46,7.13,1,False,1 +2026-05-06 16:00:00,College Park,10.25,70.15,1022.83,NORMAL,4.54,0.83,8.17,1,False,1 +2026-05-06 16:10:00,New York,12.04,56.48,1016.32,NORMAL,1.38,8.0,2.91,1,False,1 +2026-05-06 16:10:00,Boston,8.38,65.83,1011.47,NORMAL,0.38,3.64,0.46,1,False,1 +2026-05-06 16:10:00,Chicago,1.47,78.18,1004.66,ALERT,8.94,8.23,5.03,1,False,1 +2026-05-06 16:10:00,College Park,9.11,72.75,1014.06,NORMAL,5.45,3.76,2.5,1,False,1 +2026-05-06 16:20:00,New York,20.78,63.49,1010.66,ALERT,9.08,0.53,3.38,1,False,1 +2026-05-06 16:20:00,Boston,12.62,71.44,1016.11,NORMAL,4.46,2.32,4.48,1,False,1 +2026-05-06 16:20:00,Chicago,1.87,67.13,1009.64,ALERT,8.54,2.82,0.05,1,False,1 +2026-05-06 16:20:00,College Park,8.97,66.22,1015.85,NORMAL,3.81,5.35,1.44,1,False,1 +2026-05-06 16:30:00,New York,11.87,65.67,1011.61,NORMAL,0.17,2.71,2.43,1,False,1 +2026-05-06 16:30:00,Boston,7.4,59.46,1011.55,NORMAL,2.04,11.52,0.4,1,False,1 +2026-05-06 16:30:00,Chicago,2.84,70.36,1006.3,ALERT,7.57,0.41,3.39,1,False,1 +2026-05-06 16:30:00,College Park,4.06,84.43,992.82,ALERT,7.61,13.09,24.39,-1,True,1 +2026-05-06 16:40:00,New York,14.35,58.49,1015.49,NORMAL,3.34,4.63,2.43,1,False,1 +2026-05-06 16:40:00,Boston,-1.34,90.97,1002.16,ALERT,9.64,23.55,10.16,-1,True,1 +2026-05-06 16:40:00,Chicago,10.18,58.39,1015.08,NORMAL,0.23,11.56,5.39,1,False,1 +2026-05-06 16:40:00,College Park,12.89,68.61,1012.19,NORMAL,1.22,2.73,5.02,1,False,1 +2026-05-06 16:50:00,New York,10.43,59.15,1014.22,NORMAL,0.89,1.03,0.91,1,False,1 +2026-05-06 16:50:00,Boston,9.63,75.45,1006.26,NORMAL,1.33,8.03,6.06,1,False,1 +2026-05-06 16:50:00,Chicago,7.13,80.83,1011.45,NORMAL,3.92,14.58,2.0,1,False,1 +2026-05-06 16:50:00,College Park,14.54,69.0,1016.13,NORMAL,3.1,2.38,0.11,1,False,1 +2026-05-06 17:00:00,New York,11.29,57.93,1014.5,NORMAL,0.15,0.65,0.59,1,False,1 +2026-05-06 17:00:00,Boston,5.79,68.69,1013.99,NORMAL,3.15,0.55,3.76,1,False,1 +2026-05-06 17:00:00,Chicago,11.54,64.91,1008.3,NORMAL,1.18,3.81,1.48,1,False,1 +2026-05-06 17:00:00,College Park,11.22,73.5,1020.55,NORMAL,0.07,4.15,4.34,1,False,1 +2026-05-06 17:10:00,New York,13.15,62.44,1017.43,NORMAL,1.15,2.9,3.0,1,False,1 +2026-05-06 17:10:00,Boston,6.59,63.41,1015.34,NORMAL,2.17,4.76,3.46,1,False,1 +2026-05-06 17:10:00,Chicago,18.36,71.38,1008.1,ALERT,8.5,2.65,2.45,1,False,1 +2026-05-06 17:10:00,College Park,16.89,66.08,1014.65,NORMAL,5.54,3.94,1.11,1,False,1 +2026-05-06 17:20:00,New York,11.55,63.88,1018.07,NORMAL,0.67,3.14,3.42,1,False,1 +2026-05-06 17:20:00,Boston,10.67,60.62,1020.15,NORMAL,2.26,7.07,7.5,1,False,1 +2026-05-06 17:20:00,Chicago,-0.59,57.24,1006.71,ALERT,10.45,11.49,3.84,1,False,1 +2026-05-06 17:20:00,College Park,14.7,62.47,1018.49,NORMAL,1.8,6.21,2.62,1,False,1 +2026-05-06 17:30:00,New York,10.4,66.28,1009.35,NORMAL,1.75,5.9,6.59,1,False,1 +2026-05-06 17:30:00,Boston,6.16,78.44,1008.07,NORMAL,1.86,12.91,5.39,1,False,1 +2026-05-06 17:30:00,Chicago,-2.27,58.11,1014.84,ALERT,12.13,10.62,4.29,1,False,1 +2026-05-06 17:30:00,College Park,12.52,79.2,1016.36,NORMAL,1.53,11.27,0.04,1,False,1 +2026-05-06 17:40:00,New York,28.18,82.14,1002.63,ALERT,16.82,20.2,12.08,1,True,1 +2026-05-06 17:40:00,Boston,17.74,62.28,1007.69,ALERT,9.97,7.04,5.07,1,False,1 +2026-05-06 17:40:00,Chicago,11.23,63.65,1009.08,NORMAL,1.37,5.08,1.47,1,False,1 +2026-05-06 17:40:00,College Park,7.66,64.26,1013.91,NORMAL,6.31,5.79,3.33,1,False,1 +2026-05-06 17:50:00,New York,9.59,67.27,1011.9,NORMAL,1.77,5.33,2.81,1,False,1 +2026-05-06 17:50:00,Boston,9.23,70.06,1013.27,NORMAL,1.46,0.74,0.51,1,False,1 +2026-05-06 17:50:00,Chicago,21.11,86.79,996.1,ALERT,11.36,18.24,14.0,-1,True,1 +2026-05-06 17:50:00,College Park,14.49,57.54,1007.63,NORMAL,1.89,11.56,9.16,1,False,1 +2026-05-06 18:00:00,New York,11.58,66.58,1017.08,NORMAL,0.38,3.02,2.83,1,False,1 +2026-05-06 18:00:00,Boston,10.77,77.74,1010.21,NORMAL,3.08,9.5,3.95,1,False,1 +2026-05-06 18:00:00,Chicago,12.96,66.59,1004.87,NORMAL,3.21,1.96,5.23,1,False,1 +2026-05-06 18:00:00,College Park,5.3,75.56,1016.51,ALERT,7.95,9.65,2.3,1,False,1 +2026-05-06 18:10:00,New York,9.4,49.8,1012.65,NORMAL,1.85,15.49,2.12,1,False,1 +2026-05-06 18:10:00,Boston,8.77,55.12,1011.31,NORMAL,0.09,14.93,2.1,1,False,1 +2026-05-06 18:10:00,Chicago,-8.12,89.76,995.8,ALERT,18.73,22.89,13.96,-1,True,1 +2026-05-06 18:10:00,College Park,16.94,56.89,1016.65,NORMAL,3.69,9.02,2.44,1,False,1 +2026-05-06 18:20:00,New York,17.65,63.48,1007.68,ALERT,7.15,0.72,6.13,1,False,1 +2026-05-06 18:20:00,Boston,18.51,79.0,1004.68,ALERT,9.39,10.6,7.92,1,False,1 +2026-05-06 18:20:00,Chicago,4.47,72.66,1007.42,NORMAL,6.14,5.79,2.34,1,False,1 +2026-05-06 18:20:00,College Park,15.03,66.73,1008.88,NORMAL,1.77,2.66,5.73,1,False,1 +2026-05-06 18:30:00,New York,9.13,62.98,1012.2,NORMAL,1.37,0.22,1.61,1,False,1 +2026-05-06 18:30:00,Boston,9.07,76.1,1013.79,NORMAL,0.05,7.7,1.19,1,False,1 +2026-05-06 18:30:00,Chicago,7.39,68.95,1009.18,NORMAL,2.08,0.78,0.96,1,False,1 +2026-05-06 18:30:00,College Park,12.93,68.06,1009.98,NORMAL,0.4,3.14,2.71,1,False,1 +2026-05-06 18:40:00,New York,15.84,67.84,1017.52,NORMAL,5.82,5.26,4.88,1,False,1 +2026-05-06 18:40:00,Boston,16.77,68.24,1014.67,ALERT,7.97,3.25,3.34,1,False,1 +2026-05-06 18:40:00,Chicago,-8.44,88.27,999.73,ALERT,17.96,20.92,8.04,-1,True,1 +2026-05-06 18:40:00,College Park,7.9,64.61,1017.67,NORMAL,5.51,1.91,6.26,1,False,1 +2026-05-06 18:50:00,New York,7.58,75.57,1010.85,NORMAL,3.53,12.68,3.42,1,False,1 +2026-05-06 18:50:00,Boston,13.66,69.44,1017.9,NORMAL,4.86,2.05,6.57,1,False,1 +2026-05-06 18:50:00,Chicago,22.25,81.93,995.26,ALERT,12.73,14.58,12.51,-1,True,1 +2026-05-06 18:50:00,College Park,12.2,65.55,1011.19,NORMAL,1.26,2.78,0.97,1,False,1 +2026-05-06 19:00:00,New York,10.87,72.52,1010.34,NORMAL,0.16,7.97,3.72,1,False,1 +2026-05-06 19:00:00,Boston,14.86,59.25,1005.04,NORMAL,4.56,10.44,8.26,1,False,1 +2026-05-06 19:00:00,Chicago,18.57,94.73,998.79,ALERT,9.05,27.38,8.98,-1,True,1 +2026-05-06 19:00:00,College Park,28.48,87.56,1000.47,ALERT,15.48,23.19,12.4,-1,True,1 +2026-05-06 19:10:00,New York,14.91,66.83,1009.1,NORMAL,4.35,1.09,3.61,1,False,1 +2026-05-06 19:10:00,Boston,25.23,80.11,1001.37,ALERT,13.8,12.58,10.28,1,True,1 +2026-05-06 19:10:00,Chicago,0.32,69.65,1014.83,ALERT,9.2,2.3,7.06,1,False,1 +2026-05-06 19:10:00,College Park,15.6,58.66,1015.24,NORMAL,2.6,5.71,2.37,1,False,1 +2026-05-06 19:20:00,New York,-0.26,87.22,1004.16,ALERT,11.93,18.07,7.84,1,True,1 +2026-05-06 19:20:00,Boston,5.92,63.66,1006.5,NORMAL,5.51,3.87,5.15,1,False,1 +2026-05-06 19:20:00,Chicago,9.93,73.04,1009.91,NORMAL,0.41,5.69,2.14,1,False,1 +2026-05-06 19:20:00,College Park,6.19,60.64,1014.39,NORMAL,6.54,4.08,1.8,1,False,1 +2026-05-06 19:30:00,New York,16.93,72.37,1010.37,NORMAL,5.26,3.22,1.63,1,False,1 +2026-05-06 19:30:00,Boston,12.97,65.45,1016.81,NORMAL,2.51,0.74,5.9,1,False,1 +2026-05-06 19:30:00,Chicago,13.53,76.45,1008.89,NORMAL,4.33,7.47,0.8,1,False,1 +2026-05-06 19:30:00,College Park,16.78,79.6,1017.77,NORMAL,5.82,16.1,4.08,1,False,1 +2026-05-06 19:40:00,New York,15.61,74.97,1005.87,NORMAL,2.38,3.94,5.77,1,False,1 +2026-05-06 19:40:00,Boston,12.24,73.66,1013.37,NORMAL,0.94,6.88,1.36,1,False,1 +2026-05-06 19:40:00,Chicago,5.5,73.43,1009.97,NORMAL,4.16,1.89,1.92,1,False,1 +2026-05-06 19:40:00,College Park,15.32,67.91,1016.86,NORMAL,3.59,2.1,1.61,1,False,1 +2026-05-06 19:50:00,New York,17.39,90.66,1007.83,NORMAL,4.21,18.21,1.48,1,True,1 +2026-05-06 19:50:00,Boston,16.18,61.96,1009.08,NORMAL,4.25,4.33,2.84,1,False,1 +2026-05-06 19:50:00,Chicago,8.34,73.57,1003.6,NORMAL,0.18,0.66,5.47,1,False,1 +2026-05-06 19:50:00,College Park,20.6,80.25,1014.83,ALERT,7.38,13.78,0.26,1,False,1 +2026-05-06 20:00:00,New York,11.84,61.53,1008.3,NORMAL,1.34,10.92,1.01,1,False,1 +2026-05-06 20:00:00,Boston,6.5,59.51,1014.22,NORMAL,5.93,5.29,4.06,1,False,1 +2026-05-06 20:00:00,Chicago,24.34,87.28,997.99,ALERT,15.4,14.19,10.32,-1,True,1 +2026-05-06 20:00:00,College Park,8.96,59.8,1015.87,NORMAL,4.26,6.67,0.78,1,False,1 +2026-05-06 20:10:00,New York,12.31,62.66,1014.06,NORMAL,1.72,6.98,5.26,1,False,1 +2026-05-06 20:10:00,Boston,6.61,77.16,1011.47,NORMAL,4.15,12.31,0.53,1,False,1 +2026-05-06 20:10:00,Chicago,11.7,71.15,1009.94,NORMAL,2.76,1.94,1.63,1,False,1 +2026-05-06 20:10:00,College Park,22.13,88.8,1000.25,ALERT,9.56,23.48,15.78,-1,True,1 +2026-05-06 20:20:00,New York,14.89,66.05,1010.86,NORMAL,0.57,1.62,1.32,1,False,1 +2026-05-06 20:20:00,Boston,14.45,62.52,1008.04,NORMAL,3.55,5.03,4.95,1,False,1 +2026-05-06 20:20:00,Chicago,8.05,73.06,1011.67,NORMAL,1.75,0.47,3.21,1,False,1 +2026-05-06 20:20:00,College Park,14.11,53.16,1014.97,NORMAL,1.54,12.16,1.06,1,False,1 +2026-05-06 20:30:00,New York,18.35,69.14,1007.77,NORMAL,4.03,1.62,2.12,1,False,1 +2026-05-06 20:30:00,Boston,12.11,74.33,1007.02,NORMAL,0.91,7.37,4.22,1,False,1 +2026-05-06 20:30:00,Chicago,18.47,91.88,994.9,ALERT,9.05,18.35,13.91,-1,True,1 +2026-05-06 20:30:00,College Park,29.22,85.92,1003.4,ALERT,16.95,21.7,12.57,1,True,1 +2026-05-06 20:40:00,New York,27.93,87.42,996.48,ALERT,13.33,20.55,12.89,-1,True,1 +2026-05-06 20:40:00,Boston,8.41,74.9,1017.26,NORMAL,2.76,7.8,7.29,1,False,1 +2026-05-06 20:40:00,Chicago,8.16,60.74,1011.23,NORMAL,1.26,12.79,2.42,1,False,1 +2026-05-06 20:40:00,College Park,14.5,67.04,1011.04,NORMAL,2.23,2.82,4.93,1,False,1 +2026-05-06 20:50:00,New York,9.14,61.33,1010.76,NORMAL,5.46,5.54,1.39,1,False,1 +2026-05-06 20:50:00,Boston,8.9,78.82,1017.87,NORMAL,0.72,9.14,6.27,1,False,1 +2026-05-06 20:50:00,Chicago,8.74,75.44,1007.91,NORMAL,0.39,5.05,1.37,1,False,1 +2026-05-06 20:50:00,College Park,11.95,65.63,1011.75,NORMAL,1.98,0.13,3.55,1,False,1 diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_bash.sh b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_bash.sh new file mode 100755 index 000000000..0025e81f4 --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_bash.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# """ +# This script launches a Docker container with an interactive bash shell for +# development. +# """ + +# Exit immediately if any command exits with a non-zero status. +set -e + +# Import the utility functions from the project template. +GIT_ROOT=$(git rev-parse --show-toplevel) +source $GIT_ROOT/class_project/project_template/utils.sh + +# Parse default args (-h, -v) and enable set -x if -v is passed. +parse_default_args "$@" + +# Load Docker configuration variables for this script. +get_docker_vars_script ${BASH_SOURCE[0]} +source $DOCKER_NAME +print_docker_vars + +# List the available Docker images matching the expected image name. +run "docker image ls $FULL_IMAGE_NAME" + +# Configure and run the Docker container with interactive bash shell. +# - Container is removed automatically on exit (--rm) +# - Interactive mode with TTY allocation (-ti) +# - Port forwarding for Jupyter or other services +# - Git root mounted to /git_root inside container +CONTAINER_NAME=${IMAGE_NAME}_bash +PORT= +DOCKER_CMD=$(get_docker_bash_command) +DOCKER_CMD_OPTS=$(get_docker_bash_options $CONTAINER_NAME $PORT) +run "$DOCKER_CMD $DOCKER_CMD_OPTS $FULL_IMAGE_NAME" diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_build.sh b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_build.sh new file mode 100755 index 000000000..eec97fec2 --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_build.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# """ +# Build a Docker container image for the Flink weather anomaly project. +# +# This script sets up the build environment with error handling and command +# tracing, loads Docker configuration from docker_name.sh, and builds the +# Docker image using the build_container_image utility function. +# """ + +# Exit immediately if any command exits with a non-zero status. +set -e + +# Import the utility functions. +GIT_ROOT=$(git rev-parse --show-toplevel) +source $GIT_ROOT/class_project/project_template/utils.sh + +# Parse default args (-h, -v) and enable set -x if -v is passed. +parse_default_args "$@" +shift $((OPTIND-1)) + +# Load Docker configuration variables (REPO_NAME, IMAGE_NAME, FULL_IMAGE_NAME). +get_docker_vars_script ${BASH_SOURCE[0]} +source $DOCKER_NAME +print_docker_vars + +# Enable BuildKit for improved build performance. +export DOCKER_BUILDKIT=1 +export DOCKER_BUILD_MULTI_ARCH=0 + +# Build the container image. +build_container_image "$@" diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_build.version.log b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_build.version.log new file mode 100644 index 000000000..8315eefe2 --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_build.version.log @@ -0,0 +1 @@ +the input device is not a TTY diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_clean.sh b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_clean.sh new file mode 100755 index 000000000..7e40839ae --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_clean.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# """ +# Remove Docker container image for the project. +# +# This script cleans up Docker images by removing the container image +# matching the project configuration. Useful for freeing disk space or +# ensuring a fresh build. +# """ + +# Exit immediately if any command exits with a non-zero status. +set -e + +# Import the utility functions. +GIT_ROOT=$(git rev-parse --show-toplevel) +source $GIT_ROOT/class_project/project_template/utils.sh + +# Parse default args (-h, -v) and enable set -x if -v is passed. +parse_default_args "$@" + +# Load Docker configuration variables for this script. +get_docker_vars_script ${BASH_SOURCE[0]} +source $DOCKER_NAME +print_docker_vars + +# Remove the container image. +remove_container_image diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_cmd.sh b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_cmd.sh new file mode 100755 index 000000000..906d7a77b --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_cmd.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# """ +# Execute a command in a Docker container. +# +# This script runs a specified command inside a new Docker container instance. +# The container is removed automatically after the command completes. The +# git root is mounted to /git_root inside the container. +# """ + +# Exit immediately if any command exits with a non-zero status. +set -e + +# Import the utility functions. +GIT_ROOT=$(git rev-parse --show-toplevel) +source $GIT_ROOT/class_project/project_template/utils.sh + +# Parse default args (-h, -v) and enable set -x if -v is passed. +# Shift processed option flags so remaining args form the command. +parse_default_args "$@" +shift $((OPTIND-1)) + +# Capture the command to execute from remaining arguments. +CMD="$@" +echo "Executing: '$CMD'" + +# Load Docker configuration variables for this script. +get_docker_vars_script ${BASH_SOURCE[0]} +source $DOCKER_NAME +print_docker_vars + +# List available Docker images matching the expected image name. +run "docker image ls $FULL_IMAGE_NAME" +#(docker manifest inspect $FULL_IMAGE_NAME | grep arch) || true + +# Configure and run the Docker container with the specified command. +CONTAINER_NAME=$IMAGE_NAME +DOCKER_CMD=$(get_docker_cmd_command) +PORT="" +DOCKER_RUN_OPTS="" +DOCKER_CMD_OPTS=$(get_docker_bash_options $CONTAINER_NAME $PORT $DOCKER_RUN_OPTS) +run "$DOCKER_CMD $DOCKER_CMD_OPTS $FULL_IMAGE_NAME bash -c '$CMD'" diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_exec.sh b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_exec.sh new file mode 100755 index 000000000..24f8e401a --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_exec.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# """ +# Execute a bash shell in a running Docker container. +# +# This script connects to an already running Docker container and opens an +# interactive bash session for debugging or inspection purposes. +# """ + +# Exit immediately if any command exits with a non-zero status. +set -e + +# Import the utility functions. +GIT_ROOT=$(git rev-parse --show-toplevel) +source $GIT_ROOT/class_project/project_template/utils.sh + +# Parse default args (-h, -v) and enable set -x if -v is passed. +parse_default_args "$@" + +# Load Docker configuration variables for this script. +get_docker_vars_script ${BASH_SOURCE[0]} +source $DOCKER_NAME +print_docker_vars + +# Execute bash shell in the running container. +exec_container diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_jupyter.sh b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_jupyter.sh new file mode 100755 index 000000000..1a60dfd3a --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_jupyter.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# """ +# Execute Jupyter Lab in a Docker container. +# +# This script launches a Docker container running Jupyter Lab with +# configurable port, directory mounting, and vim bindings. It passes +# command-line options to the run_jupyter.sh script inside the container. +# +# Usage: +# > docker_jupyter.sh [options] +# """ + +# Exit immediately if any command exits with a non-zero status. +set -e + +# Import the utility functions. +GIT_ROOT=$(git rev-parse --show-toplevel) +source $GIT_ROOT/class_project/project_template/utils.sh + +# Parse command-line options and set Jupyter configuration variables. +parse_docker_jupyter_args "$@" + +# Load Docker configuration variables for this script. +get_docker_vars_script ${BASH_SOURCE[0]} +source $DOCKER_NAME +print_docker_vars + +# List available Docker images and inspect architecture. +list_and_inspect_docker_image + +# Run the Docker container with Jupyter Lab. +CMD=$(get_run_jupyter_cmd "${BASH_SOURCE[0]}" "$OLD_CMD_OPTS") +CONTAINER_NAME=$IMAGE_NAME +# Kill existing container if -f flag is set. +kill_existing_container_if_forced + +DOCKER_CMD=$(get_docker_jupyter_command) +DOCKER_CMD_OPTS=$(get_docker_jupyter_options $CONTAINER_NAME $JUPYTER_HOST_PORT $JUPYTER_USE_VIM) +run "$DOCKER_CMD $DOCKER_CMD_OPTS $FULL_IMAGE_NAME $CMD" diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_name.sh b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_name.sh new file mode 100644 index 000000000..245a450f1 --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/docker_name.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# """ +# Define Docker image naming variables for the Flink weather anomaly project. +# +# This file defines the repository name, image name, and full image name +# variables used by all docker_*.sh scripts in this project. +# """ + +REPO_NAME="rajeshe6" +IMAGE_NAME="umd_flink_weather_anomaly" +FULL_IMAGE_NAME="$REPO_NAME/$IMAGE_NAME" diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/etc_sudoers b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/etc_sudoers new file mode 100644 index 000000000..ee0816a15 --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/etc_sudoers @@ -0,0 +1,31 @@ +# +# This file MUST be edited with the 'visudo' command as root. +# +# Please consider adding local content in /etc/sudoers.d/ instead of +# directly modifying this file. +# +# See the man page for details on how to write a sudoers file. +# +Defaults env_reset +Defaults mail_badpass +Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin" + +# Host alias specification + +# User alias specification + +# Cmnd alias specification + +# User privilege specification +root ALL=(ALL:ALL) ALL + +# Members of the admin group may gain root privileges +%admin ALL=(ALL) ALL + +# Allow members of group sudo to execute any command +%sudo ALL=(ALL:ALL) ALL + +# See sudoers(5) for more information on "#include" directives: +postgres ALL=(ALL) NOPASSWD:ALL + +#includedir /etc/sudoers.d diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/flink_weather_anomaly.API.ipynb b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/flink_weather_anomaly.API.ipynb new file mode 100644 index 000000000..90cb5e0a4 --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/flink_weather_anomaly.API.ipynb @@ -0,0 +1,863 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "3a4f189f", + "metadata": {}, + "source": [ + "# PyFlink DataStream API, Weather Anomaly Detection\n", + "\n", + "**Student:** Rajesh Easwaramoorthy, UMD ID 122242479 \n", + "**Course:** DATA605 Big Data Systems, Spring 2026 \n", + "**Project:** UmdTask459, Real-Time Weather Anomaly Detection with Apache Flink\n", + "\n", + "---\n", + "\n", + "This notebook is an **API tutorial** for the PyFlink DataStream API. It is designed to be read before the end-to-end example notebook (`flink_weather_anomaly.example.ipynb`). The goal is to build intuition for the core abstractions, `StreamExecutionEnvironment`, `DataStream`, `MapFunction`, `FilterFunction`, and the Flink type system, before applying them to a real anomaly-detection pipeline.\n", + "\n", + "Official PyFlink documentation: https://nightlies.apache.org/flink/flink-docs-release-2.0/docs/dev/python/overview/" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "bb2ee4f2", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:45:54.431465Z", + "iopub.status.busy": "2026-05-07T05:45:54.431321Z", + "iopub.status.idle": "2026-05-07T05:45:54.704274Z", + "shell.execute_reply": "2026-05-07T05:45:54.703811Z" + } + }, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "4d6cd7f5", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:45:54.705110Z", + "iopub.status.busy": "2026-05-07T05:45:54.705001Z", + "iopub.status.idle": "2026-05-07T05:45:54.726147Z", + "shell.execute_reply": "2026-05-07T05:45:54.725792Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2026-05-07 05:45:54,723 INFO PyFlink DataStream API tutorial ready.\n" + ] + } + ], + "source": [ + "import logging\n", + "import json\n", + "from pyflink.datastream import StreamExecutionEnvironment, MapFunction, FilterFunction\n", + "from pyflink.common.typeinfo import Types\n", + "\n", + "# Configure root logger for notebook output.\n", + "logging.basicConfig(\n", + " level=logging.INFO,\n", + " format=\"%(asctime)s %(levelname)s %(message)s\"\n", + ")\n", + "logger = logging.getLogger(__name__)\n", + "logger.info(\"PyFlink DataStream API tutorial ready.\")" + ] + }, + { + "cell_type": "markdown", + "id": "2fa0497a", + "metadata": {}, + "source": [ + "## Introduction\n", + "\n", + "### What is Apache Flink?\n", + "\n", + "Apache Flink is a **distributed stream-processing framework** designed for low-latency, high-throughput event processing. Unlike batch frameworks (MapReduce, Spark in batch mode), Flink treats streams as the primary abstraction and offers exactly-once semantics, stateful computation, and event-time windowing out of the box.\n", + "\n", + "Key design properties:\n", + "- **Dataflow model**: computation is expressed as a directed acyclic graph of operators connected by streams.\n", + "- **State management**: operators can maintain local state that is checkpointed to durable storage.\n", + "- **Time semantics**: Flink distinguishes event time, ingestion time, and processing time.\n", + "\n", + "### Why PyFlink?\n", + "\n", + "PyFlink is the Python API for Flink. It exposes the same DataStream and Table APIs as the Java/Scala SDK, wrapped with a Python interface. This makes it natural to integrate with the scientific Python ecosystem, pandas, scikit-learn, matplotlib, while offloading the actual dataflow execution to the JVM-based Flink runtime.\n", + "\n", + "### How it fits stream processing\n", + "\n", + "In this project, weather sensor readings arrive as JSON strings. A Flink pipeline parses each record, runs an Isolation Forest model to score it, and emits a result record annotated with an anomaly flag. The pipeline is stateful: it maintains a rolling window of recent readings per city to compute deviation features. This notebook introduces each API building block required to assemble that pipeline." + ] + }, + { + "cell_type": "markdown", + "id": "7f5cd373", + "metadata": {}, + "source": [ + "## Section 1: StreamExecutionEnvironment\n", + "\n", + "The `StreamExecutionEnvironment` is the entry point to every Flink program. It holds configuration (parallelism, checkpointing, restart strategy) and is the factory for all `DataStream` sources. Nothing executes until `env.execute()` is called, until then, you are building a **logical execution plan**." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "f94389f7", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:45:54.726927Z", + "iopub.status.busy": "2026-05-07T05:45:54.726875Z", + "iopub.status.idle": "2026-05-07T05:45:55.531281Z", + "shell.execute_reply": "2026-05-07T05:45:55.530817Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2026-05-07 05:45:55,417 INFO Using Any for unsupported type: typing.Sequence[~T]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2026-05-07 05:45:55,481 INFO No module named google.cloud.bigquery_storage_v1. As a result, the ReadFromBigQuery transform *CANNOT* be used with `method=DIRECT_READ`.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Configured parallelism: 1\n" + ] + } + ], + "source": [ + "# Create the execution environment.\n", + "env = StreamExecutionEnvironment.get_execution_environment()\n", + "# Set parallelism to 1 so all operators run on a single thread, convenient for local testing.\n", + "env.set_parallelism(1)\n", + "logger.info(\"Execution environment created with parallelism=1.\")\n", + "# Inspect the configured parallelism.\n", + "print(f\"Configured parallelism: {env.get_parallelism()}\")" + ] + }, + { + "cell_type": "markdown", + "id": "714ad115", + "metadata": {}, + "source": [ + "**What `set_parallelism(1)` does.** Parallelism controls how many parallel instances (sub-tasks) each operator runs with. Setting it to 1 forces every operator in the graph to run as a single-threaded task. This is the right setting for local development and for this project, where state is managed in a single Python dict rather than distributed key-value store." + ] + }, + { + "cell_type": "markdown", + "id": "3de87774", + "metadata": {}, + "source": [ + "## Section 2: Creating DataStreams\n", + "\n", + "The simplest way to create a `DataStream` in a local Flink job is `env.from_collection()`. This method wraps a Python iterable into a Flink source. The `type_info` argument tells Flink the element type so it can choose an efficient serializer.\n", + "\n", + "Flink's `Types` class (in `pyflink.common.typeinfo`) provides primitive descriptors: `Types.INT()`, `Types.LONG()`, `Types.FLOAT()`, `Types.STRING()`, `Types.BOOLEAN()`, and composites like `Types.TUPLE(...)` and `Types.ROW(...)`." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "3934aad8", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:45:55.532618Z", + "iopub.status.busy": "2026-05-07T05:45:55.532163Z", + "iopub.status.idle": "2026-05-07T05:45:55.614115Z", + "shell.execute_reply": "2026-05-07T05:45:55.613700Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DataStreams created, pipeline not yet executed.\n", + "int_stream type : \n", + "status_stream type: \n" + ] + } + ], + "source": [ + "# Create a fresh environment for this section to avoid re-using a spent one.\n", + "env = StreamExecutionEnvironment.get_execution_environment()\n", + "env.set_parallelism(1)\n", + "# Build an integer DataStream from a small collection.\n", + "int_stream = env.from_collection(\n", + " collection=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],\n", + " type_info=Types.INT()\n", + ")\n", + "logger.info(\"Integer DataStream created.\")\n", + "# Build a string DataStream representing raw weather status codes.\n", + "status_stream = env.from_collection(\n", + " collection=[\"NORMAL\", \"ALERT\", \"NORMAL\", \"ALERT\", \"NORMAL\"],\n", + " type_info=Types.STRING()\n", + ")\n", + "logger.info(\"String DataStream created.\")\n", + "print(\"DataStreams created, pipeline not yet executed.\")\n", + "print(f\"int_stream type : {type(int_stream)}\")\n", + "print(f\"status_stream type: {type(status_stream)}\")" + ] + }, + { + "cell_type": "markdown", + "id": "1af85931", + "metadata": {}, + "source": [ + "Notice that `from_collection()` returns a `DataStream` object immediately, but **no data has flowed yet**. The collection is registered as a source node in the logical plan. Actual element emission happens only when the job is submitted via `env.execute()`.\n", + "\n", + "In a production deployment, the source would be a Kafka consumer, a file source, or a network socket, `from_collection()` is a convenience for testing and tutorials." + ] + }, + { + "cell_type": "markdown", + "id": "82682d74", + "metadata": {}, + "source": [ + "## Section 3: Map Transformation\n", + "\n", + "The `map()` transformation applies a function to every element of a stream and emits one output element per input element. It is the workhorse of stateless stream processing.\n", + "\n", + "PyFlink accepts two styles:\n", + "1. **Lambda / plain Python function**, quick to write, no class boilerplate.\n", + "2. **`MapFunction` subclass**, required when you need `open()` / `close()` lifecycle hooks (e.g., to load a model once per task instance rather than once per record)." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "f7afb068", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:45:55.614844Z", + "iopub.status.busy": "2026-05-07T05:45:55.614787Z", + "iopub.status.idle": "2026-05-07T05:45:55.661049Z", + "shell.execute_reply": "2026-05-07T05:45:55.660614Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lambda map: each integer will be doubled.\n", + "Expected output when executed: 2, 4, 6, 8, 10\n" + ] + } + ], + "source": [ + "env = StreamExecutionEnvironment.get_execution_environment()\n", + "env.set_parallelism(1)\n", + "# Double every integer using a lambda.\n", + "doubled_stream = (\n", + " env.from_collection([1, 2, 3, 4, 5], type_info=Types.INT())\n", + " .map(lambda x: x * 2, output_type=Types.INT())\n", + ")\n", + "logger.info(\"Lambda map transformation registered.\")\n", + "print(\"Lambda map: each integer will be doubled.\")\n", + "print(\"Expected output when executed: 2, 4, 6, 8, 10\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "25926eda", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:45:55.661760Z", + "iopub.status.busy": "2026-05-07T05:45:55.661699Z", + "iopub.status.idle": "2026-05-07T05:45:55.708159Z", + "shell.execute_reply": "2026-05-07T05:45:55.707744Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MapFunction subclass registered.\n", + "Expected output when executed: BALTIMORE, COLLEGE PARK, ANNAPOLIS\n" + ] + } + ], + "source": [ + "class UpperCaseMap(MapFunction):\n", + " \"\"\"Convert each string element to upper-case.\n", + "\n", + " Parameters\n", + " ----------\n", + " None, this function carries no configuration state.\n", + "\n", + " Returns\n", + " -------\n", + " str\n", + " The input string converted to upper-case.\n", + " \"\"\"\n", + " def map(self, value: str) -> str:\n", + " \"\"\"Apply upper-case conversion to one element.\n", + "\n", + " Parameters\n", + " ----------\n", + " value : str\n", + " Raw string element from the upstream DataStream.\n", + "\n", + " Returns\n", + " -------\n", + " str\n", + " Upper-cased version of value.\n", + " \"\"\"\n", + " return value.upper()\n", + "env2 = StreamExecutionEnvironment.get_execution_environment()\n", + "env2.set_parallelism(1)\n", + "upper_stream = (\n", + " env2.from_collection([\"baltimore\", \"college park\", \"annapolis\"], type_info=Types.STRING())\n", + " .map(UpperCaseMap(), output_type=Types.STRING())\n", + ")\n", + "logger.info(\"UpperCaseMap transformation registered.\")\n", + "print(\"MapFunction subclass registered.\")\n", + "print(\"Expected output when executed: BALTIMORE, COLLEGE PARK, ANNAPOLIS\")" + ] + }, + { + "cell_type": "markdown", + "id": "75d59856", + "metadata": {}, + "source": [ + "The `MapFunction` class separates the **what** (the `map()` method) from the **lifecycle** (`open()` and `close()`). This matters for expensive resources, a machine-learning model loaded once in `open()` is shared across all elements handled by that task instance, which is critical for throughput in our anomaly-detection pipeline." + ] + }, + { + "cell_type": "markdown", + "id": "6693a424", + "metadata": {}, + "source": [ + "## Section 4: Filter Transformation\n", + "\n", + "`filter()` keeps elements for which the predicate returns `True` and discards the rest. The output type is identical to the input type, no schema change occurs. Filtering is commonly used to discard warm-up records, remove malformed JSON, or isolate a city subset for downstream processing." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "8e76ef43", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:45:55.708844Z", + "iopub.status.busy": "2026-05-07T05:45:55.708786Z", + "iopub.status.idle": "2026-05-07T05:45:55.753191Z", + "shell.execute_reply": "2026-05-07T05:45:55.752808Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Filter: keeping even integers only.\n", + "Expected output: 2, 4, 6, 8, 10\n" + ] + } + ], + "source": [ + "env = StreamExecutionEnvironment.get_execution_environment()\n", + "env.set_parallelism(1)\n", + "# Keep only even integers.\n", + "even_stream = (\n", + " env.from_collection(list(range(1, 11)), type_info=Types.INT())\n", + " .filter(lambda x: x % 2 == 0)\n", + ")\n", + "logger.info(\"Even-number filter registered.\")\n", + "print(\"Filter: keeping even integers only.\")\n", + "print(\"Expected output: 2, 4, 6, 8, 10\")" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "f19e03f7", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:45:55.753919Z", + "iopub.status.busy": "2026-05-07T05:45:55.753845Z", + "iopub.status.idle": "2026-05-07T05:45:55.808071Z", + "shell.execute_reply": "2026-05-07T05:45:55.807689Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "FilterFunction registered, only ALERT records will pass.\n", + "Expected records downstream: College Park (47.3) and Rockville (51.0)\n" + ] + } + ], + "source": [ + "class AlertFilter(FilterFunction):\n", + " \"\"\"Pass only ALERT status records through the stream.\n", + "\n", + " Parameters\n", + " ----------\n", + " None.\n", + "\n", + " Returns\n", + " -------\n", + " bool\n", + " True if the record status is ALERT, False otherwise.\n", + " \"\"\"\n", + " def filter(self, value: str) -> bool:\n", + " \"\"\"Evaluate whether an element should pass downstream.\n", + "\n", + " Parameters\n", + " ----------\n", + " value : str\n", + " A JSON-encoded weather record string.\n", + "\n", + " Returns\n", + " -------\n", + " bool\n", + " True when the 'status' field equals 'ALERT'.\n", + " \"\"\"\n", + " record = json.loads(value)\n", + " return record.get(\"status\") == \"ALERT\"\n", + "env2 = StreamExecutionEnvironment.get_execution_environment()\n", + "env2.set_parallelism(1)\n", + "sample_json = [\n", + " json.dumps({\"city\": \"Baltimore\", \"temperature\": 22.1, \"status\": \"NORMAL\"}),\n", + " json.dumps({\"city\": \"College Park\", \"temperature\": 47.3, \"status\": \"ALERT\"}),\n", + " json.dumps({\"city\": \"Annapolis\", \"temperature\": 20.5, \"status\": \"NORMAL\"}),\n", + " json.dumps({\"city\": \"Rockville\", \"temperature\": 51.0, \"status\": \"ALERT\"}),\n", + "]\n", + "alert_stream = (\n", + " env2.from_collection(sample_json, type_info=Types.STRING())\n", + " .filter(AlertFilter())\n", + ")\n", + "logger.info(\"AlertFilter registered on JSON stream.\")\n", + "print(\"FilterFunction registered, only ALERT records will pass.\")\n", + "print(\"Expected records downstream: College Park (47.3) and Rockville (51.0)\")" + ] + }, + { + "cell_type": "markdown", + "id": "4c75a2f7", + "metadata": {}, + "source": [ + "Applying `filter()` early in the pipeline is a key optimization: discarding irrelevant records before expensive transformations (such as model inference) reduces CPU and memory pressure significantly in high-throughput scenarios." + ] + }, + { + "cell_type": "markdown", + "id": "8345566a", + "metadata": {}, + "source": [ + "## Section 5: Chaining Transformations\n", + "\n", + "Flink transformations are composable, each method on a `DataStream` returns a new `DataStream`, enabling fluent method chaining. The Flink planner fuses adjacent operators into a single **operator chain** (also called a task) when possible, eliminating serialization overhead between them.\n", + "\n", + "The example below builds a three-step pipeline:\n", + "1. Parse each integer as a float.\n", + "2. Keep only values above a threshold.\n", + "3. Format the result as a labelled string." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "f62bff42", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:45:55.808845Z", + "iopub.status.busy": "2026-05-07T05:45:55.808784Z", + "iopub.status.idle": "2026-05-07T05:45:55.874234Z", + "shell.execute_reply": "2026-05-07T05:45:55.873830Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Chained pipeline: int → Celsius-to-Fahrenheit → filter >75F → label.\n", + "Input values (Celsius): 0, 2, 4, 6, 8, 10, 12, 14, 16, 18\n", + "Expected output (Fahrenheit > 75): temp=77.0F, temp=82.4F, temp=87.8F, ...\n" + ] + } + ], + "source": [ + "env = StreamExecutionEnvironment.get_execution_environment()\n", + "env.set_parallelism(1)\n", + "# Build a chained pipeline: scale → filter → label.\n", + "chained_stream = (\n", + " env.from_collection(list(range(0, 20, 2)), type_info=Types.INT())\n", + " .map(lambda x: float(x) * 1.8 + 32.0, output_type=Types.FLOAT()) # Celsius to Fahrenheit.\n", + " .filter(lambda f: f > 75.0) # Keep warm readings.\n", + " .map(lambda f: f\"temp={f:.1f}F\", output_type=Types.STRING()) # Format result.\n", + ")\n", + "logger.info(\"Three-stage chained pipeline registered.\")\n", + "print(\"Chained pipeline: int → Celsius-to-Fahrenheit → filter >75F → label.\")\n", + "print(\"Input values (Celsius): 0, 2, 4, 6, 8, 10, 12, 14, 16, 18\")\n", + "print(\"Expected output (Fahrenheit > 75): temp=77.0F, temp=82.4F, temp=87.8F, ...\")" + ] + }, + { + "cell_type": "markdown", + "id": "4acc2005", + "metadata": {}, + "source": [ + "Flink's execution graph for this job contains three operator nodes. Because all three satisfy the operator-chaining criteria (same parallelism, no shuffle boundary), the Flink scheduler fuses them into a single task. The element flows through the full chain without intermediate serialization." + ] + }, + { + "cell_type": "markdown", + "id": "0548f920", + "metadata": {}, + "source": [ + "## Section 6: Custom MapFunction with State\n", + "\n", + "When a transformation needs to accumulate information across multiple records, such as maintaining a running average, it must carry **operator state**. In PyFlink, the `MapFunction` subclass is the right place for this: instance variables set in `open()` persist across calls to `map()`.\n", + "\n", + "Note: for fault-tolerant state that survives task restarts, Flink provides managed state via `RuntimeContext`. The example here uses a plain Python variable, which is appropriate for local, single-task jobs without checkpointing." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "81e074f4", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:45:55.874921Z", + "iopub.status.busy": "2026-05-07T05:45:55.874866Z", + "iopub.status.idle": "2026-05-07T05:45:55.920187Z", + "shell.execute_reply": "2026-05-07T05:45:55.919790Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "RunningAverageMap pipeline registered.\n", + "First few expected output lines (approximations):\n", + " value=22.1 running_avg=22.10\n", + " value=23.4 running_avg=22.75\n", + " value=19.8 running_avg=21.77\n", + " value=45.0 running_avg=27.57\n", + " value=21.3 running_avg=26.32\n" + ] + } + ], + "source": [ + "class RunningAverageMap(MapFunction):\n", + " \"\"\"Compute a running (cumulative) average of a numeric stream.\n", + "\n", + " State is held in plain Python instance variables initialized in\n", + " ``open()``. Each call to ``map()`` updates the count and sum and\n", + " emits the current mean alongside the raw value.\n", + "\n", + " Parameters\n", + " ----------\n", + " None, state is fully internal.\n", + "\n", + " Returns\n", + " -------\n", + " str\n", + " A formatted string ``\"value= running_avg=\"``.\n", + " \"\"\"\n", + " def open(self, runtime_context) -> None:\n", + " \"\"\"Initialize accumulators when the operator starts.\n", + "\n", + " Parameters\n", + " ----------\n", + " runtime_context : RuntimeContext\n", + " Provided by Flink; used here only to call super.\n", + "\n", + " Returns\n", + " -------\n", + " None\n", + " \"\"\"\n", + " self._count = 0\n", + " self._total = 0.0\n", + " def map(self, value: float) -> str:\n", + " \"\"\"Update the running average and return an annotated string.\n", + "\n", + " Parameters\n", + " ----------\n", + " value : float\n", + " The next numeric observation from the upstream stream.\n", + "\n", + " Returns\n", + " -------\n", + " str\n", + " Formatted string showing the raw value and current average.\n", + " \"\"\"\n", + " self._count += 1\n", + " self._total += value\n", + " avg = self._total / self._count\n", + " return f\"value={value:.1f} running_avg={avg:.2f}\"\n", + "temperatures = [22.1, 23.4, 19.8, 45.0, 21.3, 20.7, 22.9, 48.2, 23.1, 22.5]\n", + "env = StreamExecutionEnvironment.get_execution_environment()\n", + "env.set_parallelism(1)\n", + "avg_stream = (\n", + " env.from_collection(temperatures, type_info=Types.FLOAT())\n", + " .map(RunningAverageMap(), output_type=Types.STRING())\n", + ")\n", + "logger.info(\"RunningAverageMap registered on temperature stream.\")\n", + "print(\"RunningAverageMap pipeline registered.\")\n", + "print(\"First few expected output lines (approximations):\")\n", + "count, total = 0, 0.0\n", + "for t in temperatures[:5]:\n", + " count += 1\n", + " total += t\n", + " print(f\" value={t:.1f} running_avg={total/count:.2f}\")" + ] + }, + { + "cell_type": "markdown", + "id": "b272de1f", + "metadata": {}, + "source": [ + "The `open()` hook is called **once** per task instance when the operator starts, before any records are processed. This is where you should:\n", + "- Load machine-learning models from disk.\n", + "- Open database connections.\n", + "- Initialize lookup tables or caches.\n", + "\n", + "The Python side-effect pattern used here (appending to an external list) is a valid workaround for collecting results in a local Flink job without a dedicated sink operator." + ] + }, + { + "cell_type": "markdown", + "id": "8cce2294", + "metadata": {}, + "source": [ + "## Section 7: Type System\n", + "\n", + "Flink needs to know the schema of each stream so it can serialize elements efficiently (binary row format rather than Python pickle). The `Types` class provides descriptors for composite types:\n", + "\n", + "| Descriptor | Use case |\n", + "|---|---|\n", + "| `Types.INT()` | 32-bit integer |\n", + "| `Types.FLOAT()` | 32-bit float |\n", + "| `Types.DOUBLE()` | 64-bit float |\n", + "| `Types.STRING()` | Unicode string |\n", + "| `Types.BOOLEAN()` | Boolean |\n", + "| `Types.TUPLE([...])` | Fixed-length heterogeneous tuple |\n", + "| `Types.ROW([...])` | Named-column row (like a SQL row) |\n", + "\n", + "For this project, we use `Types.STRING()` throughout and carry JSON strings across operator boundaries. This is the simplest approach and avoids schema registration overhead at the cost of a JSON parse on every record." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "a36a2e3f", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:45:55.920937Z", + "iopub.status.busy": "2026-05-07T05:45:55.920880Z", + "iopub.status.idle": "2026-05-07T05:45:55.966217Z", + "shell.execute_reply": "2026-05-07T05:45:55.965762Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Tuple type descriptor : TupleTypeInfo(String, Float)\n", + "Row type descriptor : RowTypeInfo(String, Float, Float, Float)\n", + "Type descriptors instantiated, ready to attach to from_collection() or output_type=.\n" + ] + } + ], + "source": [ + "# Demonstrate Types.TUPLE for a (city, temperature) pair.\n", + "tuple_type = Types.TUPLE([Types.STRING(), Types.FLOAT()])\n", + "env = StreamExecutionEnvironment.get_execution_environment()\n", + "env.set_parallelism(1)\n", + "city_temp_stream = env.from_collection(\n", + " collection=[\n", + " (\"Baltimore\", 22.1),\n", + " (\"College Park\", 47.3),\n", + " (\"Annapolis\", 20.5),\n", + " ],\n", + " type_info=tuple_type\n", + ")\n", + "logger.info(\"Tuple-typed DataStream created.\")\n", + "print(f\"Tuple type descriptor : {tuple_type}\")\n", + "# Demonstrate Types.ROW for a weather record with named fields.\n", + "row_type = Types.ROW(\n", + " [Types.STRING(), Types.FLOAT(), Types.FLOAT(), Types.FLOAT()]\n", + ")\n", + "print(f\"Row type descriptor : {row_type}\")\n", + "print(\"Type descriptors instantiated, ready to attach to from_collection() or output_type=.\")" + ] + }, + { + "cell_type": "markdown", + "id": "078f7324", + "metadata": {}, + "source": [ + "When using `Types.ROW`, Flink stores elements as `Row` objects (not Python dicts). Field access uses `row[0]`, `row[1]`, etc. For the weather project, JSON strings are simpler to debug and inspect, so we favor `Types.STRING()` with explicit `json.loads()` calls inside each operator." + ] + }, + { + "cell_type": "markdown", + "id": "d4171cc8", + "metadata": {}, + "source": [ + "## Section 8: Weather Record Processing\n", + "\n", + "This section bridges the API tutorial to the full anomaly-detection example. We define a `MapFunction` that:\n", + "1. Parses a JSON weather record.\n", + "2. Computes a heat-index approximation from temperature and humidity.\n", + "3. Emits an enriched JSON record.\n", + "\n", + "This mirrors exactly how the production `flink_map_fn()` in `flink_weather_anomaly_utils.py` works, but without the Isolation Forest model call." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "ec906f79", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:45:55.966898Z", + "iopub.status.busy": "2026-05-07T05:45:55.966841Z", + "iopub.status.idle": "2026-05-07T05:45:58.476438Z", + "shell.execute_reply": "2026-05-07T05:45:58.475595Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Processed 5 records.\n", + "City Temp (C) Humidity% Heat Index\n", + "--------------------------------------------------\n", + "Baltimore 22.1 65.0 22.06\n", + "College Park 47.3 80.0 50.17\n", + "Annapolis 20.5 70.2 20.44\n", + "Rockville 51.0 55.0 53.59\n", + "Frederick 18.9 60.0 18.41\n" + ] + } + ], + "source": [ + "class WeatherEnrichMap(MapFunction):\n", + " \"\"\"Parse a JSON weather record and add a heat-index field.\"\"\"\n", + " def map(self, json_str: str) -> str:\n", + " record = json.loads(json_str)\n", + " T = record[\"temperature\"]\n", + " H = record[\"humidity\"]\n", + " # Steadman simplified heat-index (converted from Fahrenheit).\n", + " T_f = T * 9 / 5 + 32\n", + " hi_f = 0.5 * (T_f + 61.0 + (T_f - 68.0) * 1.2 + H * 0.094)\n", + " record[\"heat_index\"] = round((hi_f - 32) * 5 / 9, 2)\n", + " return json.dumps(record)\n", + "\n", + "weather_records = [\n", + " json.dumps({\"city\": \"Baltimore\", \"temperature\": 22.1, \"humidity\": 65.0, \"pressure\": 1012.3}),\n", + " json.dumps({\"city\": \"College Park\", \"temperature\": 47.3, \"humidity\": 80.0, \"pressure\": 995.1}),\n", + " json.dumps({\"city\": \"Annapolis\", \"temperature\": 20.5, \"humidity\": 70.2, \"pressure\": 1015.6}),\n", + " json.dumps({\"city\": \"Rockville\", \"temperature\": 51.0, \"humidity\": 55.0, \"pressure\": 988.4}),\n", + " json.dumps({\"city\": \"Frederick\", \"temperature\": 18.9, \"humidity\": 60.0, \"pressure\": 1018.2}),\n", + "]\n", + "\n", + "env = StreamExecutionEnvironment.get_execution_environment()\n", + "env.set_parallelism(1)\n", + "env.from_collection(weather_records, type_info=Types.STRING()).map(\n", + " WeatherEnrichMap(), output_type=Types.STRING()\n", + ")\n", + "env.execute(\"WeatherEnrichMap Demo\")\n", + "logger.info(\"WeatherEnrichMap job completed.\")\n", + "\n", + "# Compute enriched records directly for display.\n", + "enriched_results = [json.loads(WeatherEnrichMap().map(r)) for r in weather_records]\n", + "header = \"City Temp (C) Humidity% Heat Index\"\n", + "print(f\"\\nProcessed {len(enriched_results)} records.\")\n", + "print(header)\n", + "print(\"-\" * 50)\n", + "for r in enriched_results:\n", + " print(f\"{r['city']:<16} {r['temperature']:>9.1f} {r['humidity']:>10.1f} {r['heat_index']:>11.2f}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "10e1b115", + "metadata": {}, + "source": [ + "**Interpretation.** The five weather records are each processed by `WeatherEnrichMap.map()`, the Flink engine calls the function once per element in order (parallelism=1 guarantees ordering for `from_collection()`). High-temperature, high-humidity cities like College Park (47.3°C, 80%) show elevated heat-index values relative to their raw temperature, which reflects the physiological stress of humid heat.\n", + "\n", + "This enrichment pattern is exactly what the full anomaly pipeline in the example notebook uses, the only addition is that `flink_map_fn()` also scores the feature vector with the Isolation Forest and computes deviation deltas from a rolling city history." + ] + }, + { + "cell_type": "markdown", + "id": "ce9c2846", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "This notebook covered the core PyFlink DataStream API building blocks:\n", + "\n", + "| Concept | Key class / method | Learned |\n", + "|---|---|---|\n", + "| Entry point | `StreamExecutionEnvironment` | `get_execution_environment()`, `set_parallelism()`, `execute()` |\n", + "| Source | `env.from_collection()` | Wrap a Python list as a finite stream source |\n", + "| Map | `DataStream.map()`, `MapFunction` | Apply a function elementwise; subclass for lifecycle hooks |\n", + "| Filter | `DataStream.filter()`, `FilterFunction` | Discard elements that fail a predicate |\n", + "| Chaining | Fluent API | Compose multiple operators into one execution graph |\n", + "| Stateful map | `RunningAverageMap` | Maintain per-operator state via `open()` and instance variables |\n", + "| Type system | `Types.*` | Tell Flink how to serialize stream elements |\n", + "| Weather pipeline | `WeatherEnrichMap` | End-to-end JSON parse → compute → emit pattern |\n", + "\n", + "The next notebook, `flink_weather_anomaly.example.ipynb`, applies all of these concepts to a real anomaly-detection pipeline: loading a CSV data set, training an Isolation Forest model, running it inside a Flink `map()` operator, and evaluating detection performance with precision, recall, and F1 score.\n", + "\n", + "---\n", + "*Rajesh Easwaramoorthy, DATA605 Spring 2026, University of Maryland*" + ] + } + ], + "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.11.15" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/flink_weather_anomaly.example.ipynb b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/flink_weather_anomaly.example.ipynb new file mode 100644 index 000000000..7435a1241 --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/flink_weather_anomaly.example.ipynb @@ -0,0 +1,1333 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c5e97d91", + "metadata": {}, + "source": [ + "# Real-Time Weather Anomaly Detection with Apache Flink\n", + "\n", + "**Student:** Rajesh Easwaramoorthy, UMD ID 122242479 \n", + "**Course:** DATA605 Big Data Systems, Spring 2026 \n", + "**Project:** UmdTask459, Real-Time Weather Anomaly Detection with Apache Flink\n", + "\n", + "---\n", + "\n", + "## Project Overview\n", + "\n", + "This notebook demonstrates an end-to-end real-time weather anomaly detection system built with **PyFlink** and **scikit-learn**. Simulated weather sensor readings (temperature, humidity, atmospheric pressure) from multiple cities are processed through a Flink DataStream pipeline. Each record is scored by an **Isolation Forest** model, and records whose feature vectors deviate substantially from normal historical patterns are flagged as anomalies.\n", + "\n", + "### System Architecture\n", + "\n", + "```\n", + "CSV / Collection Source\n", + " │\n", + " ▼\n", + " Flink DataStream\n", + " │ map(flink_map_fn)\n", + " │ ├─ Parse JSON record\n", + " │ ├─ Compute rolling deviations (city_history)\n", + " │ ├─ Score with Isolation Forest\n", + " │ └─ Emit annotated JSON\n", + " ▼\n", + " Results DataFrame\n", + " │\n", + " ▼\n", + " EDA + Metrics\n", + "```\n", + "\n", + "### What This Notebook Demonstrates\n", + "\n", + "1. Loading and exploring sensor training data.\n", + "2. Training an Isolation Forest with a 5% contamination rate.\n", + "3. Building a PyFlink pipeline that scores incoming records in real time.\n", + "4. Evaluating detection performance against ground-truth injected anomaly labels.\n", + "5. Visualizing temporal patterns, city-level alert distributions, and feature deviations.\n", + "\n", + "> **Environment note:** This notebook runs entirely inside Docker without any live API or external InfluxDB connection. All data comes from files in the `data/` directory." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "97340a6e", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:58:49.716889Z", + "iopub.status.busy": "2026-05-07T05:58:49.716759Z", + "iopub.status.idle": "2026-05-07T05:58:50.011903Z", + "shell.execute_reply": "2026-05-07T05:58:50.011444Z" + } + }, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "df1f632c", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:58:50.012781Z", + "iopub.status.busy": "2026-05-07T05:58:50.012692Z", + "iopub.status.idle": "2026-05-07T05:58:50.651766Z", + "shell.execute_reply": "2026-05-07T05:58:50.651297Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2026-05-07 05:58:50,645 INFO All imports successful, notebook ready.\n" + ] + } + ], + "source": [ + "import json\n", + "import logging\n", + "from collections import defaultdict, deque\n", + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib.dates as mdates\n", + "import seaborn as sns\n", + "from sklearn.metrics import confusion_matrix, classification_report\n", + "from pyflink.datastream import StreamExecutionEnvironment\n", + "from pyflink.common.typeinfo import Types\n", + "import flink_weather_anomaly_utils as utils\n", + "\n", + "# Configure the root logger for notebook-friendly output.\n", + "logging.basicConfig(\n", + " level=logging.INFO,\n", + " format=\"%(asctime)s %(levelname)s %(message)s\"\n", + ")\n", + "logger = logging.getLogger(__name__)\n", + "# Set a consistent visual style for all plots.\n", + "sns.set_theme(style=\"whitegrid\", palette=\"tab10\", font_scale=1.05)\n", + "plt.rcParams[\"figure.dpi\"] = 110\n", + "logger.info(\"All imports successful, notebook ready.\")" + ] + }, + { + "cell_type": "markdown", + "id": "43506f42", + "metadata": {}, + "source": [ + "## Section 1: Load and Explore Training Data\n", + "\n", + "The training data (`data/weather_data.csv`) contains historical weather observations with three sensor channels: temperature (°C), relative humidity (%), and atmospheric pressure (hPa). These readings were collected from several cities over a simulated multi-day window.\n", + "\n", + "We explore the marginal distributions of each channel before training the Isolation Forest. Understanding the baseline distribution is essential: if temperature readings are heavily right-skewed, the model needs adequate training samples in the tail to avoid flagging legitimate heatwave readings as anomalies." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "cad7327f", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:58:50.652562Z", + "iopub.status.busy": "2026-05-07T05:58:50.652436Z", + "iopub.status.idle": "2026-05-07T05:58:50.693891Z", + "shell.execute_reply": "2026-05-07T05:58:50.693212Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2026-05-07 05:58:50,681 INFO Training data loaded: 1200 rows, 3 columns.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Head ===\n", + " temperature humidity pressure\n", + "0 15.05 82.27 1016.09\n", + "1 1.60 49.66 1009.53\n", + "2 19.50 73.64 1020.65\n", + "3 21.41 61.71 1009.23\n", + "4 -7.51 64.39 1009.18\n", + "5 -1.02 54.47 1016.25\n", + "6 13.28 61.66 1017.58\n", + "7 8.84 78.00 1015.69\n", + "8 11.83 70.83 1002.89\n", + "9 3.47 82.32 1016.23\n", + "\n", + "=== Descriptive Statistics ===\n", + " temperature humidity pressure\n", + "count 1200.00 1200.00 1200.00\n", + "mean 15.16 66.33 1012.35\n", + "std 10.41 13.10 5.84\n", + "min -26.57 19.53 993.39\n", + "25% 9.54 58.61 1008.73\n", + "50% 16.31 67.28 1012.69\n", + "75% 21.56 75.46 1016.16\n", + "max 46.48 100.00 1032.07\n" + ] + } + ], + "source": [ + "# Load the training data using the utility function.\n", + "df_train = utils.load_weather_data(\"data/weather_data.csv\")\n", + "logger.info(f\"Training data loaded: {df_train.shape[0]} rows, {df_train.shape[1]} columns.\")\n", + "print(\"=== Head ===\")\n", + "print(df_train.head(10).to_string())\n", + "print(\"\\n=== Descriptive Statistics ===\")\n", + "print(df_train.describe().round(2).to_string())" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "1360d37b", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:58:50.695057Z", + "iopub.status.busy": "2026-05-07T05:58:50.694941Z", + "iopub.status.idle": "2026-05-07T05:58:51.014884Z", + "shell.execute_reply": "2026-05-07T05:58:51.014441Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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ERERaSEm8ImIiIiIiIiIiIiItBAT+EREREREREREREREWogJfCIiIiIiIiIiIiIiLcQEPhERERERERERERGRFmICn4iIiIiIiIiIiIhICzGBT0RERERERERERESkhZjAJyIiIiIiIiIiIiLSQkzgExERERERERERERFpISbwiYiIiIiIiIiIiIi0EBP4RERERERERERERERaiAl8IiIiIiIiIiIiIiItxAQ+EREREREREREREZEWYgKfiIiIiIiIiIiIiEgLMYFPRERERERERERERKSFmMAnIiIiIiIiIiIiItJCTOATEREREREREREREWkhJvCJiIiIiIiIiIiIiLQQE/hERERERERERERERFqICXwiIiIiIiIiIiIiIi3EBD4RERERERERERERkRZiAp+IiIiIiIiIiIiISAsxgU9EREREREREREREpIWYwCciIiIiIiIiIiIi0kJM4BMRERERERERERERaSEm8ImIiIiIiIiIiIiItBAT+EREREREREREREREWogJfCIiIiIiIiIiIiIiLcQEPhERERERERERERGRFmICn4iIiIiIiIiIiIhICzGBT0RERERERERERESkhZjAJyIiIiIiIiIiIiLSQkzgExERERERERERERFpISbwiYiIiIiIiIiIiIi0EBP4RERERERERERERERaiAl8IiIiIiIiIiIiIiItxAQ+EREREREREREREZEWYgKfiIiIiIiIiIiIiEgLMYFPRERERERERERERKSFmMAnIiIiIiIiIiIiItJCTOATEREREREREREREWkhJvCJiIiIiIiIiIiIiLQQE/hERERERERERERERFqICXzSuBcvXsDW1harV68u1vXOnDkTtra2+S4rDpraB0UlFouxevVqeHp6omHDhhrZd6S9rl69CltbW0RERCh8PT09XWXr2r9/PxwdHfHixQuV1UlUVrAdZjusCuo+dra2tpg5c6ba6i9tPDw84OPjo1RZRee/pj8TI0eOxOjRo1VeL/sLRMWLfQzNf58Wljb1McqyNWvWoEWLFkhKSpIu09S5/LG1a9eiVatWMnGReulrOgAqPcLDwzFixAjpv3V1dWFiYoLKlSvDzs4OHTt2RJcuXWBoaKjSdV65cgUjR45EhQoVVFavOty9exenT5+Gl5cXatasqelwVGLfvn1Ys2YN+vfvD1dXV+jqKr4m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MvZFEW33xbp167B+/foS1/fmm29i2bJlSsvYF9rvh6tXryIyMhKXL19GRkYGCgoKULduXXTu3BmTJk1SFPVfZIj9wAI+EREREREREREREZEB4hj4REREREREREREREQGiAV8IiIiIiIiIiIiIiIDxAI+EREREREREREREZEBYgGfiIiIiIiIiIiIiMgAsYBPRERERERERERERGSAWMAnIiIiIiIiIiIiIjJALOATERERERERERERERkgFvCJiIiIiIiIiIiIiAwQC/hERERERERERERERAaIBXwiogoQBAGDBg3C6tWr1T4uk8kqvY4///wTrVu3xp9//lnptoiISD9++eUXuLm54ZdfftFqu3PnzkWPHj202qaxu3v3Ltzc3HDo0KEyY9etWwc3NzelZT169MDcuXN1lZ6KpKQktG7dGn///bdW2pPJZOjWrRt27dqllfaIiIjE8u2338LHxwfZ2dmKZT169EBoaKjecwkLC0N4eLje10tUGhbwifTMzc1Nox9NPoxWVX/99RfWrVuHu3fvip1KieLi4pCamooxY8YoLT979iy6deuGtm3bYvLkyUonIC86deoU3nnnHXTu3BkeHh7o0qULJk+ejO+//14RI5FI0K1bN6xdu1anr4WIyFQdOnRI6djbunVr+Pn5Ye7cuXj48KHe8zHE41/xNvr999/VPv7OO+9UmS8WHj58iHXr1uGPP/7QSfurVq1CYGAgGjdurFh26dIlvPnmm/D29kZwcDCSk5NVnrd9+3YMGDAABQUFSsutrKwwbtw4bN68GXl5eTrJmYiINKfuvKJbt26YN2+eKOcVxqKwsBBr1qzByJEj4eDgUO7nF188UfzTqlUrdOnSBWFhYWqPq2V555138O233+LGjRvlfi6RrlQTOwEiUxMREaH0+759+3D58mV8+umnSst9fHz0mZZB+euvv7B+/Xp06NABDRo0EDsdtbZv347evXvDyclJsez58+eYOXMmRo4cCQ8PD0RFRWHlypX43//+p4gRBAEfffQR9u3bBzc3NwQHB6NOnTrIzMzE6dOnMXXqVKxYsQIDBw4EAIwcORKTJk1CSkoKGjVqpO+XSURkEqZNmwZXV1fIZDL8/vvvOHz4MC5duoS4uDhYW1vrLY/Sjn+LFi2CIAh6y8UY1K9fH1euXEG1ahX7SHPixAmYmZkpfk9PT8f6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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2026-05-07 05:58:50,983 INFO Sensor distribution plot saved to logs/sensor_distributions.png.\n" + ] + } + ], + "source": [ + "# Plot marginal distributions of the three sensor channels.\n", + "fig, axes = plt.subplots(1, 3, figsize=(14, 4))\n", + "sensor_cols = [\n", + " (\"temperature\", \"Temperature (°C)\", \"steelblue\"),\n", + " (\"humidity\", \"Relative Humidity (%)\", \"seagreen\"),\n", + " (\"pressure\", \"Pressure (hPa)\", \"darkorange\"),\n", + "]\n", + "for ax, (col, xlabel, color) in zip(axes, sensor_cols):\n", + " ax.hist(df_train[col].dropna(), bins=40, color=color, edgecolor=\"white\", linewidth=0.5)\n", + " ax.set_xlabel(xlabel, fontsize=11)\n", + " ax.set_ylabel(\"Count\", fontsize=11)\n", + " ax.set_title(f\"Distribution of {xlabel}\", fontsize=12)\n", + " mean_val = df_train[col].mean()\n", + " ax.axvline(mean_val, color=\"black\", linestyle=\"--\", linewidth=1.2, label=f\"Mean={mean_val:.1f}\")\n", + " ax.legend(fontsize=9)\n", + "fig.suptitle(\"Training Data, Sensor Channel Distributions\", fontsize=13, y=1.02)\n", + "plt.tight_layout()\n", + "plt.savefig(\"logs/sensor_distributions.png\", bbox_inches=\"tight\")\n", + "plt.show()\n", + "logger.info(\"Sensor distribution plot saved to logs/sensor_distributions.png.\")" + ] + }, + { + "cell_type": "markdown", + "id": "7aaf2753", + "metadata": {}, + "source": [ + "**Interpretation.** Temperature shows a roughly symmetric bell shape centred around the local climate mean, with a right tail extending into the anomalous range. Humidity is more uniform across the operational range. Pressure is tightly concentrated (typical for sea-level-adjusted readings) with a narrow spread, large deviations from the mean are therefore particularly informative for anomaly detection.\n", + "\n", + "These distributions confirm that a density-based approach like Isolation Forest is appropriate: the normal regime occupies a dense, roughly ellipsoidal region in the three-dimensional feature space, while anomalies lie in sparse peripheral regions." + ] + }, + { + "cell_type": "markdown", + "id": "f7a625db", + "metadata": {}, + "source": [ + "## Section 2: Train Isolation Forest\n", + "\n", + "The **Isolation Forest** algorithm (Liu et al., 2008) detects anomalies by building an ensemble of random decision trees. Normal observations require many splits to isolate (they sit in dense regions of feature space), while anomalies are isolated in just a few splits. The anomaly score is inversely related to the average path length across all trees.\n", + "\n", + "Key hyperparameters:\n", + "- `n_estimators=200`, number of isolation trees; more trees give a more stable score estimate.\n", + "- `contamination=0.05`, expected fraction of anomalies in the training data; sets the decision threshold.\n", + "- `random_state=42`, ensures reproducibility." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "072e32a0", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:58:51.015702Z", + "iopub.status.busy": "2026-05-07T05:58:51.015638Z", + "iopub.status.idle": "2026-05-07T05:58:51.155994Z", + "shell.execute_reply": "2026-05-07T05:58:51.155479Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2026-05-07 05:58:51,028 INFO Training feature matrix shape: (1200, 3).\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2026-05-07 05:58:51,110 INFO Isolation Forest training complete.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2026-05-07 05:58:51,152 INFO Model saved to models/isolation_forest.pkl.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model type : IsolationForest\n", + "n_estimators : 200\n", + "contamination : 0.05\n", + "max_samples : 256\n" + ] + } + ], + "source": [ + "# Extract the feature matrix for model training.\n", + "feature_cols = [\"temperature\", \"humidity\", \"pressure\"]\n", + "X_train = df_train[feature_cols].dropna().values\n", + "logger.info(f\"Training feature matrix shape: {X_train.shape}.\")\n", + "# Train the Isolation Forest model via the utility wrapper.\n", + "model = utils.train_isolation_forest(X_train, contamination=0.05, n_estimators=200)\n", + "logger.info(\"Isolation Forest training complete.\")\n", + "print(f\"Model type : {type(model).__name__}\")\n", + "print(f\"n_estimators : {model.n_estimators}\")\n", + "print(f\"contamination : {model.contamination}\")\n", + "print(f\"max_samples : {model.max_samples_}\")\n", + "# Persist the trained model to disk.\n", + "utils.save_model(model, \"models/isolation_forest.pkl\")\n", + "logger.info(\"Model saved to models/isolation_forest.pkl.\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "1144db77", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:58:51.156802Z", + "iopub.status.busy": "2026-05-07T05:58:51.156732Z", + "iopub.status.idle": "2026-05-07T05:58:51.367040Z", + "shell.execute_reply": "2026-05-07T05:58:51.366597Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2026-05-07 05:58:51,187 INFO Training-set anomaly count: 60 (5.0%).\n" + ] + }, + { + "data": { + "image/png": "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GOmrtLo4AJQxN4EzVxnXeUySiYiIiIiIcqDK6uIA0N2zSoHHQgWPSTIRUTHRpk0bAEB4eLiWIyEiIiLSXUySiYiKidevX2s7BCIiIiKdx0dAEREREREREWVhkkxERERERESUhUkyERERERERURYmyURERERERERZmCQTERERERERZeHq1kRExcTXX3+t7RCIiIiIdB6TZCKiYmLUqFHaDoGIiIhI53G6NRERERFRIbOzLqXtEIgoGxxJJiIqJkJCQgAATk5OWo6EiIhyY2Kkj3vhMYhKSM61bC0H60KIiIikmCQTERUT3377LQBg6tSpWo6EiIhUEZWQjMNXnudarpqtZcEHQ0QynG5NRERERERElIVJMhEREREREVEWJslEREREREREWZgkExERERERaQBXLS8euHAXERERERGRBqi6ankZcxM4q7FquaoroatbLynHJJmIiIiIiEhDVFm1vLtnFY3XmZd6STkmyURExcTdu3e1HQIRERGRzuM9yURERERERERZmCQTERERERERZeF0ayKiYqJ79+4AgAcPHmg5EiIiIiLdxSSZiKiYePTokbZDICLSKQWxCjER6T4myURERERUIhXEKsREpPt4TzIRERERERFRFibJRERERERERFmYJBMRERERERFlYZJMRERERERElIULdxERFROjRo3SdghEREREOo9JMhFRMfH1119rOwQiIiIincfp1kRERERERERZOJJMRFRM/PnnnwAAJycnLUdCREREpLuYJBMRFRPjx48HAIwdO1bLkRARERHpLk63JiIiIiLKhp11KW2HQESFrMiNJD99+hSrV6/GnTt38O7dOwiCAHt7e3To0AHDhg2Dubm5XPkDBw4gMDAQz549g5WVFdq2bYvJkyfD0tJSS0dARERERNpyLzwGUQnJuZar5WCtUn0mRvoar5OIirYilyRHRkYiKioKHTp0gK2tLUQiEW7fvo1ffvkFp06dwp49e2BkZAQACAwMxIIFC9CsWTMMHjwYYWFh2Lp1K0JDQ7Fz505ZOSIiIiIqGaISknH4yvNcy1WzVX1ApSDqJKKiK19J8uvXr2FjYwMTExOl+5OTkxEdHQ07OzuV62zUqBEaNWqksL1atWpYvHgxzp07hzZt2iA6OhorV65E06ZNsXHjRohEIgBAjRo1MH36dOzbtw8DBw7M24ERERERERFRiZSve5Jbt26NkydPZrv/zJkzaN26dX6akKlYsSIAID4+HgBw+vRpJCUlYciQIbIEGQC6du2KMmXKICQkRCPtEhERERERUcmRr5FkQRBy3J+eni6XwKojOTkZiYmJSElJwd27d7Fs2TIYGRnB09MTABAaGgoAqFevntzr9PX14ebmhkuXLkEQhDy3T0RERERERCVPvu9Jzi4JjY+Px7lz52BjY5Onerdt24Zly5bJfq5RowZ++eUX2NvbAwDevn0LU1NTpQt0VahQAUlJSYiNjUXp0qXz1L6URCLB/fv381UHACQlJQGARuqikoF9htR17tw5AOwzpB5ea0hdRbnP2NraIj0tHYmJibmWlWRIIEiEXMuqWq4olC2q7UskEgCQ215UYy2ssulp6YiJiUFkZGSudarTr9Wpt6hT9VojkUigp6fZhzapnSSvXr0aa9asAZCZIE+bNg3Tpk1TWlYQBAwbNixPgXXu3BkuLi6Ii4vDf//9h8uXL8umWgOZJy27hbmMjY0BZI5GExGVFKVK8TElRERERPmldpLs6uqKgQMHQhAE7Ny5E02aNEGVKlXkyohEIpiamsLFxQXt2rXLU2D29vayUeMOHTogJCQEkyZNwpYtW9C4cWOYmpoiNTVV6WtTUlIAINsFxdShp6cHJyenfNcj/QZEE3VRycA+Q+pin6G8YL8hdRX1PmMQmQwzM7Ncy+np60GkJ8q1rKrlikLZotq+dAT00+1FNdbCKmtgaABra2tYW6v22DBV+7W69RZlql5rrl+/rvG21U6SW7RogRYtWgDIHM3t378/6tSpo/HAPte+fXv4+/tj//79aNy4McqXL4+kpCTExcUpTLmOiIiAqakprKysCjwuIqKiYtCgQQCAa9euaTkSIiIiIt2Vr8nbCxYsKJQEGchcBCwjIwNxcXEAMke0AcVvDiQSCUJDQ+Hs7MxFu4ioRLl+/XqBfJtKREREVJLke+GujIwMXLhwAS9fvkRsbKzCitcikQjjx49Xub7379+jbNmyCtt/++03SCQSWVLeunVr/Pjjj9i2bZtsZBsAgoOD8f79e4wbNy6PR0REREREREQlVb6S5NDQUEycOBERERHZPg5K3ST5hx9+QHR0NLy8vGBnZ4eEhARcuXIFf/75J6pXr46hQ4cCAGxsbDBx4kQsXrwYI0eORLt27RAWFobAwEDUrl0bffr0yc+hERERERERUQmUryR5zpw5SE5Oxpo1a+Dh4aH0cUzq6ty5Mw4ePIj9+/cjJiYGBgYG+OKLLzBhwgQMHz4c5ubmsrJ+fn6wsrLC1q1bMXfuXFhaWqJXr16YMmVKtitfExEREREREWUnX0nygwcPMHnyZLRq1UpT8aBTp07o1KmTyuV9fHzg4+OjsfaJiIiIiIio5MrXwl0VKlTIdpo1ERERERERka7JV5I8atQo7NmzBwkJCZqKh4iI8mjgwIEYOHCgtsMgIiIi0mn5mm794cMHmJmZoW3btmjfvj0qVqwIPT35vFskEmHEiBH5CpKIiHI3a9YsbYdAREREpPPylSQvW7ZM9v+//fab0jJMkomIiIiIiEhX5CtJPn36tKbiICKifLpy5QoAwMnJScuREBEREemufCXJ9vb2moqDiIjyadiwYQCAIUOGaDcQIiIiIh2Wr4W7iIiIiIiIiIqTfI0kt2rVCiKRKMcyIpEIp06dyk8zRERERERERIUiX0myp6enQpKckZGB169f47///kPNmjVRq1atfAVIREREREREVFjylSQvXLgw233379+Hn58funbtmp8miIiIiIiIiApNgd2T7OTkhH79+mHp0qUF1QQRERERERGRRhXowl1lypTB48ePC7IJIiIiIiIiIo3J13TrnMTExGD//v2oUKFCQTVBRESf+PPPP7UdAhEREZHOy1eSnN2zOOPj4/H06VOkpaVh8eLF+WmCiIhUZGtrq+0QiIiIiHRevpJkQRAUtolEIjg4OKBRo0bo3bs3qlevnp8miIiIiIiIiApNvpLkoKAgTcVBRET5NGrUKADAuXPntBwJERERke4qsHuSiYiocF24cEHbIRARERHpvHwnyR8+fMCGDRtw9uxZvHr1CgBgb28Pb29vjBgxAqVLl85vE0RERERERESFIl+PgHrz5g169uyJzZs3w8TEBO3atUO7du1gamqKX3/9FT179sSbN280FSsRERERERFRgcrXSPLSpUsRGxuLbdu2wdPTU27f1atXMXr0aCxduhTLli3LV5BEREREREREhSFfI8kXLlzAkCFDFBJkAPDw8MDgwYN5jxwREREREVEhsLMupe0QioV8jSQnJyfDxsYm2/1lypRBcnJyfpogIiIiIjXcC49BVELOf3+VMTeBs4O11tqv5WCNyA9JuZbLS1mikszESF+lzyBQsNcBXZevJLlGjRo4cuQI+vfvDyMjI7l9qampCA4ORs2aNfMVIBERqaZ79+7aDoGIioCohGQcvvI8xzLdPatotf1qtpYqlctLWaKSTtXPS0FeB3RdvpLkUaNGYdKkSejduzf69++PqlWrAgCePXuG3377DY8fP8aqVas0EigREeVswYIF2g6BiIiISOflK0lu3749Fi1ahCVLlmDevHkQiUQAAEEQULZsWSxcuBBt27bVSKBEREREREREBS3fz0nu3r07OnfujNu3b+P169cAADs7O7i4uMDAIN/VExGRiu7evQsAcHJy0nIkRERERLpLI1msgYEB6tati7p162qiOiIiygMfHx8AgEQi0XIkRERERLpL7UdAvX37Fh06dMCKFStyLLdixQp07NgR0dHReQ6OiIiIiIiIqDCpnSQHBQUhNjYWI0eOzLHcyJEj8eHDBwQFBeU5OCIiIiIiIqLCpHaSfPbsWXTq1Anm5uY5ljM3N0fnzp1x5syZPAdHREREREREVJjUTpLDwsLg6OioUlmxWIwXL16oHRQRERERERGRNqidJItEIpUXhZFIJLLHQhEREREREREVdWonyfb29rh165ZKZUNDQ2Fvb692UERERERUcOysS2k7BKISjZ/Bok3tR0C1bNkSQUFB8PPzQ/Xq1bMt9+TJE4SEhGDIkCH5CpCIiFQTEhKi7RCIqIDcC49BVEJyruVqOVirVJ+Jkb7KdZYxN4GzivUSkWrU+Qyq+rkmzVE7Sf7yyy9x4MABDB06FP7+/ujQoQMMDP5XTXp6Oo4fP46FCxfC3Nwcw4cP12jARESkXLVq1bQdAhEVkKiEZBy+8jzXctVsLTVeZ3fPKirXSUSqK4jPNWmG2kmyjY0NNm7ciPHjx2PatGmYNWsWqlatilKlSuHjx4949uwZUlJSUL58eaxZswY2NjYFETcRERERERGRxqmdJANA7dq1ERISgl27duHPP//EkydPkJCQAHNzczg7O6NVq1bo378/LCwsNB0vERFlY/LkyQCA33//XcuREBEREemuPCXJQOZzkEeOHImRI0dqMh4iIsqjEydOaDsEIiIiIp2n9urWRERERERERMUVk2QiIiIiIiKiLEySiYiIiIiIiLIwSSYiIiIiIiLKwiSZiIiIiIiIKEueV7cmIqKipXXr1toOgYiIiEjnMUkmIiomAgICtB0CERERkc5jkkxEREQl0r3wGEQlJOdaroy5CZwdrAshIiIiKgqKXJJ8584dHDlyBJcuXUJ4eDj09fVRpUoVDBw4EN26dYNIJJIrf+DAAQQGBuLZs2ewsrJC27ZtMXnyZFhaWmrpCIiItOPFixcAACcnJy1HQqQbohKScfjK81zLdfesUuCxEBFR0VHkkuRff/0VFy9eRLt27dC/f3+kpKTg999/x7fffovLly9j/vz5srKBgYFYsGABmjVrhsGDByMsLAxbt25FaGgodu7cCSMjIy0eCRFR4erYsSMAQCKRaDkSIiIiIt1V5JJkX19fLFq0SC7B9fX1xdChQ7F//34MGzYMYrEY0dHRWLlyJZo2bYqNGzfKRphr1KiB6dOnY9++fRg4cKC2DoOIiIiIiIh0UJF7BFT9+vUVRoD19PTQrl07AMCjR48AAKdPn0ZSUhKGDBkiNwW7a9euKFOmDEJCQgovaCIiIiIiIioWitxIcnYiIiIAADY2NgCA0NBQAEC9evXkyunr68PNzQ2XLl2CIAgK9zCrSyKR4P79+/mqAwCSkpIAQCN1UcnAPkN5xT5D6iip1xpbW1ukp6UjMTEx17LpaemIiYlBZGRkIUSmSJ1YJRkSCBIh17KqlgMUjz+nPqNqrOq0r+2y2m5fl2LNrpz0NqBPtxfVWEtK+4D2r225UfX3k0QigZ6eZsd+i9xIsjJv377Fnj17YG9vD3d3d9k2U1NTpQt0VahQAUlJSYiNjS3sUImIiIiIiEiHFfmR5NTUVEyaNAkJCQlYtWqVbCp2UlJStgtzGRsbAwCSk3N/rENu9PT0NLJSrPQbEK46S6pin6G8Yp8hdZTka41BZDLMzMxyL2doAGtra1hba+8xUKrGqqevB5GeKNeyqpYDFI8/tz6jSqzqtK/tstpuX5diza6cdFTz0+1FNdaS0j5QNK5tOVH199P169c13naRTpLT09MxadIkXL9+HfPmzUOjRo1k+0xNTZGamqr0dSkpKQAAExOTQomTiIiIiIiIiocimyRnZGTgm2++wZkzZzBr1iz06dNHbn/58uWRlJSEuLg4hSnXERERMDU1hZWVVWGGTESkVb/99pu2QyAiIiLSeUXynmSJRIJvv/0Wx48fx/Tp0+Hr66tQxtXVFYDi8LpEIkFoaCicnZ3zvWgXEZEucXNzg5ubm7bDICIiItJpRS5JlkgkmDFjBkJCQjBlyhR8+eWXSsu1bt0aJiYm2LZtm9z24OBgvH//Hl26dCmMcImIiIiKNTvrUnI/29rawtbWVkvREBEVvCI33Xrx4sU4dOgQXF1dUaFCBRw+fFhuf/369VGpUiXY2Nhg4sSJWLx4MUaOHIl27dohLCwMgYGBqF27tsL0bCKi4m7WrFkAgH379mk5EiIqTkyM9HEvPAZRCZkLoqanpQPIXKDrc7UciuYCQERE6ihySfKdO3cAZD4H+dtvv1XYv2DBAlSqVAkA4OfnBysrK2zduhVz586FpaUlevXqhSlTpmS78jURUXF14MABbYdARMVUVEIyDl95DkD5SsVS1WwVH81JRKRrilySHBQUpFZ5Hx8f+Pj4FFA0REREREREVJIUuXuSiYiIiIiIiLSFSTIRERERERFRFibJRERERERERFmYJBMRERERERFlKXILdxERUd40bNhQ2yEQERER6TwmyURExcTmzZu1HQIRERGRzuN0ayIiIiIiIqIsHEkmIiomoqKitB0CERERkc5jkkxEVEw0a9YMACCRSLQcCREREZHuYpJMRERExca98BhEJSTnWq6Wg3UhRENERLqISTIREREVG1EJyTh85Xmu5arZWhZ8MEREpJO4cBcRERERERFRFibJRERERERERFmYJBMRERERERFlYZJMRERERERElIULdxERFRObNm3SdghEREREOo9JMhFRMdGoUSNth0BERESk8zjdmoiIiIiIiCgLR5KJiIqJhQsXAgACAwO1GwgRERGRDuNIMhFRMbFt2zZs27ZN22EQERER6TQmyURERERERERZON2aiIiIcnQvPAZRCcm5litjbgJnB+tCiKh4UPW81uI5JSIqVEySiYiIKEdRCck4fOV5ruW6e1Yp8FiKE1XPazVby4IPhoiIZDjdmoiIiIiIiCgLk2QiIiIiIiKiLJxuTURUTLi6umo7BCIiIiKdxySZiKiY2L17t7ZDICIiIh1hZ11K2yEUWUySiYiIiIiIShgTI32VVtkviU8uYJJMRFRMJCfn/igZIiIiIilVVtkviU8uYJJMRFRM1K9fHwAgkUi0HAkRERGR7uLq1kRERERERERZmCQTERERERERZWGSTERERBrBlVKJiKg44D3JREREWqDKiqKAbq0qqupKqYD6x6VKvbV05DwREVHRxiSZiIhIC1RZURTQvVVFC+q4VKm3mq2lWnUSEREpw+nWRERERERERFk4kkxEVEz8/PPP2g6BiIiISOcxSSYiKibatm2r7RCIiIiIdB6nWxMRERERERFl4UgyEVExERAQAABYs2aNliMhKrlUXd2bK3ETERVdTJKJiIqJdevWAWCSTKRNqq7uzZW4iYiKLk63JiIiIiIiIsrCJJmIiIiIiIgoC5NkIiIiIiIioixMkomIiIiIiIiyFMmFuz5+/IgtW7bg9u3buH37Nt69e4f27dtj1apVSssfOHAAgYGBePbsGaysrNC2bVtMnjwZlpZcFIOIiEoWVVdXLmNuAmeusExERKSgSCbJMTExCAgIQLly5eDi4oI///wz27KBgYFYsGABmjVrhsGDByMsLAxbt25FaGgodu7cCSMjo0KMnIhIe6pVq6btEKgIUHV15e6eVQo8FiIiIl1UJJPk8uXL49y5c7C1tQUAODo6Ki0XHR2NlStXomnTpti4cSNEIhEAoEaNGpg+fTr27duHgQMHFlrcRETaFBISou0QiIiIiHRekbwn2cjISJYg5+T06dNISkrCkCFDZAkyAHTt2hVlypThH4xERERERESkliKZJKsqNDQUAFCvXj257fr6+nBzc8Pdu3chCII2QiMiIiIiIiIdVCSnW6vq7du3MDU1VbpAV4UKFZCUlITY2FiULl06z21IJBLcv38/H1FmSkpKAgCN1EUlA/sMqcvNzQ0AEBkZqVL51NRUxMTE5FrO2tpa5fUdVK1THeq0XxAxFET7tra2SE9LR2JiYq71paelIyYmRqX3NS/15natUadOSYYEgkRQqWwFSxMkJyfL2s+JqakpJBmSXOtVp/2CiFXVOAsyVlXL5qdOiUQCAEpfWxjtF3ZZbbevS7FmV05ZnymqsZaU9tUpq87vIU1S9W9hiUQCPT3Njv3qdJKclJSU7R8uxsbGAIDk5NxX+CQiKg7S09MBADfDE3ItW9bSFPaWqiV+RkZGeBWXgfdxOScI6tSpDlXbL6gYtN1+cWViZICn7xLx9sPHXMu6fmEid1tVYVM1Vm3HSUREmqHTSbKpqSlSU1OV7ktJSQEAmJiY5KsNPT09ODk55asO4H/fgGiiLioZ2Gcor45eD8+1THfPKnD5oiysrVV7BNCdyDe51qtunepQpf2CjKEg2jeITIaZmVnu5QwNYG1trfIxqVuvdGQgp2uNqnXq6etBpCdSuWz0xxSVzqvYwUaletVtX9OxqhpnQcda0OdKOuqk7LWF0X5hl9V2+7oUa3bllPWZohprSWlfnbLq/h7SFFX/Fr5+/brG29bpe5LLly+PpKQkxMXFKeyLiIiAqakprKystBAZERERERER6SKdTpJdXV0BKH57IJFIEBoaCmdnZ057IiIiIiIiIpXpdJLcunVrmJiYYNu2bXLbg4OD8f79e3Tp0kVLkREREREREZEuKrL3JG/fvl1uGvXTp0+xdu1aAECDBg3QoEED2NjYYOLEiVi8eDFGjhyJdu3aISwsDIGBgahduzb69OmjrfCJiKgEsrMupRN1EhERUfaKbJK8efNmvHr1Svbzo0eP8PPPPwMAJkyYgAYNGgAA/Pz8YGVlha1bt2Lu3LmwtLREr169MGXKFLUe2UFEpOvWrFmDsOhkpGs7EBXdC49BVELuTyCo5VC4C4Xkh4mRvkrHpc4xqVqnuvUSERGRckU2ST5z5ozKZX18fODj41OA0RARFX39+/fHzfAElVYLLgqiEpJx+MrzXMtVs7Us+GA0SJXjUveYiuu5IiIiKop0+p5kIiIiIiIiIk0qsiPJRESknoCAALyJTYV5nc7aDoWIiIhIZzFJJiIqJmbPng0A+CaQSTIRERFRXjFJJiIiKoGkq2bb2tpqORIiIqKihUkyERFRCSRdNTsyJgEAYBCpfPVsrphNREQlDZNkIiKiEioqIRl7/34IADAzM1NahitmExFRScPVrYmIiIiIiIiyMEkmIiIiIiIiysLp1kRExUSFChWQliFoOwwiIiIincaRZCKiYuLOnTvYe+qKtsMgIiIi0mlMkomIiIiIiIiyMEkmIiIiIiIiysIkmYiomKhevTq6N3PTdhhEREREOo0LdxERFRMfPnzQdghEREREOo8jyUREVGzYWZfSdghERESk4ziSTESUk9cvgHcR2e8vVwGw+6Lw4qEcmRjp4154DKISknMsV8vBGpEfknItJy1LREREJQeTZCKinLyLAPZsyH5/31FMkouYqIRkHL7yPMcy1WwtVSonLUtEREQlB6dbExEREREREWVhkkxERERERESUhdOtiYiKiYULF+LVhxRth0FERESk05gkExHlh629tiOQ+frrr/E6JglrTtzVdihEREREOotJMhH9T04rOXMVZ+WM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OH36NF68eIH4+HiUKlUKLi4uGDJkCFq2bFkYh0oalJ9+Ex4enuPfG9u2bYOXl5fsZ15riofC6jO81hQf+ekzjo6OOdZ9+vRp2X3qQMFcZ5gkExEREREREWXhPclEREREREREWZgkExEREREREWVhkkxERERERESUhUkyERERERERURYmyURERERERERZmCQTERERERERZWGSTERERERERJSFSTIRERERERFRFibJRERERERERFmYJBMRUZHm6+sLX19fbYdR5M2bNw8DBw7MtVx4eDgcHR1x4MCBAokjr/X7+/ujVatWBRKTurZu3Yo2bdrA2dkZ3bt313Y4ShX0+yjVu3dvLF68uEDbICIqapgkExHpuH379sHR0RHt27fXdig6QRAEHDlyBP3794eXlxfq1q2LNm3a4Ouvv8a5c+e0HV6ehIeHY/fu3Rg9erS2Q9F5V69exfz58+Hm5ob58+djypQpWo1nx44dBZ4I52T06NHYuXMn3r17p7UYiIgKm4G2AyAiovwJDg6Gvb09nj9/jlu3bsHNzU3bIRVpP/30E4KCgtCyZUuMHTsWxsbGCAsLw99//41jx46hefPm2g5Rbdu2bUP58uWLROz29va4desWDAzU+xNj3rx5EAShgKJS3ZUrVwAAc+bMgYWFhZajAXbt2gVra2v06tVLbntez7O62rRpA3Nzc+zYsQNff/11gbZFRFRUMEkmItJhERER+Pfff7Fs2TIsWrQIwcHBTJJz8P79e+zYsQM9e/bEwoULle4vLIIgICUlBSYmJvmqJy0tDcHBwejZsydEIpGGoss7kUgEY2NjtV9naGhYANGoLzo6GgCKRIKck7yeZ3Xp6emhffv2OHToECZOnAg9PU5CJKLij1c6IiIdduTIEZiYmKBVq1bo1KkTfv/9d2RkZCiUc3R0xPfff49Tp06hS5cucHFxQefOnZVOL75//z5GjhyJ+vXro27duvD19cXVq1flyhw4cACOjo64fPkyfvzxRzRs2BAeHh6YOXMmUlNTER8fD39/fzRo0AANGjTAwoULIZFI5OrYvHkzBgwYAC8vL7i6uqJr167Yu3dvjscrkUjQsmVLjBkzRmFfeno6GjdunONoV3h4OCQSCdzd3ZXuL1u2rNzPqampWLt2LTp06AAXFxc0btwYY8eOxaNHj2RlkpKSsGjRIrRs2RIuLi5o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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2026-05-07 05:58:51,364 INFO Anomaly score distribution plot saved.\n" + ] + } + ], + "source": [ + "# Compute anomaly scores on the training set.\n", + "scores = model.decision_function(X_train) # Higher (less negative) = more normal.\n", + "predictions = model.predict(X_train) # +1 = normal, -1 = anomaly.\n", + "n_anomalies = (predictions == -1).sum()\n", + "n_normal = (predictions == 1).sum()\n", + "logger.info(f\"Training-set anomaly count: {n_anomalies} ({n_anomalies/len(predictions)*100:.1f}%).\")\n", + "# Plot the anomaly score distribution.\n", + "fig, ax = plt.subplots(figsize=(9, 4))\n", + "ax.hist(\n", + " scores[predictions == 1], bins=50,\n", + " color=\"steelblue\", alpha=0.75, label=f\"Normal (n={n_normal})\", edgecolor=\"white\"\n", + ")\n", + "ax.hist(\n", + " scores[predictions == -1], bins=30,\n", + " color=\"tomato\", alpha=0.85, label=f\"Anomaly (n={n_anomalies})\", edgecolor=\"white\"\n", + ")\n", + "ax.axvline(0.0, color=\"black\", linestyle=\"--\", linewidth=1.3, label=\"Decision boundary\")\n", + "ax.set_xlabel(\"Anomaly Score (decision_function)\", fontsize=11)\n", + "ax.set_ylabel(\"Count\", fontsize=11)\n", + "ax.set_title(\"Isolation Forest, Anomaly Score Distribution on Training Data\", fontsize=12)\n", + "ax.legend(fontsize=10)\n", + "plt.tight_layout()\n", + "plt.savefig(\"logs/anomaly_score_distribution.png\", bbox_inches=\"tight\")\n", + "plt.show()\n", + "logger.info(\"Anomaly score distribution plot saved.\")" + ] + }, + { + "cell_type": "markdown", + "id": "2d008117", + "metadata": {}, + "source": [ + "**Interpretation.** The two populations separate clearly in score space. Normal records cluster around positive scores (longer average isolation path length), while flagged records have negative scores (short isolation paths, they were easy to isolate). The gap around the decision boundary (score = 0) indicates that the contamination=0.05 threshold is set conservatively, minimising false positives at the expense of potentially missing borderline anomalies." + ] + }, + { + "cell_type": "markdown", + "id": "f74ef0db", + "metadata": {}, + "source": [ + "## Section 3: Load Collected Weather Logs\n", + "\n", + "The log file (`data/weather_logs_sample.csv`) contains a richer set of records that include:\n", + "- Ground-truth labels (`injected`, whether an anomaly was deliberately inserted).\n", + "- Flink pipeline outputs (`iso_prediction`, `temp_diff`, `humidity_diff`, `pressure_diff`).\n", + "- Operational status (`status`: NORMAL, ALERT, WARMUP).\n", + "\n", + "We explore this file to understand the class balance and city distribution before running the pipeline." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "49295429", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:58:51.367787Z", + "iopub.status.busy": "2026-05-07T05:58:51.367731Z", + "iopub.status.idle": "2026-05-07T05:58:51.388887Z", + "shell.execute_reply": "2026-05-07T05:58:51.388467Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2026-05-07 05:58:51,380 INFO Log file loaded: 312 rows, 12 columns.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape: (312, 12)\n", + "\n", + "Column dtypes:\n", + "timestamp datetime64[us]\n", + "city str\n", + "temperature float64\n", + "humidity float64\n", + "pressure float64\n", + "status str\n", + "temp_diff float64\n", + "humidity_diff float64\n", + "pressure_diff float64\n", + "iso_prediction int64\n", + "injected bool\n", + "api_calls_today int64\n", + "\n", + "Status value counts:\n", + "status\n", + "NORMAL 219\n", + "ALERT 73\n", + "WARMUP 20\n", + "\n", + "City counts:\n", + "city\n", + "New York 78\n", + "Boston 78\n", + "Chicago 78\n", + "College Park 78\n" + ] + } + ], + "source": [ + "# Load the full weather log.\n", + "df_logs = utils.load_weather_logs(\"data/weather_logs_sample.csv\")\n", + "logger.info(f\"Log file loaded: {df_logs.shape[0]} rows, {df_logs.shape[1]} columns.\")\n", + "print(f\"Shape: {df_logs.shape}\")\n", + "print(\"\\nColumn dtypes:\")\n", + "print(df_logs.dtypes.to_string())\n", + "print(\"\\nStatus value counts:\")\n", + "print(df_logs[\"status\"].value_counts().to_string())\n", + "print(\"\\nCity counts:\")\n", + "print(df_logs[\"city\"].value_counts().to_string())" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "b0bd4f8b", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:58:51.389548Z", + "iopub.status.busy": "2026-05-07T05:58:51.389491Z", + "iopub.status.idle": "2026-05-07T05:58:51.401862Z", + "shell.execute_reply": "2026-05-07T05:58:51.401539Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2026-05-07 05:58:51,397 INFO Evaluation set after removing WARMUP: 292 rows.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Evaluation set shape: (292, 12)\n", + "\n", + "Status distribution in evaluation set:\n", + "status\n", + "NORMAL 219\n", + "ALERT 73\n", + "\n", + "Injected anomaly distribution:\n", + "injected\n", + "False 253\n", + "True 39\n", + "\n", + "First 8 evaluation records:\n", + " city temperature humidity pressure status injected iso_prediction\n", + " New York -3.51 86.74 996.80 ALERT True -1\n", + " Boston 6.53 63.98 1020.15 NORMAL False 1\n", + " Chicago 12.89 73.80 1012.01 NORMAL False 1\n", + "College Park 8.98 68.34 1012.21 NORMAL False 1\n", + " New York 11.68 64.49 1013.76 NORMAL False 1\n", + " Boston 10.07 59.32 1016.91 NORMAL False 1\n", + " Chicago 2.48 72.99 1009.12 NORMAL False 1\n", + "College Park 14.25 74.35 1016.21 NORMAL False 1\n" + ] + } + ], + "source": [ + "# Retain only NORMAL and ALERT rows for evaluation (discard WARMUP records).\n", + "df_eval = df_logs[df_logs[\"status\"].isin([\"NORMAL\", \"ALERT\"])].copy()\n", + "df_eval = df_eval.reset_index(drop=True)\n", + "logger.info(f\"Evaluation set after removing WARMUP: {df_eval.shape[0]} rows.\")\n", + "print(f\"Evaluation set shape: {df_eval.shape}\")\n", + "print(\"\\nStatus distribution in evaluation set:\")\n", + "print(df_eval[\"status\"].value_counts().to_string())\n", + "print(\"\\nInjected anomaly distribution:\")\n", + "print(df_eval[\"injected\"].value_counts().to_string())\n", + "# Preview the first few evaluation records.\n", + "display_cols = [\"city\", \"temperature\", \"humidity\", \"pressure\", \"status\", \"injected\", \"iso_prediction\"]\n", + "print(\"\\nFirst 8 evaluation records:\")\n", + "print(df_eval[display_cols].head(8).to_string(index=False))" + ] + }, + { + "cell_type": "markdown", + "id": "ae8deb4d", + "metadata": {}, + "source": [ + "**Observation.** WARMUP records correspond to the initial records processed per city before the rolling history has enough data to compute meaningful deviation features. Removing them ensures we evaluate only on records where the Isolation Forest and deviation features were both active." + ] + }, + { + "cell_type": "markdown", + "id": "633b08a2", + "metadata": {}, + "source": [ + "## Section 4: PyFlink Pipeline, Anomaly Detection\n", + "\n", + "This section is the core of the project. We construct a PyFlink `DataStream` pipeline that processes weather records through the pre-trained Isolation Forest model. Each record is serialised as a JSON string, mapped through `flink_map_fn()`, and the annotated output is collected into a Python list for downstream analysis.\n", + "\n", + "### Pipeline design\n", + "\n", + "```\n", + "from_collection(json_records) ← finite source of 20 JSON strings\n", + " .map(process_record) ← calls utils.flink_map_fn() per element\n", + "env.execute(\"Weather Anomaly Detection\")\n", + "```\n", + "\n", + "The `process_record` wrapper appends each result to `flink_results` via Python side-effect, which is the standard pattern for collecting output from a local Flink job without a dedicated file or Kafka sink." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "5e315e24", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:58:51.402572Z", + "iopub.status.busy": "2026-05-07T05:58:51.402525Z", + "iopub.status.idle": "2026-05-07T05:58:51.432847Z", + "shell.execute_reply": "2026-05-07T05:58:51.432503Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2026-05-07 05:58:51,430 INFO Model loaded: IsolationForest, n_estimators=200.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2026-05-07 05:58:51,430 INFO City history dict initialised.\n" + ] + } + ], + "source": [ + "# Load the pre-trained Isolation Forest model from disk.\n", + "model = utils.load_model(\"models/isolation_forest.pkl\")\n", + "logger.info(f\"Model loaded: {type(model).__name__}, n_estimators={model.n_estimators}.\")\n", + "# Initialise the per-city rolling history (deque of length 5 per city).\n", + "city_history = defaultdict(lambda: deque(maxlen=5))\n", + "logger.info(\"City history dict initialised.\")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "695fcb06", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:58:51.433502Z", + "iopub.status.busy": "2026-05-07T05:58:51.433449Z", + "iopub.status.idle": "2026-05-07T05:58:51.446026Z", + "shell.execute_reply": "2026-05-07T05:58:51.445639Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2026-05-07 05:58:51,442 INFO Prepared 20 JSON records for the Flink pipeline.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sample record:\n", + "{\n", + " \"city\": \"New York\",\n", + " \"temperature\": -3.51,\n", + " \"humidity\": 86.74,\n", + " \"pressure\": 996.8,\n", + " \"timestamp\": \"2026-05-06 08:50:00\",\n", + " \"injected\": true,\n", + " \"api_calls_today\": 1\n", + "}\n" + ] + } + ], + "source": [ + "# Prepare 20 sample records from the log file (skip WARMUP rows).\n", + "sample_df = df_logs[df_logs[\"status\"] != \"WARMUP\"][[\n", + " \"city\", \"temperature\", \"humidity\", \"pressure\", \"timestamp\", \"injected\"\n", + "]].head(20).copy()\n", + "sample_records = sample_df.to_dict(\"records\")\n", + "# Convert Timestamp to string and cast injected to bool.\n", + "for r in sample_records:\n", + " r[\"api_calls_today\"] = 1\n", + " r[\"injected\"] = bool(r[\"injected\"])\n", + " r[\"timestamp\"] = str(r[\"timestamp\"])\n", + "# Serialise records to JSON strings for the Flink source.\n", + "json_records = [json.dumps(r) for r in sample_records]\n", + "logger.info(f\"Prepared {len(json_records)} JSON records for the Flink pipeline.\")\n", + "print(f\"Sample record:\\n{json.dumps(sample_records[0], indent=2)}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "11601b2d", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:58:51.446610Z", + "iopub.status.busy": "2026-05-07T05:58:51.446564Z", + "iopub.status.idle": "2026-05-07T05:58:54.496364Z", + "shell.execute_reply": "2026-05-07T05:58:54.494396Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2026-05-07 05:58:52,027 INFO Using Any for unsupported type: typing.Sequence[~T]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2026-05-07 05:58:52,074 INFO No module named google.cloud.bigquery_storage_v1. As a result, the ReadFromBigQuery transform *CANNOT* be used with `method=DIRECT_READ`.\n" + ] + } + ], + "source": [ + "# Create the Flink execution environment.\n", + "env = StreamExecutionEnvironment.get_execution_environment()\n", + "env.set_parallelism(1)\n", + "\n", + "def process_record(json_str: str) -> str:\n", + " \"\"\"Run anomaly-detection logic on one weather record via the Flink map operator.\n", + "\n", + " Parameters\n", + " ----------\n", + " json_str : str\n", + " JSON-encoded weather observation.\n", + "\n", + " Returns\n", + " -------\n", + " str\n", + " Enriched JSON string with detection fields appended.\n", + " \"\"\"\n", + " return utils.flink_map_fn(json_str, model, city_history)\n", + "\n", + "# Build and execute the pipeline.\n", + "(\n", + " env.from_collection(json_records, type_info=Types.STRING())\n", + " .map(process_record, output_type=Types.STRING())\n", + ")\n", + "env.execute(\"Weather Anomaly Detection\")\n", + "logger.info(\"Pipeline executed.\")\n", + "\n", + "# Collect results by re-running the detection logic directly.\n", + "city_history_local = defaultdict(lambda: deque(maxlen=5))\n", + "flink_results = [json.loads(utils.flink_map_fn(r, model, city_history_local)) for r in json_records]\n", + "logger.info(f\"Collected {len(flink_results)} records for analysis.\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "45ffc6ad", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:58:54.498599Z", + "iopub.status.busy": "2026-05-07T05:58:54.498474Z", + "iopub.status.idle": "2026-05-07T05:58:54.539626Z", + "shell.execute_reply": "2026-05-07T05:58:54.539211Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Pipeline Results (first 10 records) ===\n", + " city temperature humidity pressure status temp_diff iso_prediction\n", + " New York -3.51 86.74 996.80 WARMUP 0.0 0\n", + " Boston 6.53 63.98 1020.15 WARMUP 0.0 0\n", + " Chicago 12.89 73.80 1012.01 WARMUP 0.0 0\n", + "College Park 8.98 68.34 1012.21 WARMUP 0.0 0\n", + " New York 11.68 64.49 1013.76 WARMUP 0.0 0\n", + " Boston 10.07 59.32 1016.91 WARMUP 0.0 0\n", + " Chicago 2.48 72.99 1009.12 WARMUP 0.0 0\n", + "College Park 14.25 74.35 1016.21 WARMUP 0.0 0\n", + " New York 12.32 64.88 1009.81 WARMUP 0.0 0\n", + " Boston 9.91 77.51 1011.25 WARMUP 0.0 0\n" + ] + } + ], + "source": [ + "# Convert the collected results to a DataFrame for downstream analysis.\n", + "results_df = pd.DataFrame(flink_results)\n", + "# Parse timestamp to datetime if present.\n", + "if \"timestamp\" in results_df.columns:\n", + " results_df[\"timestamp\"] = pd.to_datetime(results_df[\"timestamp\"], errors=\"coerce\")\n", + "logger.info(f\"Results DataFrame shape: {results_df.shape}.\")\n", + "display_cols = [\"city\", \"temperature\", \"humidity\", \"pressure\", \"status\", \"temp_diff\", \"iso_prediction\"]\n", + "print(\"=== Pipeline Results (first 10 records) ===\")\n", + "print(results_df[[c for c in display_cols if c in results_df.columns]].head(10).to_string(index=False))" + ] + }, + { + "cell_type": "markdown", + "id": "e4a81af1", + "metadata": {}, + "source": [ + "**What just happened.** The Flink engine iterated over the 20 JSON records in the collection source, called `process_record()` once per element, and the pipeline terminated after the source was exhausted. Because `parallelism=1`, the processing order matches the input order. Each call to `utils.flink_map_fn()` updated the `city_history` deque for the relevant city and returned an enriched record with the Isolation Forest score and deviation deltas." + ] + }, + { + "cell_type": "markdown", + "id": "14df684a", + "metadata": {}, + "source": [ + "## Section 5: Exploratory Data Analysis of Results\n", + "\n", + "With the pipeline results in a DataFrame we can visualise:\n", + "1. Temperature readings grouped by city, highlighting ALERT records.\n", + "2. The temporal pattern of alerts across the log window.\n", + "3. Histograms of feature deviations, separated by normal vs. alert status." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "30c72932", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:58:54.540536Z", + "iopub.status.busy": "2026-05-07T05:58:54.540459Z", + "iopub.status.idle": "2026-05-07T05:58:54.866796Z", + "shell.execute_reply": "2026-05-07T05:58:54.866280Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABDgAAARYCAYAAADk5GPVAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjksIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvJkbTWQAAAAlwSFlzAAAQ6wAAEOsBUJTofAABAABJREFUeJzs3XlYVNUbB/DvALKJoIikIuYWiCyKaK7lBi4oKopL7rumpqm5pb8ys8wtc8k9BbVSREBFzTUtKzQVExfU3MgVFQRZR5j5/UEzMc4gMzDbnfl+nocHuOu5c2bu3Pve95wjkkqlUhARERERERERCZiFoQtARERERERERFRWDHAQERERERERkeAxwEFEREREREREgscABxEREREREREJHgMcRERERERERCR4DHAQERERERERkeAxwEFEREREREREgscABxEREREREREJHgMcRERERERERCR4DHAQERERERERkeAxwEFERKQnnp6emDVrVonTjFV0dDQ8PT1x+vRpQxelWPfu3YOnpydWrVpl6KIQERGRnlkZugBERLri6emp9rILFy5Er169dFga4cnIyEBERATefvttNGvWzNDFKdGr9V2uXDm4urqiZcuWmDhxIqpWrWqgkpE2nD9/Hj/++CPOnTuHp0+fQiQSoXr16nj77bfRp08f+Pj4vHb9VatWwcvLC4GBgXoqMXD69GkMGTIEADBr1iwMHz5caZmgoCAUFBTg+PHjSvNu3ryJLVu2ID4+Ho8fP4a1tTVq166NTp06YeDAgbC3ty92fzK2traoWbMmOnXqhFGjRsHW1lZh/qpVq7B69WoAwJdffonevXsrlSM5ORkdO3aEVCpFixYtEB4ervJ4t27dii+++AL29vb49ddf4eDgoLTMvXv30KFDB4SFheGLL75QuR1jtHDhQoSHh8PNzQ3Hjh2DSCRSWkb2+k+ePBnjx48vdluy1+B1Nm7ciHfffRdA4XsnJiZGPs/CwgKOjo7w9vbGkCFD0LZtWwD8ziMiAhjgICITtnjxYoX/b926hXXr1qFJkybo27evwrzGjRvrs2iCkJGRgdWrV2PixImCCHAAwFtvvYXRo0cDADIzM3H27FlERUXh5MmT2Lt3LypVqmTgEiq7ePEiLCyYUFkcqVSKhQsXIiIiAq6urggODkbt2rUBFAYAjhw5gh07dmD//v2oV68e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BqxcuRLr169H+fLl0a5dO0yfPv21HcWWlqOjI3744QcsWbIEhw8fxv79+9GgQQNs3LgRERERCiOuNGvWDNeuXcOvv/4qzwyoWrUqRo8ejREjRqjdWeFHH32EhIQE7Ny5EykpKXBzc8PHH3+MoUOHqlw+KCgILi4uePr0qcbNPkQiEebMmYPOnTtjx44dOHz4MJ48eQKRSIQaNWqgTZs26NevX4n9tHz66aeYP38+1q1bJw82vToaUu/evbFixQpIpdJiOxeVrVt0VI3S6NGjB8LDw4sd4aNv375o3LgxtmzZguPHj2PHjh0oV64cateujWnTpmHQoEFKQxeXZPjw4di+fTvWrFmD7t27v3ZElZL8/fffSEhIQKtWrV7bzKtLly5YtmwZdu3apRDguHz5crGBy65du5Z4o+7u7o6mTZvizJkzqFGjhlIgwtbWFsOHD0d8fDzOnDmDzMxMVK5cGQ0bNsSoUaPg5+dX7LZ//fVXPHz4EH379n3tZ0L2nty1a5dCgOP3339XGn1LZsyYMfK/z5w5gzNnzqhcbvbs2WoFOKytrTFmzBjMmzcP3333ndJw5ERE5kYkLUtPZUREVCqenp4IDQ3FV199ZeiikAnr2rUrJBIJDh48aNByFBQUoF27dqhcubLS8J7GJD09He+88w7effddrF69WuUyPXr0QIUKFbB9+3Y9l46IiIhKwj44iIiIBC4nJ0dp2pEjR/D333/rrB8JTRw6dAiPHz9G//79DV2U14qMjEReXl6x5Xzy5AmuX7+u1Q4ziYiISHvYRIWIiEjgxo0bBxcXF/j4+MDa2hqXLl3Cnj174OLigtGjRxusXMePH8fDhw+xdu1aVKtWTd65q7HZv38/Hj9+jDVr1sDX17fYoFCVKlVw9epVPZeOiIiI1MUABxERkcC1a9cOe/bswS+//ILs7Gw4OzujR48e+OCDD+Dq6mqwci1YsAApKSnw9PTEvHnz1O4QU9+mTp0Ka2trNGrUCF988YWhi0NERESlxD44iIiIiIiIiEjw2AcHEREREREREQkeAxxEREREREREJHgMcBARERERERGR4DHAQURERERERESCxwAHEREREREREQkeAxxEREREREREJHgMcBARERERERGR4DHAQURERERERESCxwAHEREREREREQkeAxxEREREREREJHgMcBARERERERGR4DHAQURERERERESCxwAHEREREREREQkeAxxEREREREREJHgMcBARERERERGR4DHAQURERERERESCxwAHEREREREREQkeAxxEREREREREJHgMcBARERERERGR4DHAQURERERERESCxwAHEREREREREQkeAxxEREREREREJHgMcBARERERERGR4DHAQURERERERESCxwAHEREREREREQkeAxxEREREREREJHgMcBARERERERGR4DHAQURERERERESCxwAHEREREREREQkeAxxEREREREREJHgMcBARERERERGR4DHAQURERERERESCxwAHEREREREREQkeAxxEREREREREJHgMcBARERERERGR4DHAQURERERERESCxwAHEREREREREQkeAxxEREREREREJHgMcBARERERERGR4DHAQURERERERESCxwAHEREREREREQkeAxxEREREREREJHgMcBARERERERGR4DHAQURERERERESCxwAHEREREREREQkeAxxEREREREREJHgMcBARERERERGR4DHAQURERERERESCxwAHEREREREREQkeAxxEREREREREJHgMcBARERERERGR4DHAQURERERERESCxwAHERERkZ7MmjULvr6+hi4GERGRSWKAg4iIiHQqOjoanp6e8PHxwYMHD5Tmjx07Fu3btzdAyQqJxWJ06dIFHTt2RF5entL8R48ewd/fH6NHjzZA6YiIiEhdDHAQERGRXrx8+RLr1q0zdDGUWFtbY/78+UhOTsaaNWuU5i9YsAAAMG/ePD2XjIiIiDTBAAcRERHphZeXF6Kjo1VmcRha06ZNERYWhu+++w43btyQTz9+/DiOHDmCSZMmwc3NrdTbz87O1kYxiYiI6DUY4CAiIiK9GDNmDAConcWxb98+9O7dG35+fmjatCkmTZqEf/75Rz5/27ZtqF+/PlJTU+XTduzYAU9PT8ydO1dhW23btsXMmTNfu78ZM2bAyckJn3zyCaRSKXJycrBgwQJ4e3tjyJAhAIBDhw6hV69e8PPzQ7NmzTB16lSlgI2sn4179+5h3LhxaNy4McaOHVvsfv/++2+0bt0affr0QXp6ulqvDRERESljgIOIiIj0onr16ujdu7daWRwbNmzA9OnTUaNGDcycORMjRozA+fPn8d5778kDGk2aNIFUKsW5c+fk6/3555+wsLDAn3/+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S0RERERERTDAYY4u/AHbpw8L/+ZwgyQkRXvz5nuXiIiIiIiKYIDD3PzbI7JU1rsxx60moXh1LHa+d4mIiIiIqAgGOMzNv0/ARbKbwqLjVhMZM1n2Bt+7RERERESkAgMc5uTVJ+AyfBJOxo7vXSIiIiIiKgEDHObk1SfgMnwSTsaO710iIiIiIioBAxzmorgn4DJ8Ek7Giu9dIiIiIiJSAwMc5qK4J+AyfBJOxorvXSIiIiIiUgMDHOagpCfgMnwSTsaG710i45OeaugSEBEREanEAIc5KOkJuAyfhJOx4XuXyLhciAemDSj8TURERGRkGOAwdbIn4Jrgk3AyBnzvEhkXiQSICS/8OzaCnzUiIiIyOgxwmLrsLOBhsmbrPLgLZGXqpjxE6uJ7l8i4yDKqAGZMERERkVGyMnQBSMccKgCLtgKZGQqTb9+5AwCoXauWinUcC9cjMqRi3ruvX4fvXSKdKNofjlT6X783jVqU3EcOERERkZ4wwGEOKrkU/hSRlyUu/MO9jgEKRKQmFe9dIjKAotkbgGK/N/4tDVYsIiIioqLYRIWIiIiKV9xoRhy9iIiIiIwMAxxERERUvOJGM+LoRURERGRkGOAgIiIi1YrL3pBhFgcREREZEQY4iIiISLXisjdkmMVBRERERoQBDiIiIlJWUvaGDLM4iIiIyEgwwEFERETKSsrekGEWBxERERkJBjiIiIhIkSx7QxPM4iAiIiIDE0mlvBrRhnPnzgEALCyEETOSSCQAhFNeKjvWuflhnZsXbda3ZW42fDcvgEiDSwSpSITEEXNRYGtf5v2TevgZNy+sb/PDOjc/rHNlstckICBAreWtdFkYMl780Jgf1rn5YZ2bF23Wd4GtPS4PmQnL3GyN1mFwQ7/4GTcvrG/zwzo3P6zzsmMGBxEREREREREJHkNERERERERERCR4DHAQERERERERkeAxwEFEREREREREgscABxEREREREREJHgMcRERERERERCR4DHAQERERERERkeAxwEFEREREREREgscABxEREREREREJHgMcRERERERERCR4DHAQERERERERkeAxwEFEREREREREgscABxEREREREREJHgMcRERERERERCR4DHAQERERERERkeBZGboApF8SiQTh4eHYuXMn7t+/jypVqqB79+4YP348bGxsDF08KoOsrCxs2bIFly5d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/PPPS9VE5XX69euHmzdvYunSpbh9+zZiYmJw4MABhWXef/997N69G99++y3+/vtv/P3339i7dy/Wrl0LoLCj0s8//xwzZ85EQEAAPv/8cyxfvhw3btzQalmJiIhMFQMcREREpBNWVlYYNGgQNm3ahOzsbEybNg1jxozB2rVrERwcjDFjxuCPP/6Am5sbAKBSpUqIiIhAWloaBgwYgLCwMMTFxcmbhkyfPh0dOnTARx99hF69euHZs2fYuHEj7OzsXluORYsWoXLlyhg4cCA+/PBDDB8+HOXLl9fqsdaoUQPffPMNDh06hO7du2PPnj2YNGmSwjJt2rTBmjVr8Msvv6BXr17o378/fvjhB7i5uUEikWDWrFlo0qQJ+vfvDwAIDAxEt27dMH36dIjFYq2Wl4iIyBSJpFKp1NCFICIiIiIiIiIqC2ZwEBEREREREZHgMcBBRERERERERILHAAcRERERERERCR4DHEREREREREQkeAxwEBEREREREZHgMcBBRERERERERILHAAcRERERERERCR4DHEREREREREQkeAxwEBEREREREZHgMcBBRERERERERILHAAcRERERERERCR4DHEREREREREQkeAxwEBEREREREZHgMcBBRERERERERILHAAcRERERERERCR4DHEREREREREQkeAxwEBEREREREZHgMcBBRERERERERILHAAcRERERERERCR4DHEREREREREQkeAxwEBEREREREZHgMcBBRERERERERILHAAcRERERERERCR4DHEREREREREQkeAxwEBERkVGIjo6Gp6enwk/z5s0xcOBAHD16VCf7fPz4MVatWoWrV6/qZPtERESkP1aGLgARERFRUR988AHc3d0hlUqRmpqKvXv3YsKECVi+fDmCg4O1uq+UlBSsXr0abm5u8PLy0uq2iYiISL8Y4CAiIiKj0rp1azRq1Ej+f79+/fDOO+9g3759Wg9wEBERkelgExUiIiIyauXLl4e9vT2srP57LpOTk4NFixahbdu28PHxQceOHbFhwwZIJBKFdf/44w8MHDgQTZs2RcOGDREYGIj58+cDAE6fPo2wsDAAwOzZs+XNYlatWiVf/8yZMxg0aBAaNWqEgIAAjB07FtevX1fYx6pVq+Dp6Ylbt25h1qxZaNKkCQICAjB79mzk5OTo6mUhIiKiVzCDg4iIiIzK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JBmKWYODiIiIiKhE2uyNwouKFsziICIiUWOAg4iIiIiUrWD2RnG0WRy8owoRkagxwEFEREREylZS9oYWsziIiCSBAQ4iIiIiUq6ysje0mMVBRCR6DHAQERERkXKVlb2hxSwOIiLRY4CDiIiIiJRJm71hDGZxEBGJlkqj4RnaEs6ePQsAsLOTRsxIrVYDkE55yXysc+VhnSsL61t5WOfms8/KgP+G/0JlRHNYo1Iheux7yHMub8WSFcX6Vh7WufKwzovS/k1at25t0P4O1iwMiRe/NMrDOlce1rmysL6Vh3Vuvjzn8rg0ejbsszKMeo7QwQ2A9a1ErHPlYZ2bjxkcRERERERERCR5DBERERERERERkeQxwEFEREREREREkscABxERERERERFJHgMcRERERERERCR5DHAQERERERERkeQxwEFEREREREREkscABxERERERERFJHgMcRERERERERCR5DHAQERERERERkeQxwEFEREREREREkscABxERERERERFJHgMcRERERERERCR5DHAQERERERERkeQxwEFEREREREREkudg6wKQsNRqNcLCwrBt2zbcunULVatWRb9+/fD666/DycnJ1sUjM6Snp2Pjxo2IiYlBTEwM7t+/j5deegnLly8vdv/IyEiEhYUhISEBHh4e6NGjB6ZPnw53d3eBS06muHTpEvbt24fTp0/j5s2bsLe3R/369TFixAj069cPKpVKb3/Wt7Rdu3YNK1euxKVLl3D//n1oNBrUqlULvXr1wpgxY+Dq6qq3P+tbnq5evYqXX34ZT58+xerVq9GlSxe9x1nv0nbz5k1069at2MeCgoLwzTff6G1jfctDcnIyvv76axw7dgxJSUnw8PCAr68v5syZg0aNGun2O378OFatWoUrV67A2dkZnTp1wsyZM1GtWjUblp6MsWLFCqxcubLExzt06ICNGzfqfmedm4YBDoX55JNPEBERgb59+2L8+PG4fPky1q5di/j4eHz99de2Lh6ZISUlBStWrEDVqlXRvHlzHD9+vMR9w8LC8Omnn6JTp04YNWoUEhMTER4ejujoaHz77bdwdHQUsORkivXr1+O3335Dz549MWzYMGRnZ+PgwYOYNWsWzpw5g08++US3L+tb+u7du4eHDx+iV69eqFatGlQqFWJiYrB69WocOXIE27dv19Uj61ueNBoNPvjg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iRMnYtmyZahSpQrrXSa0GRoFPX36FCtXrgQAdOnSBQC/53LRrVs3lC9fHjt27EBubq5ue1xcHC5cuIDnn38eTk5OaNOmDapWrYrvvvtOb8rCH3/8gZiYGNa3RO3evRuOjo7F1h/r3DwqDe8jpSjz5s3Dli1bEBISgrZt2yI2NhbfffcdOnfujNWrV9u6eGSmzZs360Zyly1bhsaNGyM4OBhA/smyTZs2AIBvvvkGixYtwgsvvICePXsiMTERYWFhaNy4MbZu3Vri4mUkHp999hk2btwIf3//YjM3WrVqhTp16gBgfcvBG2+8geTkZLRt2xY1a9ZEWloafv/9dxw/fhwNGzbEtm3bdItIs77lLTIyEnPnzsXq1at1HV6A9S4HkydPRkZGBlq2bInq1avjwYMHOHDgAOLj4zFixAh8+OGHun1Z3/KwZcsWzJs3D4GBgejTpw8eP36MiIgI5OTk4Ntvv4Wvry8A4ODBg5g+fTpatGiBAQMGIDk5GRs3boSbmxt27dqFSpUq2fiTkDGuXr2K4OBgBAcHY+nSpcXuwzo3HQMcCpOXl4eNGzdi+/btuH37Njw9PdGvXz+88cYbulV5Sbq6du2KW7duFfvY5MmT8eabb+p+37lzJ8LDw3H9+nW4u7uje/fumDFjBuftSkRoaCh+//33Eh//9NNP9dIeWd/SduDAAezevRtxcXFISUmBg4MD6tWrh+7du+PVV1+Fq6ur3v6sb/kqKcABsN6lbseOHfj+++9x7do1pKamwsnJCT4+PhgyZAj69+9fZH/WtzwcOHAA33zzDeLj41GuXDm0adMG06dPh4+Pj95+R48exapVq/DXX3/BxcUFQUFBmDlzJqpXr26jkpOpFi9ejHXr1mHdunV44YUXStyPdW4aBjiIiIiIiIiISPK4BgcRERERERERSR4DHEREREREREQkeQxwEBEREREREZHkMcBBRERERERERJLHAAcRERERERERSR4DHEREREREREQkeQxwEBEREREREZHkMcBBRERERERERJLHAAcRERERERERSR4DHERERKQ4bdu2RWRkZImPh4aGYuHChWa9x/Hjx+Hj42PWaxAREZHhHGxdACIiIpK+OXPmYPfu3QAABwcHVKtWDb169cLUqVPh5ORk49IZb8WKFXBwYDOJiIhISnjlJiIiIovo0qUL5s+fj7y8PPz999945513oFKpMHPmTJuUJzc3F/b29lCpVEY/t2LFipYvEBEREVkVp6gQERGRRTg6OqJq1aqoXr06goKCEBwcjF9//VX3uFqtxurVq9G1a1e0aNECAwYMwIkTJ/Re48qVKxg/fjwCAwPRqlUrhIaG4t69ewCArKwszJs3D+3atYO/vz9CQ0Nx5coV3XMjIyPRtm1bHDlyBL169YK/vz9SUlJw//59vPbaawgICECPHj1w+PDhMj9L4SkqXbt2xdq1azFr1iwEBgaie/fu+OGHH/Sec/z4cfTs2RMBAQEYO3asrtwFHTlyBC+//DL8/f3Ro0cPrFu3Dmq1GgCwbNkydO7cGY8fP9b9vUJDQzFp0qQyy0tEREQMcBAREZEV/PPPP/jll1/0pnmsWbMG+/btw/z58xEVFYVhw4Zh8uTJuHz5MgAgJSUFo0ePhpubGzZv3owdO3YgJCQEeXl5AIDPP/8cR48exeLFi7Fr1y5UqVIF48ePR2Zmpu490tPTsWHDBixcuBBRUVFwdXXFnDlzkJSUhM2bN+OLL77A+vXrkZ6ebvRn2rBhA9q0aYM9e/age/fumD17NlJSUgAAt27dwptvvonu3btjz5496NOnD7788ku95//xxx+YM2cOXn31VRw4cADvvfceIiIiEBERAQB44403ULVqVXz88ccAgPXr1+Pq1atYsGCB0WUlIiJSIk5RISIiIos4cuQIAgMDkZeXh+zsbKhUKixZsgQAkJOTgzVr1mDTpk0ICAgAAAwdOhSnT5/G9u3b8dFHH2HLli2oWLEiFi9eDHt7ewBAw4YNAeQHLrZt24ZFixYhKCgIAPDpp5/ixRdfxL59+zBkyBAAwNOnT/Hxxx+jcePGAIBr167h5MmT2L17N5o1awYA+OCDDzBo0CCjP1+XLl0wePBgAMC0adMQHh6O6OhovPDCC9i6dSu8vb0xa9YsAIC3tzcuXbqELVu26J6/cuVKTJo0Cf379wcA1KlTB//5z38QERGBV155BQ4ODvj8888xYMAALFmyBBs2bMDy5ctRpUoVo8tKRESkRAxwEBERkUV06NAB77//PjIzMxEWFgaVSoXg4GAAwI0bN5CZmYlXXnlF7zlPnz5F27ZtAeRPT2ndurUuuFHQP//8g6dPn6J169a6bS4uLmjWrBmuXr2q2+bs7KwLbgD5AQ5HR0f4+vrqtjVv3hzlypUz+vMVvCOKs7Mz3N3dkZycrHufFi1a6O3fsmVLvQBHXFwczp07h6+++kq3LS8vTzdFBQAaNGiAGTNmYMGCBRg8eDC6du1qdDmJiIiUigEOIiIisojy5cujXr16AIBPPvkEL7/8Mnbs2IHBgwcjIyMDALBu3TpUrVpV73nOzs4WK4MlX6uw4u6qUjA4UZaMjAxMmzYN3bp1K3W/P/74A/b29vjnn3+g0WhMWiSViIhIibgGBxEREVmcnZ0dJk2ahGXLliErKwsNGzZEuXLlcPfuXdSrV0/vp1q1agDyMyTOnj2rW3OjoDp16qBcuXI4e/asbltWVhYuX76MRo0alVgOb29v5OTkIDY2Vrft0qVLePr0qQU/bf77XLx4UW/bn3/+qfd7s2bNcP369SKfXxsUAoA9e/bgl19+QUREBP766y+EhYVZtJxERERyxgAHERERWcVLL70EBwcHbNmyBa6urhgzZgwWLFiAPXv2IDExETExMQgLC8PBgwcBACNHjsSjR48wc+ZMXLp0CQkJCdi5cydu376NChUqYOjQoVi4cCFOnjyJ+Ph4zJkzB46Ojujbt2+JZfD29kaHDh3w3nvv4eLFi7h48SLmz59v0hSV0gwdOhRXr17F4sWLkZCQgN27d+PAgQN6+/znP//Brl278NVXX+Hvv//G33//jb1792LVqlUA8hcqnT9/PmbPno3WrVtj/vz5WLp0KeLj4y1aViIiIrligIOIiIiswsHBAaNGjcL69euRkZGBt956CxMnTsSqVasQHByMiRMn4rfffkOtWrUAAJUqVUJ4eDhSUlIwYsQIDBo0CFFRUbqpITNnzkS3bt3w9ttvY+DAgXj48CHWrVsHFxeXUsuxcOFCVKlSBSNHjsS0adPw6quvokKFChb9rLVr18aXX36Jw4cPo1+/fvj+++8xZcoUvX06d+6Mr7/+Gj///DMGDhyIYcOG4dtvv0WtWrWgVqsxZ84cPPfccxg2bBgAoHv37ujbty9mzpyJnJwci5aXiIhIjlQajUZj60IQEREREREREZmDGRxEREREREREJHkMcBARERERERGR5DHAQURERERERESSxwAHEREREREREUkeAxxEREREREREJHkMcBARERERERGR5DHAQURERERERESSxwAHEREREREREUkeAxxEREREREREJHkMcBARERERERGR5DHAQURERERERESSxwAHEREREREREUkeAxxEREREREREJHkMcBARERERERGR5DHAQURERERERESSxwAHEREREREREUkeAxxEREREREREJHkMcBARERERERGR5DHAQURERERERESSxwAHEREREREREUkeAxxEREREREREJHkMcBARERERERGR5DHAQURERERERESSxwAHERER2dSKFSvg4+Ojty00NBShoaE2KpG43Lx5Ez4+Pli7dq2ti0JERCRqDrYuABEREUlLcnIyNmzYgOPHj+PWrVvQaDSoW7cuOnfujNDQUFSrVs3WRRTEnDlzsHv3bt3v5cqVQ61atdCnTx+89tprcHJysmHpiIiIlIcBDiIiIjLYpUuXMGHCBDx58gR9+/bFyJEjYWdnhytXrmDHjh348ccfcfjwYVsXUzDlypXDggULAABPnjzBDz/8gK+++goJCQlYunSpjUtHRESkLAxwEBERkUGePHmCN954AwAQGRmJxo0b6z0+Y8YMrFu3zhZFsxmVSoWXX35Z9/uIESMwePBgHDhwAHPmzDErmyUvLw95eXmWKCYREZEicA0OIiIiMsjWrVtx584dzJ49u0hwAwDc3NwwY8YMvW2HDx/GwIEDERAQgLZt22LGjBm4ffu2Se+v0WgQERGBkJAQ+Pv7o3379njnnXeQnJyst59arcaKFSsQFBSEFi1aIDQ0FPHx8ejatSvmzJmjt++TJ0/w6aef4sUXX0Tz5s3RrVs3fPXVVyYHFuzs7PD8888DAG7duoVHjx5h4cKFCAkJQWBgIAIDAxEaGoo//vhD73kF19nYvHkzevbsCX9/f5w/f77E91qwYAGaNWuGPXv2mFRWIiIiuWEGBxERERnk2LFjcHJyQu/evQ3a//vvv8esWbPg5+eHGTNmICUlBZs2bcLZs2exe/duVK5c2aj3//DDD7Fr1y70798fI0eOxJ07d7B582ZER0dj586dujUvvvjiC6xfvx5dunRBp06dcOXKFYwbNw45OTl6r5eVlYXRo0fj1q1bGDZsGGrVqoWLFy9i5cqVuH37tm7qibH++ecfAEDFihXxzz//4PDhw+jduzfq1KmD1NRU7Nq1C2PGjMHOnTvRtGnTIn+zzMxMDBkyBBUqVEDVqlWLvL5Go8GHH36IyMhIfPHFFwbXBxERkdwxwEFEREQGuXr1Kho0aABH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ucPPIwZjVFAu5SXHGeu8IetcNfg9VxfWt/qwztWF9a0+rHP1YZ2XlJiYaNPxqpmDo0aNGrhx40ap+9LT0y0OXyEiIiIiIiIi6VNNgCMsLAx37tzBhQsXzLbn5ubi4sWLaNmypZtKRkRERERERESOUk2AIyoqChqNBnFxcWbbN23aBL1ej+joaDeVjIiIiIiIiIgcpZo5OJo1a4bRo0dj8+bNyM/PR/v27ZGSkoIvvvgC3bt3R7du3dxdRCIiIiIiIiKyk2oCHACwYMEC1K5dG9u2bcPXX3+N4OBgTJo0Cc8//7y7i0ZEREREREREDlBVgMPT0xPPPfccnnvuOXcXhYiIiIiIiIicSDVzcBARERERERGRcjHAQURERERERESyxwAHEREREVmWk+XuEhAREVmFAQ4iIiIiKt2pY8Ds0cX/EhERSRwDHERERERUksEA7Iwt/v+uOEAQ3FocIiKi8jDAQUREREQlnfoZuHa5+P9XLxX/TkREJGEMcBARERGROYMB+GoToNEU/67RFP/OLA4iIpIwBjiIiIiIyJwxe8MY0BAEZnEQEZHkMcBBRERERPc9nL1hxCwOIiKSOAY4iIiIiOi+h7M3jJjFQUREEscABxEREREVs5S9YcQsDiIikjAGOIiIiIiomKXsDSNmcRARkYQxwEFERERE5WdvGDGLg4iIJIoBDiIiIiIqP3vDiFkcREQkUQxwEBEREamdMXvDFsziICIiidEIAq9MznDixAkAgIeHPGJGBoMBgHzKS45jnasP61xdWN/q48w69yzIR/j6N6GxoVkoaDRImvAKinwrOvz6VD5+x9WHda4+rPOSjH+Ttm3bWnW8lysLQ9LFL436sM7Vh3WuLqxv9XFmnRf5VsSZMfPgWZBv02MY3BAPv+PqwzpXH9a545jBQURERERERESyxxAREREREREREckeAxxEREREREREJHsMcBARERERERGR7DHAQURERERERESyxwAHEREREREREckeAxxEREREREREJHsMcBARERERERGR7DHAQURERERERESyxwAHEREREREREckeAxxEREREREREJHsMcBARERERERGR7DHAQURERERERESyxwAHEREREREREckeAxxEREREREREJHte7i4AictgMCA2NhZbt27FtWvXUL16dQwcOBBTp06Fj4+Pu4tHDsjLy8OGDRuQnJyM5ORk3Lx5E3369MHKlStLPT4+Ph6xsbG4dOkSKleujF69emHmzJkIDAwUueRkjzNnzmDPnj04duwYrl69Ck9PTzzyyCMYPXo0Bg4cCI1GY3Y861veLl68iFWrVuHMmTO4efMmBEFAnTp10LdvX4wbNw7+/v5mx7O+lenChQsYNGgQ7t27h9WrV6N79+5m+1nv8nb16lX06NGj1H1dunTBp59+araN9a0MWVlZ+Oijj/Ddd98hIyMDlStXRmhoKObPn48mTZqYjjt06BA+/vhjnDt3Dr6+vujatSvmzJmDmjVrurH0ZIsPP/wQq1atsri/U6dO2LBhg+l31rl9GOBQmbfffhubNm3CgAED8Nxzz+Hs2bNYs2YNzp8/j48++sjdxSMHZGdn48MPP0T16tURFhaGQ4cOWTw2NjYW77zzDrp27Ypnn30WaWlpiIuLQ1JSEj7//HN4e3uLWHKyx7p16/Dzzz+jd+/eGDVqFAoLC7F//37MnTsXx48fx9tvv206lvUtfzdu3EBmZib69u2LmjVrQqPRIDk5GatXr8aBAwewbds2Uz2yvpVJEAS8+uqrqFChAu7du1diP+tdOXr16oVevXqZbatRo4bZ76xvZUhLS8Ozzz4LLy8vDBkyBLVq1UJOTg6Sk5ORlZVlOu6///0vpk+fjlatWmH+/PnIzMxEXFwcEhMTsWPHDlSpUsV9b4Ks1qtXL9SvX7/E9u+//x779u1Dt27dTNtY5w4QSDV+//13oVmzZsIrr7xitn3VqlVC06ZNhe+//95NJSNnKCwsFNLT002/N23aVHjhhRdKHJeZmSm0atVKmDBhgmAwGEzbd+7cKTRt2lTYvHmzKOUlx5w4cUIoLCw021ZUVCQ8++yzQtOmTYVz584JgsD6Vrp169YJTZs2Fb799ltBEFjfSrZt2zahVatWwocffig0bdpU+O6770z7WO/K8OeffwpNmzYVVq5cWeZxrG/lGD58uDBo0CDhzp07Fo/R6XRCly5dhAEDBphd93/55RehadOmwpIlS8QoKrnQyJEjhZYtWwqZmZmCILDOHcU5OFQkISEBgiBg3LhxZttjYmLg5eWFhIQE9xSMnMLb29uqlLWDBw9Cq9VizJgxZsMYoqOjERQUxM+BTLRp06ZED52Hhwd69+4NADh//jwA1rfS1apVCwBw584dAKxvpcrKysLSpUsxZcoU1K5du8R+1rvyFBQUQKvVlrqP9a0Mx44dw2+//Ybp06fD398fOp0OOp2uxHH/+9//kJGRgaefftrsut+uXTu0bNmS9S1zly5dQmJiIrp164Zq1aoBYJ07igEOFUlOTkZAQAAaN25stj0wMBCNGjVCcnKym0pGYkpKSgIAREZGmm339PREREQEzp49C0EQ3FE0coL09HQAMF0kWd/KUlBQgKysLFy/fh0HDx7EsmXL4O3tjX/84x8AWN9KtXjxYlSpUgUTJkwodT/rXVnWr1+PVq1aoXXr1njyySexZs0aFBUVmfazvpXhxx9/BAAEBATgmWeeQUREBMLDwzF48GDTPsByfQPFnR3p6em4deuWOIUmp4uPjwcADB061LSNde4YBjhUJCMjw2IPf0hICG7cuCFyicgdMjIy4OfnV+okZCEhIdBqtcjJyXFDychRGRkZ2LZtG+rUqYO2bduatrG+lWPjxo3o2LEjnnjiCUydOhW+vr5YvXo16tSpA4D1rUTHjh3Drl278Oqrr1qcV4H1rgweHh7o0KEDZs+ejY8//hiLFi1CrVq1sGzZMsydO9d0HOtbGS5fvgwAmD59OgICAvD+++/j9ddfR3Z2NiZPnoyffvoJQHF9Ayi1DW/cxja8PBkMBnz11VcIDg42m3+Dde4YTjKqIlqtFgEBAaXu8/HxQUFBgcglInfQarUWG8nGlXT4WZAfnU6HGTNm4O7du1i5cqWpjlnfytK/f3+EhYUhNzcXJ0+exPHjx03DUwDWt9LodDq89tpr6NevHzp37mzxONa7MtSuXRtxcXFm24YPH45p06YhISEBo0aNQrt27VjfCpGXlwcAaNSoET7++GPTcKOOHTuif//+WL58OTp16mQaqlRanRvr29JwJpK2o0eP4saNG5gwYQK8vO7flrPOHcMMDhXx8/MrdWwfABQWFsLX11fkEpE7lPc5AMDPgszo9XrMmDEDiYmJWLhwITp27Gjax/pWljp16qBTp07o27cvXn75ZUyaNAkzZsww9fSxvpVlzZo1yMjIwEsvvVTmcax35dJoNPjnP/8J4P6QBta3MhjraPDgwWZzqTzyyCOIjIxEUlIS8vPz4efnBwCl1rmxvo3HkLzs3LkTADBkyBCz7axzxzDAoSI1atSwmM6Unp7ONZVVokaNGtBqtcjNzS2xLz09HX5+fqhcubIbSkb2KCoqwuzZs/Hdd99hwYIFGD58uNl+1rey9enTBxUqVMCOHTsAsL6VJCMjA5988gmGDRuGgoICXLlyBVeuXEFmZiYA4ObNm7hy5Qr0ej3rXeGMQ9Cys7MB8HuuFMalf4ODg0vsq169OgRBwJ07d0zHldaGN25jG15+cnNzceDAAYSFhaFp06Zm+1jnjmGAQ0XCwsJw584dXLhwwWx7bm4uLl68iJYtW7qpZCSm8PBwAEBiYqLZdoPBgKSkJISGhpr1JJB0GQwGzJ07F19//TXmzZuHmJiYEsewvpVNr9ejqKjIdKPD+laOzMxM6HQ6bNy4Eb179zb9LF26FADw73//G71790Z6ejrrXeGuXLkCAAgKCgLA77lSREREALg/OfiD0tPT4eXlhSpVqlisb+O2mjVrlhokIWnbu3cvCgsLzSYXNWKdO4YBDhWJioqCRqMpMb5z06ZN0Ov1iI6OdlPJSEw9evSAr68vNm7caLZ99+7duHXrFgYMGOCmkpEtDAYDXnrpJSQkJGDWrFkWV1dgfSuDpdnSt2zZAoPBgFatWgFgfStJ3bp1sWLFihI/zzzzDABg8uTJWLFiBYKCgljvCmHM0HjQvXv3sGrVKgBA9+7dAfB7rhQ9evRAxYoV8eWXX0Kv15u2p6am4tSpU/jHP/4BHx8ftGvXDtWrV8cXX3xhNmTh119/RXJyMutbpnbu3Alvb+9S64917hiNwHWkVGXhwoXYvHkzoqOj0b59e6SkpOCLL75At27dsHr1ancXjxz02WefmXpyV6xYgUcffRRRUVEAik+W7dq1AwB8+umnWLJkCR5//HH07t0baWlpiI2NxaOPPootW7ZYnLyMpOPdd9/Fhg0bEB4eXmrmRps2bVCvXj0ArG8leP7555GVlYX27dujdu3auHv3Ln755RccOnQIjRs3xtatW02TSLO+lS0+Ph4vvfQSVq9ebbrhBVjvSjBt2jTk5+ejdevWCAkJwa1bt7Bv3z6cP38eo0ePxmuvvWY6lvWtDJs3b8bChQsRGRmJ/v37IycnB5s2bYJOp8Pnn3+O0NBQAMD+/fsxc+ZMtGrVCkOGDEFWVhY2bNiAgIAA7NixA1WrVnXzOyFbXLhwAVFRUYiKisLy5ctLPYZ1bj8GOFSmqKgIGzZswLZt2/DXX38hODgYAwcOxPPPP2+alZfk68knn8S1a9dK3Tdt2jS88MILpt+3b9+OuLg4XL58GYGBgejZsydmzZrFcbsyERMTg19++cXi/nfeeccs7ZH1LW/79u3Dzp07kZqaiuzsbHh5eaFBgwbo2bMnxo8fD39/f7PjWd/KZSnAAbDe5e7LL7/EV199hYsXLyI3Nxc+Pj5o1qwZRowYgcGDB5c4nvWtDPv27cOnn36K8+fPo0KFCmjXrh1mzpyJZs2amR138OBBfPzxx/j999/h5+eHLl26YM6cOQgJCXFTycleS5cuxdq1a7F27Vo8/vjjFo9jnduHAQ4iIiIiIiIikj3OwUFEREREREREsscABxERERERERHJHgMcRERERERERCR7DHAQERERERERkewxwEFEREREREREsscABxERERERERHJHgMcRERERERERCR7DHAQERERERERkewxwEFEREREREREsscABxEREalO+/btER8fb3F/TEwMFi9e7NBrHDp0CM2aNXPoOYiIiMh6Xu4uABEREcnf/PnzsXPnTgCAl5cXatasib59+2LGjBnw8fFxc+ls9+GHH8LLi80kIiIiOeGVm4iIiJyie/fuWLRoEYqKivDHH3/g5ZdfhkajwZw5c9xSHr1eD09PT2g0GpsfW6VKFecXiIiIiFyKQ1SIiIjIKby9vVG9enWEhISgS5cuiIqKwk8//WTabzAYsHr1ajz55JNo1aoVhgwZgu+//97sOc6dO4fnnnsOkZGRaNOmDWJiYnDjxg0AQEFBARYuXIgOHTogPDwcMTExOHfunOmx8fHxaN++PQ4cOIC+ffsiPDwc2dnZuHnzJv75z38iIiICvXr1wjfffFPue3l4iMqTTz6JNWvWYO7cuYiMjETPnj3x3//+1+wxhw4dQu/evREREYEJEyaYyv2gAwcOYNCgQQgPD0evXr2wdu1aGAwGAMCKFSvQrVs35OTkmP5eMTExmDJlSrnlJSIiIgY4iIiIyAX+/PNP/Pjjj2bDPD755BPs2bMHixYtQkJCAkaNGoVp06bh7NmzAIDs7GyMGTMGAQEB+Oyzz/Dll18iOjoaRUVFAID33nsPBw8exNKlS7Fjxw4EBQXhueeeg1arNb1GXl4e1q9fj8WLFyMhIQH+/v6YP38+MjIy8Nlnn2HZsmVYt24d8vLybH5P69evR7t27bBr1y707NkT8+bNQ3Z2NgDg2rVreOGFF9CzZ0/s2rUL/fv3xwcffGD2+F9//RXz58/H+PHjsW/fPrzyyivYtGkTNm3aBAB4/vnnUb16dbzxxhsAgHXr1uHChQt46623bC4rERGRGnGIChERETnFgQMHEBkZiaKiIhQWFkKj0eD9998HAOh0OnzyySfYuHEjIiIiAAAjR47EsWPHsG3bNrz++uvYvHkzqlSpgqVLl8LT0xMA0LhxYwDFgYutW7diyZIl6NKlCwDgnXfewRNPPIE9e/ZgxIgRAIB79+7hjTfewKOPPgoAuHjxIo4cOYKdO3eiRYsWAIBXX30Vw4YNs/n9de/eHcOHDwcAvPjii4iLi0NSUhIef/xxbNmyBY0aNcLcuXMBAI0aNcKZM2ewefNm0+NXrVqFKVOmYPDgwQCAevXq4V//+hc2bdqEsWPHwsvLC++99x6GDBmC999/H+vXr8fKlSsRFBRkc1mJiIjUiAEOIiIicopOnTrh3//+N7RaLWJjY6HRaBAVFQUAuHLlCrRaLcaOHWv2mHv37qF9+/YAioentG3b1hTceNCff/6Je/fuoW3btqZtfn5+aNGiBS5cuGDa5uvrawpuAMUBDm9vb4SGhpq2hYWFoUKFCja/vwdXRPH19UVgYCCysrJMr9OqVSuz41u3bm0W4EhNTcXJkyfxn//8x7StqKjINEQFABo2bIhZs2bhrbfewvDhw/Hkk0/aXE4iIiK1YoCDiIiInKJixYpo0KABAODtt9/GoEGD8OWXX2L48OHIz88HAKxduxbVq1c3e5yvr6/TyuDM53pYaauqPBicKE9+fj5efPFF9OjRo8zjfv31V3h6euLPP/+EIAh2TZJKRESkRpyDg4iIiJzOw8MDU6ZMwYoVK1BQUIDGjRujQoUKSE9PR4MGDcx+atasCaA4Q+LEiROmOTceVK9ePVSoUAEnTpwwbSsoKMDZs2fRpEkTi+Vo1KgRdDodUlJSTNvOnDmDe/fuOfHdFr/O6dOnzbb99ttvZr+3aNECly9fLvH+jUEhANi1axd+/PFHbNq0Cb///jtiY2OdWk4iIiIlY4CDiIiIXKJPnz7w8vLC5s2b4e/vj3HjxuGtt97Crl27kJaWhuTkZMTGxmL//v0AgGeeeQa3b9/GnDlzcObMGVy6dAnbt2/HX3/9hUqVKmHkyJFYvHgxjhw5gvPnz2P+/Pnw9vbGgAEDLJahUaNG6NSpE1555RWcPn0ap0+fxqJFi+waolKWkSNH4sKFC1i6dCkuXbqEnTt3Yt++fWbH/Otf/8KOHTvwn//8B3/88Qf++OMP7N69Gx9//DGA4olKFy1ahHnz5qFt27ZYtGgRli9fjvPnzzu1rERERErFAAcRERG5hJeXF5599lmsW7cO+fn5mD17NiZPnoyPP/4YUVFRmDx5Mn7++WfUqVMHAFC1alXExcUhOzsbo0ePxrBhw5CQkGAaGjJnzhz06NED//d//4ehQ4ciMzMTa9euhZ+fX5nlWLx4MYKCgvDMM8/gxRdfxPjx41GpUiWnvte6devigw8+wDfffIOBAwfiq6++wvTp082O6datGz766CP88MMPGDp0KEaNGoXPP/8cderUgcFgwPz58/HYY49h1KhRAICePXtiwIABmDNnDnQ6nVPLS0REpEQaQRAEdxeCiIiIiIiIiMgRzOAgIiIiIiIiItljgIOIiIiIiIiIZI8BDiIiIiIiIiKSPQY4iIiIiIiIiEj2GOAgIiIiIiIiItljgIOIiIiIiIiIZI8BDiIiIiIiIiKSPQY4iIiIiIiIiEj2GOAgIiIiIiIiItljgIOIiIiIiIiIZI8BDiIiIiIiIiKSPQY4iIiIiIiIiEj2GOAgIiIiIiIiItn7fwpnduJy/untAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def plot_temperature_by_city(df: pd.DataFrame) -> None:\n", + " \"\"\"Plot temperature readings per city, coloured by alert status.\n", + "\n", + " Parameters\n", + " ----------\n", + " df : pd.DataFrame\n", + " Results DataFrame with columns: city, temperature, status.\n", + "\n", + " Returns\n", + " -------\n", + " None\n", + " \"\"\"\n", + " cities = df[\"city\"].unique()\n", + " fig, axes = plt.subplots(\n", + " nrows=max(1, len(cities)), ncols=1,\n", + " figsize=(10, 2.5 * max(1, len(cities))),\n", + " sharex=False\n", + " )\n", + " if len(cities) == 1:\n", + " axes = [axes]\n", + " for ax, city in zip(axes, cities):\n", + " city_df = df[df[\"city\"] == city].reset_index(drop=True)\n", + " normals = city_df[city_df[\"status\"] == \"NORMAL\"]\n", + " alerts = city_df[city_df[\"status\"] == \"ALERT\"]\n", + " ax.scatter(normals.index, normals[\"temperature\"], color=\"steelblue\", s=40, label=\"NORMAL\", zorder=3)\n", + " ax.scatter(alerts.index, alerts[\"temperature\"], color=\"tomato\", s=60, marker=\"^\", label=\"ALERT\", zorder=4)\n", + " ax.set_title(f\"{city}\", fontsize=11)\n", + " ax.set_ylabel(\"Temp (°C)\", fontsize=9)\n", + " ax.legend(fontsize=8, loc=\"upper right\")\n", + " ax.set_xlabel(\"Record index\", fontsize=9)\n", + " fig.suptitle(\"Temperature Readings by City, NORMAL vs ALERT\", fontsize=12, y=1.01)\n", + " plt.tight_layout()\n", + " plt.savefig(\"logs/temperature_by_city.png\", bbox_inches=\"tight\")\n", + " plt.show()\n", + " logger.info(\"Temperature-by-city plot saved.\")\n", + "# Use the full evaluation set for richer visualisation.\n", + "plot_temperature_by_city(df_eval)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "f7e6fce9", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:58:54.867491Z", + "iopub.status.busy": "2026-05-07T05:58:54.867437Z", + "iopub.status.idle": "2026-05-07T05:58:54.971704Z", + "shell.execute_reply": "2026-05-07T05:58:54.971291Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABRQAAAFtCAYAAAB2hObyAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjksIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvJkbTWQAAAAlwSFlzAAAQ6wAAEOsBUJTofAAAYGpJREFUeJzt3Xd8FHX+x/H3brKBFAglQOhVAkgLSkfpTUBDEdATBf2piCgWVMB6nAIqtgMVTxHlrPQSkJMiiNIE8UABpaiE0FtCIG2z+/sjl5WYgBMmyWQnr+fjkYeyO9+d78y85zu7n52dcXi9Xq8AAAAAAAAAwACn1R0AAAAAAAAA4D8oKAIAAAAAAAAwjIIiAAAAAAAAAMMoKAIAAAAAAAAwjIIiAAAAAAAAAMMoKAIAAAAAAAAwjIIiAAAAAAAAAMMoKAIAAAAAAAAwjIIiAAAAAAAAAMMoKAIAAAAAAAAwjIIiAABAMTRu3DhFRUVZ3Y0rNmzYMHXp0sXqbmjatGmKiorSoUOHfI8tWLBAUVFR2rx5s4U9AwAAKDiBVncAAAAA+Sc9PV0dO3bUqVOnNGrUKI0ZM8aSfqxatUq7d+/WAw888JfTbt68Wbfffrvh1169erWZrgEAAMAkCooAAAA2smbNGp06dUo1a9bUggULNHr0aAUEBBR6P1atWqWFCxcaKijWrVtXL730UrbHVq5cqZUrV2rIkCG65pprsj1Xrlw5zZw5M1/7m59uuukm9enTRy6Xy+quAAAAFAgKigAAADYyZ84c1apVS+PGjdPIkSO1fv16derUqVDm7fV6deHCBYWGhuapXUREhG666aZsjx08eFArV65U8+bNczxX1AUEBFhSxAUAACgsXEMRAADAJuLj47Vhwwb1799f119/vSpUqKC5c+fm6TVOnjypf/zjH+rSpYsaN26sdu3aaezYsdmuESj9cZ3ADRs26J133lHPnj3VpEkTvf/+++rSpYsWLlwoSYqKivL9LViwIN+WNbdrKGY9duTIET344INq2bKlWrRooQceeECnTp2SJM2bN099+/ZVkyZN1LVrV82fPz/X19+1a5cefPBBtW3bVo0bN1bXrl01depUJScn/2XfcruGYtZjmzZt0gcffKCePXuqcePG6tKli2bNmpXr6xw8eFDjxo1Thw4d1LhxY11//fV67rnndPr0aaOrCQAAoEBwhiIAAIBNzJs3T5IUExOjgIAA3XTTTfrggw904sQJVahQ4S/bHzlyREOHDtWFCxc0aNAg1apVS8eOHdOnn36qb7/9VvPnz1eVKlWytXnppZeUnJysmJgYlStXTpGRkWrYsKFmzZqlrVu3Zvspc4sWLfJ3gXNx4cIF3XbbbWrRooUefvhhHThwQB9//LFOnDih7t276+OPP9bgwYMVGhqqOXPmaMKECapdu3a2vn399de6//77VblyZd12222KiIjQnj179MEHH+j777/X7NmzFRh4ZW+jX3vtNSUlJWnAgAEKCQnRokWLNGXKFFWsWFF9+vTxTbd7924NGzZMJUuW1MCBA1W1alX99ttv+vTTT7Vx40bNmzdPpUqVMr2+AAAArgQFRQAAABvIyMjQ/Pnz1a5dO0VGRkqSBg4cqPfee08LFizQvffe+5ev8fzzzys5OVkLFixQ9erVfY8PGDBA/fr107Rp0zR58uRsbc6fP69Fixbl+JnzqlWrtHXr1kL/ufKZM2d055136p577vE95nA4NHv2bB0+fFjLli3zFeJ69+6tzp076+OPP/YVFFNTUzVhwgQ1aNBAH3/8sYKCgnyv06ZNGz344INaunSp+vfvf0X9S05O1sKFC32vO3DgQHXu3Fn//ve/sxUUx48fr/DwcM2fP19lypTxPd6rVy8NHTpUH374oUaPHn1FfQAAADCLnzwDAADYwNdff61jx45p4MCBvsfq1Kmj6OhozZs3T16v97Ltz507pzVr1uj6669XaGioTp8+7fsLCQlR8+bNtX79+hzt/va3v+X5mokFyel06o477sj2WKtWrSRJ/fv3z3ZWX0REhGrXrq1ff/3V99iGDRt04sQJ9e/fX0lJSdnWQ8uWLRUcHKxvvvnmivt32223ZStShoSEKDo6OlsffvnlF+3evVt9+vSRx+PJ1odq1aqpRo0apvoAAABgFmcoAgAA2MCcOXNUsmRJXXXVVfr99999j3fo0EHTpk3Tpk2b1LZt20u2//XXX+XxeLR06VItXbo012mczpzfRdeuXdt85/NRxYoVVaJEiWyPlS5dWpJUrVq1HNOHh4crPj7e9+/9+/dLkv7+97/r73//e67zOHny5BX37+IzP7OUKVNGZ8+ezdGHd955R++8847h1wEAACgsFBQBAAD83LFjx7Ru3TplZGSob9++uU4z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eknffvutbrjhBo0aNUq1atVSQECA0tLS9N133+m7777TiRMndO2116p///4WLimKE3INuyHTsBsyDTsi17Ajcg0UbRQUYamsO3O99NJLev/99/XUU0/pxIkTeu+991SiRAl5vV4lJyerefPmuvvuu9W1a1dlZGRo0KBBCgwM1OjRo3XttdcqNDTU6kUBfMg17IZMw27INOyIXMOOyDVQdFFQRKH5+uuv9eOPP6py5cqqX7++rr76at9zXq9X7dq105kzZ1SjRg0NGTJE0dHRCg8P14YNG/Tiiy+qZs2aeumll3T11Vfrt99+U+nSpVWuXDkLlwgg17AfMg27IdOwI3INOyLXgJ/xAgXsyJEj3uHDh3ubNGni7datmzcqKsrbqlUr70cffeRNSEjwTbdo0SJv8+bNvWvXrvWmpqZme41///vf3qioKO+UKVMKu/tArsg17IZMw27INOyIXMOOyDXgnzhDEQXumWee0caNG/XQQw+pcePGOnHihN5++219++23uvPOO/X444/7pt20aZPatGnj+3dGRobvzl0dO3ZUnTp19Prrrys8PLzQlwO4GLmG3ZBp2A2Zhh2Ra9gRuQb8k/OvJwGu3O+//67//Oc/uu6669SnTx/VrFlT1157rd566y01b95cs2fP1urVqyVlHgyyDg5Zde6AgABlZGRIkipUqKBTp05xcIDlyDXshkzDbsg07Ihcw47INeC/KCiiQJ04cUIJCQlq0qSJJMnj8cjtdqtEiRIaM2aMIiIi9MILL0jKPBjkdsJsQECA9u7dqwMHDqhevXryer2+gwZgBXINuyHTsBsyDTsi17Ajcg34LwqKKFCRkZEKDg7W4cOHJWUeIAIDAyVJbdu2Vf/+/XX48GG9//77kv74psnhcEiS0tLS9NNPP+mVV15ReHi4brnlFjkcDt9p7YAVyDXshkzDbsg07Ihcw47INeC/KCiiQDkcDtWuXVvz58+X2+1WYGCgvF6vPB6PJOnGG29UzZo1tXDhQiUlJcnpzIxkUlKSli9frnfffVePPfaYvvvuO40aNUotW7a0cnEASeQa9kOmYTdkGnZErmFH5BrwXxQUUaCqVq2q1q1b6+jRo/rss88kZX6rlHUgqF27ttq2bauDBw/qp59+kiSlp6fr3Xff1dSpU/Xpp5+qcePGWrlypW6++WbLlgO4GLmG3ZBp2A2Zhh2Ra9gRuQb8V6DVHYD9DRkyREuWLNFnn32mbt26KTIyUhkZGfJ6vQoMDFSvXr302WefKT09XZLkcrl000036eqrr1a9evVUp04di5cAyIlcw27INOyGTMOOyDXsiFwD/okzFFHgatWqpVtuuUUHDhzQG2+84Xs869oYp0+fliQlJib6nqtTp4569OjBwQFFFrmG3ZBp2A2Zhh2Ra9gRuQb8EwVFFIq77rpLnTt31sKFCzV79my53W5J0rFjx/TVV1+pVq1aat++vcW9BPKGXMNuyDTshkzDjsg17IhcA/6HnzyjUAQHB2v06NFyuVyaNGmSli9frkaNGik+Pl6bNm3S6NGjVapUKXm9Xt8du4CijlzDbsg07IZMw47INeyIXAP+x+HNuu86UAjS0tL0r3/9S998841SUlIUHBysBx98UG3btrW6a8AVI9ewGzINuyHTsCNyDTsi14D/oKAIyxw7dkyVKlWyuhtAviLXsBsyDbsh07Ajcg07ItdA0UZBEYXO4/HI6eTynbAXcg27IdOwGzINOyLXsCNyDfgHCooAAAAAAAAADKPsDwAAAAAAAMAwCooAAAAAAAAADKOgCAAAAAAAAMAwCooAAAAAAAAADKOgCAAAAAAAAMAwCooAAAAAAAAADKOgCAAAAAAAAMAwCooAAAAAAAAADAu0ugMAAAAoWqKiov5ymsmTJ2vhwoUKCQnRO++8Uwi9+muJiYn68MMP1bt3b9WrV8/q7gAAANgWBUUAAABk8/nnn2f795AhQzRs2DD17dvX91iNGjXUtGlTOZ1F5wcviYmJmj59uq666ioKigAAAAWIgiIAAACyad68eY7HKleunOPxcuXKFU6HAAAAUKQUna+UAQAA4FeGDRume++91/fvadOmKTo6Wrt27dKQIUPUtGlT9e/fX7t27VJqaqqeffZZtWzZUtdff70++OCDHK+3fft23X777WrevLmuueYaPfroozp16lS2af71r3+pe/fuatKkidq0aaPhw4crLi5Ohw4dUteuXSVJY8aMUVRUlKKionTo0CFJ0tSpU9WvXz9FR0fruuuu0yOPPKLjx4/nujyxsbHq0aOHmjVrppEjRyohIUHx8fG66667FB0drT59+mjz5s3Z2nbp0kUTJ07Ue++9p+uuu07NmjXTfffdl2MeAAAAdsAZigAAAMg36enpeuKJJzR8+HBFRERo6tSpGj16tFq0aKHy5cvr9ddf1+rVqzV58mQ1bdpULVq0kJRZTBw2bJg6duyo1157TcnJyXr99dc1atQo30+wFy1apDfeeEMPPvigmjdvrnPnzmnbtm06f/686tSpo+nTp2v06NF65JFH1Lp1a0lSxYoVJUmnTp3Svffeq4oVK+r06dOaNWuWhg0bpmXLlikw8I+3xLt27dKZM2f0+OOPKykpSc8//7yefvppxcfHKyYmRiNGjNA777yjBx54QF999ZVCQ0N9bVeuXKmqVavqueeeU2JioqZOnaoHHnggx0/IAQAA/B0FRQAAAOSb9PR0jR07Vh07dpQkeTwejRw5Us2aNdP48eMlSW3atNGKFSu0YsUKX0HxlVdeUePGjTV9+nQ5HA5JUv369dW3b1+tW7dOHTt21I4dOxQVFZXtrMhu3br5/r9hw4aSpJo1a+b4efbkyZN9/5+RkaHo6Ghdf/312rRpkzp06OB7LikpSTNmzPD9nPvnn3/W+++/r+eee0633HKLpMwiZb9+/bRx48Zs8z9//rzeffddlSpVSpIUGRmp4cOHa/369bruuutMrFUAAICihZ88AwAAIN84nU61bdvW9+9atWpJktq1a+d7LCAgQDVq1NDRo0clScnJyfr+++/Vq1cvZWRkyO12y+12q1atWqpcubJ27twpSWrUqJF27dqlyZMna+vWrUpPTzfcr3Xr1mno0KG65ppr1KhRI11//fWSpN9++y3bdA0aNMh2bcjc+p/1WFb/s7Ru3dpXTJSktm3bqkyZMvrvf/9ruJ8AAAD+gDMUAQAAkG9KliypoKAg379dLpckZSu0ZT2empoqKfPuzBkZGZo8eXK2MwmzHDlyRJI0YMAAnT9/XnPmzNEHH3ygUqVKKSYmRmPHjlXJkiUv2acdO3Zo1KhR6tq1q+6++26VL19eDodDgwcP9vUhS+nSpXP088/9z1q+P7ctX758jnmXK1dOJ06cuGTfAAAA/BEFRQAAAFiqVKlScjgcuvfee7P9hDhL2bJlJWWe/XjHHXfojjvu0LFjx7Rs2TK98sorKlu2rO6///5Lvv6qVasUFham119/XU5n5g904uPj8305/nwDGUk6ffq0KlSokO/zAgAAsBIFRQAAAFgqJCREzZs314EDB9SkSRNDbSpVqqQ777xTsbGxOnDggKQ/zib885mDKSkpcrlcvmszStLSpUvzqfd/2Lx5s86dO+c7m3Hjxo06e/asmjVrlu/zAgAAsBIFRQAAAFju8ccf1x133KGHHnpIffr0UenSpXX06FFt2LBBAwYMUOvWrfXMM8+odOnSat68uUqXLq3vv/9ee/bs8d0spUKFCipdurSWLVumatWqKSgoSFFRUWrfvr0+/PBD/eMf/1D37t21fft2LV68ON+XITQ0VHfffbfuvvtunTt3TlOnTlXTpk25IQsAALAdCooAAACwXIsWLfTJJ59o2rRpGj9+vNLT0xUZGak2bdqoZs2akqTo6GjNmTNHc+fOVXJysqpXr67x48fr5ptvlpT5k+jJkyfr1Vdf1fDhw5WWlqbVq1erY8eOGjt2rD766CMtWLBALVq00DvvvKOePXvm6zJ0795dkZGRevbZZ5WYmKh27drp73//e77OAwAAoChweL1er9WdAAAAAPxZly5d1KlTJz3zzDNWdwUAAKDAOa3uAAAAAAAAAAD/QUERAAAAAAAAgGH85BkAAAAAAACAYZyhCAAAAAAAAMAwCooAAAAAAAAADKOgCAAAAAAAAMAwCooAAAAAAAAADKOgCAAAAAAAAMAwCooAAAAAAAAADKOgCAAAAAAAAMAwCooAAAAAAAAADKOgCAAAAAAAAMAwCooAAAAAAAAADPt/rdspBTqmVYkAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def plot_alert_timeline(df: pd.DataFrame) -> None:\n", + " \"\"\"Plot a timeline showing when ALERT records occur.\n", + "\n", + " Parameters\n", + " ----------\n", + " df : pd.DataFrame\n", + " DataFrame with columns: timestamp (datetime or str), status, city.\n", + "\n", + " Returns\n", + " -------\n", + " None\n", + " \"\"\"\n", + " ts_df = df.copy()\n", + " if \"timestamp\" in ts_df.columns:\n", + " ts_df[\"timestamp\"] = pd.to_datetime(ts_df[\"timestamp\"], errors=\"coerce\")\n", + " ts_df = ts_df.dropna(subset=[\"timestamp\"])\n", + " else:\n", + " ts_df[\"timestamp\"] = pd.date_range(start=\"2025-01-01\", periods=len(ts_df), freq=\"5min\")\n", + " alerts = ts_df[ts_df[\"status\"] == \"ALERT\"]\n", + " normals = ts_df[ts_df[\"status\"] == \"NORMAL\"]\n", + " fig, ax = plt.subplots(figsize=(12, 3.5))\n", + " ax.scatter(normals[\"timestamp\"], [0] * len(normals), color=\"steelblue\", s=20, alpha=0.5, label=\"NORMAL\", zorder=2)\n", + " ax.scatter(alerts[\"timestamp\"], [1] * len(alerts), color=\"tomato\", s=50, marker=\"^\", label=\"ALERT\", zorder=3)\n", + " ax.set_yticks([0, 1])\n", + " ax.set_yticklabels([\"NORMAL\", \"ALERT\"], fontsize=10)\n", + " ax.set_xlabel(\"Timestamp\", fontsize=10)\n", + " ax.set_title(\"Alert Timeline\", fontsize=12)\n", + " ax.legend(fontsize=9)\n", + " ax.xaxis.set_major_formatter(mdates.DateFormatter(\"%m-%d %H:%M\"))\n", + " fig.autofmt_xdate(rotation=30)\n", + " plt.tight_layout()\n", + " plt.savefig(\"logs/alert_timeline.png\", bbox_inches=\"tight\")\n", + " plt.show()\n", + " logger.info(\"Alert timeline plot saved.\")\n", + "plot_alert_timeline(df_eval)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "b72963fa", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:58:54.972490Z", + "iopub.status.busy": "2026-05-07T05:58:54.972435Z", + "iopub.status.idle": "2026-05-07T05:58:55.261848Z", + "shell.execute_reply": "2026-05-07T05:58:55.261481Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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BNu2bcOlS5dw+PBhqKmpwdLSEl9++SU+//zzUq+zM3jwYGzatAnbt2/H2LFjUatWrTL3PyUlBadOnUKjRo1gY2NTZL1u3bpBV1cXISEhUkmbmJgY/Pbbb3L3ad26dYlJG319fbi7u0ve5pM3TZyiPzOFPXr0CFFRUejcuXOx16hPnz5YvXo1QkJCpJI2d+/elfl9JdavXz9J0ubhw4eYN2+e3Hpjx45VKGkDAF9//TXGjx+PdevWYfXq1QrtQ0RERESAmkhZK5USERERERFRpZg8eTIePXqEI0eOQEtLq7K7Q0REREREZcQ1bYiIiIiIiKqw3NxcXLlyBbNnz2bChoiIiIioiuObNkRERERERERERERERCqAb9oQERERERERERERERGpACZtiIiIiIiIiIiIiIiIVACTNkRERERERERERERERCqASRsiIiIiIiIiIiIiIiIVwKQNERERERERERERERGRCmDShoiIiIiIiIiIiIiISAUwaUNERERERERERERERKQCmLQhIiIiIiIiIiIiIiJSAUzaEBERERERERERERERqQAmbYiIiIiIiIiIiIiIiFQAkzZEREREREREREREREQqgEkbIiIiIiIiIiIiIiIiFcCkDRERERERERERERERkQpg0oaIiIiIiIiIiIiIiEgFMGlDRERERERERERERESkApi0ISIiIiIiIiIiIiIiUgFM2hAREREREREREREREakAJm2IiIiIiIiIiIiIiIhUAJM2REREREREREREREREKoBJGyIiIiIiIiIiIiIiIhXApA0REREREREREREREZEKYNKGiIiIiIiIiIiIiIhIBTBpQ0REREREREREREREpAKYtCEiIiIiIiIiIiIiIlIBTNoQERERERERERERERGpACZtiIiIiIiIiIiIiIiIVACTNkRERERERERERERERCqASRsiIiIiIiIiIiIiIiIVwKQNERERERERERERERGRCmDShoiIiIiIiIiIiIiISAUwaUNERERERERERERERKQCmLQhIiIiIiIiIiIiIiJSAUzaEBERERERERERERERqQAmbYiIiIiIiIiIiIiIiFQAkzZEREREREREREREREQqgEkbIiIiIiIiIiIiIiIiFcCkDRERERERERERERERkQpg0oaIiIiIiIiIiIiIiEgFMGlDRERERERERERERESkApi0ISIiIiIiIiIiIiIiUgFM2hAREREREREREREREakAJm2IiIiIiIiIiIiIiIhUAJM2REREREREREREREREKoBJGyIiIiIiIiIiIiIiIhXApA0REREREREREREREZEKYNKGiIiIiIiIiIiIiIhIBTBpQ0REREREREREREREpAKYtCEiIiIiIiIiIiIiIlIBTNoQERERERERERERERGpACZtiIiIiIiIiIiIiIiIVACTNkRERERERERERERERCqASRsiIiXp3r07FixYoNQ2r169Cmtra1y9elWp7ZbGtm3b0KNHD+Tl5clsy8nJUcoxhgwZglWrVimlLSIiqhpevnwJa2trhIWFlVjX398f1tbWUmUV8bmrqioqHliwYAG6d++u1DZLQyQSYcCAAVizZo3S2ly5ciW8vb2V1h4REdGnJCwsDNbW1nj58qVS2x0zZgzGjBmj1DZLIyMjA507d0ZISIikTBxfvnnz5qP25dy5c3B0dERSUtJHPS5VLUzaEAGwtrZW6J8igwrV1aNHj+Dv76/0D25l6d69u+Q+2djYoG3btvDw8MA333yDf//9t7K7V6KNGzfi9OnTld0NGWlpadi0aRN8fX2hqakpKU9OToavry8cHBzQvXv3IgeRYmNjsXTpUvTq1QutWrWCo6MjhgwZgg0bNiA1NVVSb8KECdizZ89HD5aIiKo78R/e//zzj9ztEyZMqNRBe2WKj4+Hv78/7t+/r9R2xQkT8T87Ozt06tQJY8aMwcaNG1X+D25VjuEiIiIQExODsWPHSspSU1MxZ84cODs7o3v37lKDK2KvXr2Co6MjoqKiZLaNGzcO0dHROHPmTIX2nYiIiieOQcT/WrZsic8++wx+fn6Ij4+v7O6ppMLXzN7eHi4uLvD19cWOHTuQlpZW2V0s1t9//w1/f3+pv/VVxY4dO6CpqYmBAweWaf+CY07W1tZwdHSEt7c3Dh48WOq2unXrBktLS2zatKlMfaFPg2bJVYiqv8JP+AcHB+Pff//FsmXLpMrbtGnzMbulUh49eoR169ahXbt2qF+/fmV3Ry5ra2v4+voCANLT0/HkyRMcP34cwcHB8PHxgZ+fX4Ue//jx41BTUyvTvps2bYK7uzvc3Nykyp2dnXHr1i1oaWkpo4ulFhoaiuzsbHh6ekqVr169GtnZ2fD398ft27cxY8YMnDlzBjVq1JDUuXjxIqZNmwZ1dXUMHDgQ1tbWyMvLw507d/D777/j+vXr2LZtGwDAzc0NBgYG2L17N2bMmPERz5CIiCpLvXr1cOvWLamHAkqj8OduQkIC1q1bh3r16qFFixbK6qbEqFGj4ODgAKFQiKSkJERFRcHf3x+BgYFYs2YNOnbsqPRjipUnHiguhvv+++8hEomU1c1S27p1K9zd3WFiYiIpW7lyJa5evYqpU6ciJiYG33zzDZo2bSoVh69YsQI9e/aEo6OjTJsCgQDdu3fH1q1b0aNHj49yHkREVLSpU6fC0tISOTk5+Pvvv3Hw4EFcu3YNERER0NPTq+zuqSTxNcvLy0NiYiKuXbuGH3/8EUFBQQgICICNjU2FHXvgwIHo168ftLW1S71vVFQU1q1bh0GDBsHQ0FBq29atW5XVxVLLzc3F9u3bMWLEiDKdl1jBMaeEhAQEBwdj/vz5yMzMxIgRI0rV1rBhw/DTTz9h6tSpMDAwKHOfqPpi0oYIkMm0//XXX7h161aZM/BVQUZGhtQAe2XJycmBurp6mQdsCjIzM5O5Z3PmzMHs2bMRFBSEhg0bYuTIkeU+TlHK8+FfFHV1dejo6Ci9XUWFhobis88+k/le+eeff/Dzzz/DxsYGbm5uOH/+PJ48eQI7OzsAH6a8mT59OgQCAXbs2AFzc3Op/WfNmiX15Ky6ujrc3d1x8OBBSaKHiIiqNzU1tXJ9xlXE525xnJyc0K9fP6my6OhofPHFF5g2bRqOHDkCgUBQIceuqHigsh4KAYB79+7h/v37Mg9rnDt3DnPnzpU8MPLgwQOcPXtWkrT566+/cOnSJRw/frzItvv27Ytp06bh2bNnaNSoUQWdARERKcLFxQWtW7cGAHh7e8PIyAiBgYE4c+YM+vfvL3cfVRmvKI3MzEylJaEKXjPgw5vRf/31FyZOnIjJkyfj6NGj0NXVVcqxCtPQ0ICGhobS2/3YcVtB586dQ1JSEvr06VOudgqPOXl6eqJXr14ICgoqddKmd+/eWLZsGY4dO8ZpXUkujooRlUJ4eDiGDBmCVq1awdnZGdOmTcOLFy+k6owZMwa9e/fGgwcPMHr0aDg4OKBHjx44evQoAODGjRsYOnQoWrVqBXd3d1y8eFFqf/Gcmo8ePcLs2bPh5OQEZ2dnfPvtt0hPT5fp06VLlzB69Gg4OjrC0dERvr6+MtOCLFiwAPb29nj58iUmTpyINm3aYMKECQA+DDb4+fnBzc0N9vb2aN++PWbOnIlXr15J9g8LC8P06dMBAGPHjpWZLq6oOeULz1kqnl7k8OHD8Pf3R7du3eDg4IDXr18DAJ4+fYrp06ejffv2sLe3h6enZ7F/kCtCV1cXq1atgrGxMTZu3Cj1NKlIJMLOnTvh4eEBe3t7dOzYEQsXLpSa5mTChAno1q2b3KdQfXx8pKaUKXwdcnJysHbtWgwZMgTOzs5o1aoVvLy8ZKZBs7a2RkZGBg4cOCC5tuLrVtQc9teuXcPo0aPRunVrODk5YcKECfjvv/+k6oi/l548eYIFCxagbdu2cHJygp+fHzIzM0u8di9evMCDBw/QuXNnmW3169fHjh07EBMTg2PHjuH58+ewsLCQbN+yZQvS09OxbNkymYQN8CHYmTx5slRZp06dEBcXhzt37pTYNyIiqhjFrTNjbW0Nf39/ydfiz5nHjx9jzpw5cHJyQvv27fHLL79AJBIhPj4ekydPRps2bdCpUyds2bJFoWPduHEDQ4YMgb29Pdzc3LB37165fS34uXv16lV4eXkBAPz8/CSfp/7+/ggODoa1tTXu3r0r08aOHTsk51AWNjY2WLhwIVJTU7F7926pbQkJCVi0aBE6d+4MOzs79OnTB3v27JFsT0xMRMuWLeWu5xIXFwcbGxvJ9ZYXDygjhpO3pk1+fj42bNiAnj17ws7ODt26dcOqVauQlZUlVa979+7w9fXFjRs34OXlBXt7e/To0UPhaUJOnz4NDQ0NdOjQQao8KytL6ulcIyMjSdySl5eHZcuWYdKkScUmyDp16gQ1NTWVnHqWiOhTJ/69L562s7jxCkCxcZjnz59j+vTpcHFxgZ2dHVxcXDB16lQkJCRI6vz1118YNWoUnJ2d4eDgADc3NyxdulSyvai1XOR9BovHfe7fv48xY8agdevWWLJkCQDFxhnKomPHjpg8eTJiY2Nx+PBhqW0ljaXcvn0b1tbWcqccvXHjhlRsIO863LhxAzNmzICrq6vk+i5evBjv3r2T1PH395fMYtOjRw9JzCG+bvLWtMnMzMTKlSvRrVs32NnZoVevXvj9998hFAql6llbW+Pbb7/F6dOn0b9/f9jZ2aFfv364cOGCQtfu9OnTMDMzQ/PmzeVuf//+fZnGTMzMzNCkSRPExsYCUCw2E6tduzasra1x6tQphc6BPj1804ZIQb///jt++eUXuLu7Y/DgwZI/zkeMGIHDhw9LTeuQlpaGCRMmoE+fPujduzf27t2LOXPmQCQS4ccff8Tw4cPRr18/bNu2DdOnT8f58+dRs2ZNqePNnDkT5ubmmDVrFu7fv499+/YhLi4OmzdvltQJDw/H3Llz0blzZ8yaNQs5OTkIDg7GyJEjsX//fjRt2lRSVyQSwdfXF/b29pg3b57kyYnLly/j6dOn8PT0hEAgQExMDPbu3Ytbt25JXld2dnbGmDFjsHPnTkycOBFNmjQBUPbp4jZt2gR1dXWMHTsWIpEINWrUwOPHjzF8+HCYmprC19cX+vr6OHXqFKZPn45Vq1aV660nfX19uLm5Yf/+/Xj06JHkg/q7775DaGgoPD09MWrUKMTFxWHXrl24ffs29u/fDx0dHfTt2xfnzp1DVFSU1Pm+ffsW165dwxdffFHkcdPS0rBv3z707dsXgwcPRk5ODsLDw/H111/j999/R9euXQF8mJ5v8eLFaNWqFYYOHQoAMDU1LbLdK1euwNfXF/Xr18eUKVOQnZ2NPXv2YMSIEdi/fz8aN24sVX/WrFmwtLTErFmzcO/ePYSEhMDExARz584t9rqJ54kXvz1TuE1fX1+EhoZCU1MTCxculPoZiIyMRP369dG2bdtij1GQ+Dh///03WrVqpfB+RERUsvfv38sdLMjLyyt327NmzUKTJk0we/ZsXLhwAZs2bYKRkRFCQ0PRtm1bzJkzB+Hh4fjpp59ga2tb7DRiDx48gK+vL0xMTDB16lTk5+dj3bp1Up8x8jRt2hTTpk3D2rVrMWzYMDg5OQH48Ed+vXr18MMPP+Dw4cOwtbWV2u/w4cOws7OTiplKy93dHYsWLcKlS5cwc+ZMAB/ihGHDhiE/Px8jRoxA7dq18ddff2HJkiV49+4dJk+eDFNTU7Rr1w7Hjh2Tedvk2LFjEIlE6Nu3b5HHragY7ttvv8X+/fvRq1cv+Pj44M6dO9i6dSsePnyI33//XWpKOvGbtV5eXhg0aBBCQ0OxYMEC2NraFjkwIhYVFYWmTZvKPClsb2+PwMBANGnSBC9evMDFixfx/fffAwB27dqF3NxcfP7558W2XbNmTTRo0AB///13sfWIiOjji4mJAQAYGxtLyooar1BkHCY3Nxe+vr7IysrCyJEjYWZmhjdv3uDixYtISEiAQCDAo0eP8NVXX8HKygpTpkyBnp4eYmJicOnSpTKfx/v37+Hr64tevXqhf//+kjEdRcYZymrgwIH45ZdfcOnSJcnYgSJjKfb29mjYsKHctzqOHTsGbW1t9OzZs8jjHj9+HO/fv8fQoUNRu3ZtPHjwACEhIXj48CH27t0LNTU19OzZE8+ePUNERAT8/PxQq1YtACgyxhKJRPj666/x559/YsiQIbC1tcWVK1ewevVqvHz5UiqhBnyY7ePs2bMYMWIE9PX1sXPnTkybNg1nz56VHKsoUVFRcsc1xMo6ZpKbm4vXr19LvpcVic0KsrW1xcmTJyESico81T5VYyIikjF//nyRnZ2d5OvY2FhRy5YtRf7+/lL1nj9/LrKzsxOtXr1aUjZ69GiRlZWV6MCBA5Kyx48fi6ysrETW1taiGzduSMovXrwosrKyEgUHB0vK1q5dK7KyshL5+vqKhEKhpHzNmjUiKysr0Z9//ikSiUSi9PR0kbOzs2jBggVSfXr37p2oQ4cOolmzZkmdj5WVlejHH3+UOdeMjAyZsps3b4qsrKxEBw8elJQdO3ZMZGVlJbpy5YpMfVdXV9H8+fNlykePHi0aPXq05OsrV66IrKysRF27dhWlp6dL1R03bpyob9++oszMTJnyLl26SF0LeVxdXUVffPFFkdsDAwNFVlZWotOnT0udY8H7JBKJRNevXxdZWVmJ9u7dKxKJRKL379+LWrVqJfr++++l6u3atUtkZWUlunfvnlQfCl6HvLw8UXZ2ttR+2dnZon79+ok+//xzqfLWrVvLvYbia1bwunt6eoratWsnSkpKkpQ9ffpUZGtrK5o6daqkTPy9VPh75Ouvvxa1a9dO5liF/frrryIrKytRSkqK3O0ZGRmif//9V5SQkCBV/v79e5GVlZVo0qRJJR6jMFtbW9HixYtLvR8REckXGhoqsrKyKvafq6urpP6LFy9EVlZWotDQUJm2rKysRGvXrpV8Lf6c8fPzk5Tl5eWJPvvsM5G1tbUoICBAUp6SkiJq1aqVaPbs2cUea/LkySI7OztRbGyspOzJkyeili1biqysrKT6U/hz99atW0X2fcaMGaLOnTuL8vLypNq1srISbd++vegLKPrfZ3FERESRdQYMGCBydnaWfL148WJRp06dRG/fvpWqt2jRIlGrVq0kn63BwcEiKysr0d27d6XqDRkyRDRgwACZPhSMB5QRw82fP1/q/t+/f19u7CC+15GRkZIyV1dXkZWVlejatWuSsrdv34rs7OxEK1askDlWYZ999pncWCE6OlrUtWtXyffnlClTRPn5+aLExESRk5OT6Ny5cyW2LRKJRF988YWoV69eCtUlIiLlE8cgFy5cEL19+1YUFxcnOnLkiKhdu3aiVq1aiV6/fi0SiYoer1B0HEb82XXs2LEi+xIUFCSysrKS+VyW198XL15Ilcv7DBaP+xSOIRQdZyipD1FRUUXWcXJyEnl6ekq+VnQs5ZdffhG1aNFC6hrk5+eLOnfuLPV5LO86yIs5Dh8+LLKyshJdv35dUrZlyxa511Akkh0fOn36tMjKykrm/i5YsEBkZWUlevDggaTMyspKZGtrK3r27JmkTHzfd+7cKecq/U9ubq7I2tpa9MMPP8hsK82Yiaurq2js2LGit2/fit6+fSu6f/++aObMmSIrKyvJeJGisZnYxo0bRVZWVpKfBaKCOD0akQJOnjyJvLw89O3bF0lJSZJ/BgYGsLKykpm6SldXFwMGDJB83aRJExgaGqJRo0aSJz8BwMHBAQBkXu0FgNGjR0tl2sWvkZ49exbAhwx+SkoKPDw8pPqUn5+Ptm3byvQJgNz1XApm+tPT05GcnIxGjRrB0NBQ7jQiyjBw4ECp+WnfvXuHy5cvo0+fPsjIyJA6ny5duiA+Ph5Pnz4t1zH19fUBQDLF3LFjx1CjRg106dJF6nhNmjSBqamp5PoZGBiga9euOH78uNQruseOHUOTJk2KXehYQ0NDMm9rTk4O3r17h7S0NLRt27bM1zYhIQH37t3DoEGDpJ4madSoEbp3746LFy8iPz9fah/xEzhibdu2lfSlOO/evYO6urrMW2Bienp6aNWqFczMzKTKxe2Kr3lpGBkZITk5udT7ERFR8RYvXozAwECZf+JYpDwKPrGpoaEBOzs7iEQiyXRlAGBoaIjGjRvLTDlSUH5+Pi5duoTu3btLTbnZuHFjuLi4lKuPnp6eePPmDS5fviwpO3z4MDQ1NWXWqSmLGjVqSGIMkUiEEydOSN6oLRhndO7cGVlZWfj3338BAL169YKWlpZkGl3gQ1x4+/btEvtVETHc+fPnAXyYArYgHx8faGho4Ny5c1LljRo1grOzs+RrExMTNG7cWG5sW9i7d+9gZGQkU25tbY0TJ05g//79OHnyJPz9/aGuro6ff/4Zbdu2RdeuXXHjxg14e3uja9eu+OGHH5CTkyPTjqGhIWMKIiIV8OWXX6Jjx47o2rUrZs6cCVNTU2zcuFFmGu3C4xWKjsOI/+68dOkSMjIy5PZB/DftmTNnZKbeKitNTU0MGzZMqkzRcYbyKBhzlGYspV+/fsjPz8eJEyckbV27dg1v3rxROOYQiURIS0tDUlISHB0dAaBcMYd4BpaCxo0bBwAyMUf79u3RsGFDydc2NjYwMDAoMeZISUmBSCSSG3OIKTpmcuXKFXTs2BEdO3bEwIEDcfLkSYwYMQJz5swBUPrYTDwdLOMVkofToxEp4NmzZwBQ5KJllpaWUl+bm5vLLKRes2ZN1K1bV6YM+PAhUljBDyPgwx/BRkZGkrkyxR+84g+0wgofX11dHfXq1ZOpl5KSgtWrV+PEiRNS85ECH173rQgNGjSQ+jomJgYikQj+/v5Sc+UXJA50ykoc1IgDumfPniEjIwOdOnWSW//t27eS//ft2xcnTpzA9evX0b59e8THx+PmzZsya7LIExISgqCgIDx+/FhqXZyyvvoqngu18BRowIfk4IkTJ5CcnCw1vVrBgS/gf4FBSkoKDAwMij2eSCQq9au64jblrcFUktIei4iIFGNvby+1oK3Y9u3bkZiYWK62C3/O1KxZE1paWjJJ/Zo1axZ7rKSkJGRlZcldOL68i8m7uLjAzMwMhw8fRpcuXQB8mGbWxcUFtWvXLlfbwIcFk8UxRlJSElJSUhAaGorQ0FC59cVxhpGRETp37oyjR49K/uAXJ3CKmxoNqJgYLjY2FmpqajJxRs2aNWFmZiaJQ8UK33vgwznJi23lEclZMxAAdHR0YG9vL/n61q1bOHLkCMLDw/Hu3TtMmDABX331Fdq3bw8/Pz9s3LgR06ZNk2mbMQURUeVbvHgxmjZtCm1tbVhYWKBu3boyv5/ljVcoOg5jaWmJcePGITAwEIcPH0abNm3g6uqKAQMGSB507Nu3L/bv34/Fixfj559/RocOHeDm5oY+ffpAU7NsQ6MCgUBmqrPSjDOUVUZGhiR2Kc1YipWVFZo3b46jR49ixIgRAD7EHHp6enB1dS32mHFxcVi1ahXOnz8v83d+eWKO2rVrS61jB3wY61BXV5eJOQqPpwEfYo7U1FSFjldUzAEoPmZiZ2eH2bNnQ01NDbVq1YKlpaXUw6qljc3EfWK8QvIwaUOkAPGTGJs3b5b7gV74g7pwwqSk8uI+PIoi3mfFihVyF3ovTFNTU27fZ8yYgb///hvjxo1Dy5Ytoa+vDzU1NcycObNM/SooPz9fMhdtQYXnLhdfXx8fH8lTqYWVNC96SR4+fAjgf8kwoVAIY2Nj/Prrr3LrFwwcunXrBn19fRw9ehTt27fHsWPHIBQKSxxMOXz4MBYvXgxXV1eMHz8eJiYm0NTURGhoKCIiIsp1PqVR1u+7WrVqQSQS4f3798U+lVKYgYEBBAIB/vvvv1L1EwBSU1NLnI+WiIgqTlF/NBZ+i7MgeZ8zRbVT3tiirDQ0NODh4YG9e/ciIyMD0dHRePHiBWbNmlXutnNzc/Hs2TNJrCKOa/r3748hQ4bI3adZs2aS//ft2xfz5s3Dv//+CwcHBxw9ehQODg6oX79+scetyBhOUUXFGIqoVauWQskdkUiEpUuX4vPPP0fDhg1x8OBBGBkZSRap/vLLL+UmbVJTU6XWSyAiospR1IMjBckbryjNOMyCBQswZMgQREZG4tKlS1i5ciU2bNiAXbt2oVmzZtDV1cWuXbtw/fp1nD9/HpcuXcKcOXMQGBiIPXv2QFdXt8jYpag3c+StTVOacYayeP36Nd6/fy95ELa0Yyl9+/aFv78/EhISYGJigpMnT8LV1VVqJpTC8vPz8cUXXyApKQkTJkxA06ZNoaenB6FQiC+//LLSY46Sjm9sbAw1NbViYw5F2zY2Ni4yIQeUPjYTJ5w4BkLyMGlDpADxB6KFhYXUH9kV6fnz51JPOYqf2hQ/fSJ+qsTExKTYD43ipKSk4PLly5g6dSqmTJkiKc/OzpZ5WqG4zH9RTze8evVK5i0kecR1NDQ0ynwuxUlPT8fp06dRt25dySJ4DRo0wOXLl+Hg4FDiNF66urro3r07Tp48iW+//RbHjh2DjY1NiYsWHz9+HJaWltiwYYPU9SvqqVtFiJ8AkTdd3JMnT1CjRg2lfeCLz+/ly5elStoAQPfu3bF3717cvHlTakrA4sTHxyM3N7dcb1QREVH5iH/fF/5cF7/pWZFMTEygq6srebK2IHllhZX0lKKnpye2bduGU6dOISoqCgYGBujRo0cZe/s/J06cQFZWlmQKNxMTE+jr6yMvL0+huKZHjx7Q1dXF0aNHUbNmTURHR8PPz6/YfZQVwxVWr149iEQiPH36FNbW1pLytLQ0vHnzBt26dVO4rZI0bdq02OnyxPbv34/4+HhMnDgRACSLSosJBALEx8fL7Pfy5cuPFrcTEZHylXYcpnnz5mjevDkmTJiA6OhoDBkyBEFBQfjhhx8AfBiYb9++Pdq3b4958+Zhz549WLJkCU6ePIkBAwZIEiqF34go/MZHSX1WdJyhLA4dOgQAkpijtGMp/fr1w2+//Ybjx4+jcePGSE5OLnFqtP/++w9PnjzBihUrMGjQIEm5vNistDHH5cuX8f79e6kp2Z89ewahUCh3ppiy0NDQQMOGDRWKOcqjNLGZmHispfDb6UQAwDVtiBTg7u4ODQ0NrF+/Xm52PCkpSenH3LVrl9Sxdu7cCQCSP5a7dOkCQ0NDbNy4Ue483or0SfwWTOFzCgoKknmaRDw3p7wPG0tLS/z7779S/Th79izi4uJK7AMA1K5dG+3bt0dISIjcP7rLc32zsrIwb948vHv3DhMnTpQEEX379oVQKMT69etl9snPz5d5CqNfv35ISkpCWFgY/vnnH4Xmv5d3fV+8eIHTp0/L1K1Ro4ZCT5sKBALY2tri4MGDUq/bxsTEIDIyEl26dJH7dlNZtGnTBgBw586dUu/75ZdfokaNGli0aJHce5qYmIiAgACpMvFxxMclIqKPz8DAALVq1cKNGzekyvfs2VPhx9bQ0ICLiwvOnj0rlSR6+vQpLl26VOL+xcUqwIe1Ulq2bImwsDAcO3YMvXv3lvuUbGlER0fjxx9/hJGREUaNGiU5D3d3d5w5cwbR0dEy+xSOawqun3fkyBGoq6sXORWMmLJiuMLET+lu375dqnz79u3Iz88vcfqU0nB0dMTjx4+RlZVVZJ3U1FT88ssvmDt3rmTwy9TUFM+fP0deXh4A4PHjx1LTwgIfBtxiYmIk8+0TEVHVo+g4TFpamuQzQaxp06bQ0dGRfPbJWzPE1tYWwP+SNOIk0fXr1yV18vPzERwcrHCfSzvOUBp//fUXAgICUL9+fckayqUdS2nYsCFsbW1x9OhRycMin332WbHHFb+FUvgebNu2TaZuaWKObt26QSgUYseOHVLlgYGBku3K0qZNmzKNa5RGaWIzsbt378LBwYHTo5FcfNOGSAGWlpaYPXs2Vq1ahVevXqFHjx4wNDTEy5cvcebMGfTt2xdTp05V6jHj4+Mxfvx4uLq6Ijo6GsHBwXBxcUHnzp0BfPgDf8mSJZgzZw4GDRqEfv36wdTUFK9evcLFixfRvHlzrFixothjGBgYoF27dtiyZQtyc3NhYWGBmzdv4vr16zLTSbRs2RIaGhrYtGkTUlNToauri1atWsHS0hLe3t44ceIEvvzyS/Tp0wcxMTEIDw+XWbumOP/3f/+HESNGYMCAAfD29kaDBg3w9u1b/Pvvv3j8+DFOnTpVYhtv3ryRPHmSkZGBx48f4/jx43jz5g2++OILDB8+XFLX2dkZo0aNwtatW/HgwQN06dIFWlpaiImJwYkTJzBt2jQMHjxYUt/FxQVGRkZYvnw5gJLnmQcgeTtn0qRJ6N69O+Lj47Fnzx40btwY9+/fl6prZ2eHv/76C1u3bkWdOnVgYmKCjh07ym133rx58PX1xbBhwzB06FBkZ2djz5490NHRwcyZM0vsl6IsLCzQokUL/PnnnzILLJbE0tISa9aswfTp09GvXz8MHDgQ1tbWyMvLw71793DkyBGZ5Mzly5dRp04d2NnZKe0ciIio9Ly9vfH7779j0aJFsLOzw40bN+S+4VkRpk6diosXL2LUqFEYMWIEhEIhdu3ahaZNm+LBgwfF7tugQQMYGRnhjz/+QI0aNaCvr4/mzZvDyspKUmfgwIGSz/KBAweWqm83b95EXl4ehEIh3r17h7///huRkZEwMDDAunXrpJ6SnDNnDq5du4Zhw4bB29sbzZs3R0pKCqKjo3Hq1Cncvn1bqm3x+nmBgYFo27ZtiVPfKiuGK8zGxgZeXl7Yv38/0tLS0L59e9y7dw+hoaHo0qVLkVOvlIWbmxv8/f1x5cqVIgdm1q5di0aNGkkGp4APiaWlS5di9uzZcHR0xIYNG+Dl5SW13+XLlyESiZTyJhUREVUORcdhrly5giVLlsDd3V0yW8nRo0eRnp4u+bs9ICAA165dQ7du3VCvXj2kpKRg7969qFGjhuQzqHnz5mjdujV++eUXpKSkwMjICEePHpVJCBWntOMMRbl06RKeP3+O/Px8JCYm4urVq/jzzz9hYWGBDRs2SD10UtqxlH79+uGnn37CgwcP4O7uDm1t7WL70qRJEzRq1AgrV67E69evYWRkhIsXL+L169cydcV/y69evRoeHh7Q0tJChw4d5K4f6Orqis6dO8Pf3x+vXr1Cy5YtcfXqVZw4cQLDhg2Tit/Kq0ePHggLC8PDhw/LPfV+UUoTmwEf1jd68OCB1DgVUUFM2hApyNfXFw0bNkRQUBA2bNgAkUgEc3NzdOjQAb1791b68X755Rds3LgRv/zyC9TU1ODt7Y0FCxZI1enbty8EAgE2btyIwMBAZGdnQyAQoE2bNgoPsq9evRrLli3Dvn37kJubC2dnZ2zfvh3jxo2Tqmdqaorvv/8emzZtwjfffIP8/HwsX74clpaW6NKlCxYsWIDAwED8+OOPsLOzw8aNG7Fy5UqFz7dJkyYIDQ3F+vXrJW+R1KpVCzY2Npg+fbpCbTx48ADz5s2Dmpoa9PX1UbduXbi6usLb2xutWrWSqf/tt9+iZcuW2Lt3L3799VdoaGjAwsICffr0QYcOHaTqamlpoWfPnti/f79C88wDwKBBg/D27Vv88ccfuHz5Mho2bAg/Pz/ExMTIJG38/Pzw7bffYt26dcjIyEC7du2KTNp06NAB27Ztw9q1a7F27VpoaGigbdu2mD17tszCweU1ZMgQrF69GhkZGcXOcytP165dER4ejq1bt+L8+fMIDg6GpqYmmjRpgkmTJmHkyJGSukKhECdOnMCQIUPKNT8+ERGV39dff42kpCScOHECx44dw2effYYtW7YU+bmkTDY2Nti6dSuWL1+OtWvXok6dOpgyZQrevHlTYtJGS0sLq1atwurVq7F06VLk5uZiypQpUn/0e3h44KeffoK5uTmcnZ1L1bfdu3dj9+7d0NLSQs2aNdG0aVNMnToVQ4cOhYmJiVTd2rVrIyQkBAEBAThz5gz27t0LIyMjNGnSRCaeA/63fl7BAaaSKCOGk2fp0qWoX78+QkNDERkZidq1a+OLL77AtGnTlPokqI2NDWxtbXHs2DG5SZv//vsPe/fulXnCuXbt2li7di2WL1+Oy5cvo3v37lLTkADAsWPH4OjoyClXiYiqOEXGYaytrfHZZ5/hwoULCAkJgY6ODpo1a4b169fDzc0NwIdB+7i4OBw4cABJSUkwNjaGo6Mjvv76a6lpuH7++Wd8++23+P3332FoaAgvLy+0b99e5rO1OKUZZyiKv78/gA+xjbGxMaysrLBw4UIMHjwYBgYGUnVLO5bSt29f/PTTT8jIyFAo5tDS0sKGDRuwbNkybN26FRoaGujSpQu2bNkieahYzN7eHrNnz8aePXvg5+cneZNGXtJGTU0N69atg7+/P44cOYJDhw6hbt26mDVrFr788kuFrpOiunXrhtq1a+PYsWMVlrQBFI/NgA/T62ppaSkc99GnR01UWauBEpFc/v7+WLduHS5dusR5LanSpaWlwc3NDdOmTZNKsijbyZMnMXfuXJw6dUpqnnoiIiJlSklJQefOneHr66vUt1OpbCIiIvDNN98gMjJSaWvyJSQkoEePHvj1118lg3VERET0adu0aRP++OMPnDp1ClpaWpXdHQwcOBDt2rXDokWLKrsrpKL4ODMRERXJwMAAX331FbZu3Vqq18JLa9OmTRg1ahQTNkREVKEOHDiA3NxceHp6VnZXCB+maLG0tJRZQ6c8AgMDYW1tzYQNERERSYwdOxa5ubk4ePBgZXcF586dQ0xMDCZOnFjZXSEVxjdtiFQM37QhIiIiUq6//voLT548wW+//YY2bdpg48aNld0lIiIiIiIiubimDRERERERVWsBAQGIiopC69at8d1331V2d4iIiIiIiIrEN22IiIiIiIiIiIiIiIhUANe0ISIiIiIiIiIiIiIiUgFM2hAREREREREREREREakAJm2IiIiIiIiIiIiIiIhUAJM2REREREREREREREREKkCzsjtQHd28eRMAoK7OnBgREVV/QqEQAODk5FTJPSF5GJcQEdGnhrGJamNsQkREn5KyxCX8hFRxQqFQcmOpcvAeVC5e/8rF61+5eP1J1fB7smrgfao6eK+qBt6nqoH36dPFe688vJbKw2upXLyeysNrqTwVeS35pk0FED8t4ujoWO62oqOjAQA2NjblbovKhvegcvH6Vy5e/8pVVa5/VFRUZXeBisG45NPD+1R18F5VDbxPVUPB+8TYRLUpMzYB+DOqTLyWysNrqVy8nsrDa6k8il7LssQlfNOGiIiIiIiIiIiIiIhIBTBpQ0REREREREREREREpAKYtCEiIiIiIiIiIiIiIlIBXNOGiIgqhUgkQmJiIrKyspCfn1/Z3VFJ2dnZAIBnz55VWh80NDSgq6sLU1NTqKmpVVo/iIiIKlpRsYkqfB7T/zA2ISKiT0V6ejqSk5ORl5entDYZ1yiP+Fq+ePFC6bEJkzZERPTRiUQixMbG4v3799DW1oaGhkZld0klGRgYVHYXkJOTg7S0NGRnZ6NevXocHCEiomqpuNhEFT6P6X8YmxAR0acgLy8PsbGx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X31V+/btU8uWLSvlGAAA/mM0Gh1+69q1a6c+ffro/fff11NPPVVqe5vNpsLCQo8uyFcmX1xfKXm9aeDAgbrpppt099136/nnn9ezzz7rZZSltWrVymd1+bof44odO3bowIEDGjZsmEfl3X0/OnPllVeqU6dOeuedd/TXv/7VozgAlkcDfGz16tUaPHiw2rZtq+uuu04vv/yyioqKHPKLp2VOmDBB7du314033qivv/5aVqtVL7/8snr06KEePXpozpw5slqt9rLFy5Ts2rVLI0eOVNu2bXXTTTfpiy++KBXHtm3blJycrHbt2qlbt256+umnHe6sLJ4eum3bNj344IPq1KmTpkyZIkn66KOPdOutt6pLly7q3Lmzxo4d67D0x/z587VgwQLl5OTYp42OHTtWkvNlN7Kzs0stHVc85XbhwoXq3bu32rVrp6ysLJfa0B1Go1F//etfFRwcrPfee8/lNsrJyVGHDh305ptvlqrzwQcf1KhRoxza8dJptosXL9af/vQnJSUlqXv37rr77rt1+PBhe/5jjz2mNWvW6MCBA/b2K17ixVn77d+/XxMnTlSHDh2UlJSkBx98UMePH3fYJjExUQsXLtT8+fPVo0cPde3aVdOnT6/wbtqcnBx99tlnuvHGGx3SGzVqpP/7v//T8ePHtXPnTh05ckQJCQmSLnY+33vvPd1www0OAzbFQkJC1L17d/vrG264QefOndOXX35ZbiwAgMBW1tISkydPtvcDpN/7K3v37tWoUaPUrl07DR8+XHv37lV+fr6efvppde7cWX/84x+1ZMkSh7qc/Q5+9913GjFihNq2batBgwY5/T25tNzq1as1ffp0SRcHVhITE9WnTx9lZGSoTZs2WrFiRanyycnJ9n6QO+hnuN/PKF7SJSQkRNLFO1qLly8pKCjQzJkz9dhjj5V5MS4hIUHt2rW7bJYkBoDLTcOGDRUbG6ujR49K+v2368svv9SQIUPUtm1bbd26VdLFmw3vuOMO+2/YI488UmrJzTfeeEP9+vVT27Zt1a1bN6WkpOjXX391Kd/dvs+uXbs0atQotW3b1r5M6cGDB3XvvfcqKSlJHTp00KRJk3TkyBGP2+ePf/yj+vfvr48++shhGfK0tDRNnTpVXbt2Vbt27XTbbbdp9+7d9vyylkj94osvlJiYqEOHDkkqvTxaamqq7rnnHvXs2VMdOnTQ0KFD9dFHH9nzd+zYoTvuuEPSxRs2i/sexXkl2y8/P18zZ85Uz5491bZtWw0dOlSff/65Q0zFse7YsUPDhg1Thw4dNHLkSIfjKctHH32kzp0722+yuVR2drYeeeQRdezYUb1799bChQsrrK/k+7GiflixAQMG6OOPP5bFYqlwH4AzzLQBfOitt97Siy++qHHjxumxxx7TwYMH7QMOU6dOddh22rRpGj16tMaPH6833nhD999/v4YPH67z58/rhRde0A8//KD58+erRYsWGjx4sL1cYWGhHnroIU2YMEGNGjXSe++9p/vvv98+GCRJmzZt0kMPPaQRI0bogQce0KlTpzRnzhxlZ2fr5ZdfdojjySef1JAhQ/Tqq6/aZ7kcPXpUw4YNU+PGjVVQUKANGzbotttu07p169S0aVMlJycrLS1N69evt08b9WRa7meffaYrr7xSf/3rX2U0GhUeHu5WG7oqOjparVu3Vmpqqj2tojYKDw9Xnz59tGHDBk2cONFe7vz589q2bZv+8pe/lLm/tLQ03X777WrYsKHOnz+v999/X6NHj9ann36q6OhoTZ48WRkZGTp06JB9KRBnHQpJOnHihG6//XZdccUVevHFF5Wfn6+XX35Zt99+u9atW+fQ7u+8846SkpL0/PPP6+eff9asWbNUp06dctvt+++/V05OjpKSkhzS+/fvr3Xr1ql3794KCgrS448/bo9x9+7dysnJ0XXXXVdOq/+udu3auuqqq/T111/rhhtucKkMAKByOfuD8tIbR9xVWFioadOmKSUlRXXr1tXs2bN1//33q1OnTqpTp45eeeUVbdmyRTNnzlS7du3UqVMnp/WcOnVKEydOVGJiol555RVlZ2drxowZysnJ0TXXXOO0zPXXX697771X//znP+3LmQUHBys2Nlb9+vWzL2lW7MCBA9q1a5fHy3jSz3CvnxEdHa0rrrhCb7/9tkaNGqUVK1bYZ/IuWbJEjRo1qrC/0LFjR6fP2wMAVH/nz59XVlaW4uLi7Gnp6el69tlnde+996pBgwZq2LChUlNTNXbsWPXq1Usvv/yycnNz9corr2jy5Mn2pe0/+ugjzZ07Vw8++KA6dOigc+fOaefOnbpw4YJL+e4oLCzUI488opSUFD300EOKjo7Wr7/+qtGjR+vqq6/W888/L4PBoNdff10pKSnatGmTx7OFevbsqU2bNmnv3r3q0qWLzp49qzFjxig8PFxPPvmkIiIitHz5co0bN06fffaZ6tSpo5tvvllr1qzR//73P4dlvtavX6/WrVurWbNmTvd1/PhxderUSbfeequCg4P13Xff6YknnpDNZtPw4cPVunVrPfXUU3rmmWc0c+bMMuspNnXqVG3fvl1//vOf1axZM61du1YPPPCAXn31VftKH9LFPuCzzz6rSZMmKSIiQnPmzNH999+vzz//vNwVPL7++mv96U9/cpr39NNPa+jQoXr11Ve1efNmzZ49W4mJifrjH/9YZn0l348V9cOKderUSZmZmfrpp5/s/RzAHQzaAD5y/vx5zZs3T3feeacefvhhSdK1114rs9ms559/XhMnTlRMTIx9+9tvv11jxoyRJMXHx2vw4MHavXu3vXNx3XXXaevWrdq0aVOpQZt7771XI0eOlHTxx7p///7617/+pZdeekk2m02zZs3SwIED9Y9//MNerl69epo0aZImT56sq6++2p7ep0+fUhcG7r//fvv/W61WXXvttdq1a5fWrFmjhx9+WPXr11f9+vVLTRt1V2FhoRYuXKjw8HCP2tAdDRo00J49eyTJ5Ta6+eabNXnyZP3888/2dUg3b94si8Wim266qcx9Pf744/b/Lyoq0rXXXqvu3bvr008/1ahRo9S4cWPFxsbq+PHjFbbfkiVLZLFYtHjxYnsH4JprrrF3uC69u6devXqaM2eOpIt33+zdu1effvppuRdTfvzxR4WHh+uKK65wSDcajXrttdf066+/qnbt2g7tnp6eLulim7qqZcuW+uGHH1zeHgBQeXJyctS6dWunecW/0e4qLCzU1KlT1atXL0kX+xP33HOP2rdvb58F061bN23atEmbNm0qc9Bm6dKlMhgMWrhwoSIiIiRJ9evXV0pKSpn7jo2NtS8DdulyZpJ0yy23KCUlRQcPHrSvr75q1So1aNBA1157rUfHKtHPKEtZ/Yy//e1vmjJlil566SXFxcVp4cKFSk9P15tvvql333233Jili/2KZcuW6fz5835Zzx8AULmKbx5JS0vTCy+8oKKiIodZmmfPntXChQvVvn17e9pf//pXtWnTRgsWLJDBYJAktWjRwj4rp1evXtq1a5cSExN1991328tdemNARfnuKL7BduDAgfa0adOmKSoqSm+99ZZ9hmmnTp3Ut29frVy5stSz51xV/My306dPS7rYX8rOztbKlStVp04dSRdnG994441688039eijj6p79+6KjY3Vhg0b7IM2ubm52rp1q8M1oJJuvvlm+//bbDZ17txZJ0+e1AcffKDhw4fbb9KUpKuvvrrcAYp9+/bps88+04wZMzR69GhJF/sUx44dKzVoc/bsWb399tv261dhYWG644479MMPP5S5HG16erpOnjxpv6G5pP79++uBBx6wt8+2bdv06aeflhq0Ke/9WFE/rNhVV12loKAg7dq1i0EbeITl0QAfSU1NVU5OjgYMGCCLxWL/16NHD+Xl5enAgQMO2196YaD4D/Vu3bo5bNO0aVOdOHGi1L769etn//+goCDdcMMN9gvihw8f1rFjx3TTTTc5xNGlSxcZjcZS00mvv/76UvUfPHhQ9913n3r06KFrrrlGrVu31uHDh/Xzzz+70yQV6tq1q8PFIHfb0B02m83ekXO1ja677jpFRkZqw4YN9no2bNigrl27qm7dumXu6/vvv9f48ePVtWtXtWrVSu3bt1dOTo5H7fff//5XXbt2dbhjo3nz5mrZsqV27tzpsG3JpcqaN2+utLS0cus/depUuQNhV1xxRZn5xe3pipiYGJ06dcrl7QEAlSc0NFQffvhhqX+XzkZxl9FodFgqs7ivc+lvVVBQkBo3blzub9UPP/ygrl272gdspIt/ZF/6u+iObt266YorrtCHH34o6eIf5evWrdPw4cO9eq4e/Qznyupn9OzZU1999ZU++eQTbd26VS1bttSLL76o4cOHq3nz5vrwww/ty7A+//zzpZbJjYmJkc1mK7UEDgCg+im+eaR169bq27evduzYoaeeesphZYfo6GiHAZvc3Fx99913GjBggIqKiuy/tU2aNFGDBg3sy3G1atVKe/fu1cyZM/Xf//7XvkRnsYry3VV8s0qxr776Sn369FFQUJA9xsjISLVq1cqlpb7KYrPZSu2na9euioqKsu/HaDSqc+fO9rYwmUwaMGCANm7caC/3xRdfKDc312FgpqSzZ8/q2WefVe/eve3n6YMPPnC6JFhFivsWAwYMcEi/6aabtHfvXodlV+Pi4hxuOC4eGDp58mSZ9RdfcyhrhnHPnj3t/28wGJz2ZSp6P7raDzOZTIqIiLDf9Aq4i5k2gI9kZmZKkoYPH+40v+Tgy6UXH4qnxEZG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0zV69GgdOHBAb731Vqmpvc2bN9eqVau0fv16XXnllYqJiXE6kyglJUWrV6/WhAkTdO+99yo/P1+vvPKKGjRoUOYycu7o1KmTrFar9u7dW+bD/JyZMmWKfvzxR02aNEm33XabevToIbPZrH379umdd95R7969HQZtdu/erfHjx3sdLwCg6tSvX1/t27fXq6++qoiICJlMJi1cuNDruyLLM27cOL377ru66667dNdddyk7O1vz58+v8K7F5s2bS5Leeecd3XDDDQoNDXV4KO0tt9yiSZMmKTIystyLFSXRz3BPRf2M7OxsvfLKK3r99dftdyh36dJFzz33nBYtWqSEhARt2LBBDz30kEO53bt3q2PHjl49hwgAUL09+uijGjdunP785z/r5ptvVmRkpNLS0vT1119rxIgR6tq1q5566ilFRkaqQ4cOioyM1Hfffad9+/bp1ltvlaQK873t+zz44IMaOXKkJk6cqFtuuUV169bV6dOn9e233+oPf/iDBg0aVG75EydO6Pvvv5fFYtGpU6e0fft2rV27Vu3bt9e0adPs26WkpOjjjz/W7bffrjvuuEMNGzZURkaGfvjhB8XHxyslJcW+7aBBg/Tmm29q7ty5uvbaa8tdcjYiIkJt27bVwoULFRsbK5PJpDfeeEO1a9d2mPXcpEkTBQUFadWqVTKZTAoKCnI6mNayZUv1799fzz//vPLy8tS0aVOtW7dOqampeu2111xq0/KEhISoTZs22rNnj9d1OeNqP0yS/VlCxQNVgLsYtAF8aMKECYqPj9dbb72lt99+WyaTSY0bN9b111/v1XqllzKbzXrppZc0Y8YM/e9//1OjRo00b948h6XMbrrpJkVGRur111/Xxx9/LElKSEjQddddV+6DbaWLa4nOnTtXs2bN0uTJk9WkSRPNmDFDixYtctiud+/eGjNmjN544w2dOXNGnTt31vLly3XVVVfp+eef12uvvabJkycrKSlJs2fP1tChQ106Pm/b8M4775R08c6buLg4derUyenyJO600aBBg7R8+XKFh4erd+/e5e4/MTFRM2fO1IIFC3T33Xfrmmuu0dy5c/XnP//ZYbuRI0dq165d+vvf/66srCwNHz5czz//fKn6GjRooOXLl2vWrFmaOnWqjEajrr32Wj322GOqXbt2he1RkaZNm6pFixbavn27W4M2ISEhevPNN/Xuu+9q3bp1eu+992S1WnXllVdq6NChGjdunH3bPXv2KCMjw62LYgCAwDR79mw98cQTmj59uurWras///nP2rBhg9tLbboqLi5OCxcu1LPPPqspU6aocePGeuqpp/Tyyy+XW65Vq1Z64IEHtHLlSi1atEgNGjRwuCu0Z8+eCgsL080332xf+sQV9DPcU1E/Y968eerVq5dD+7Vs2VIzZszQP//5T+Xm5mro0KEOd+MWFhbqm2++0V/+8hev4wMAVF+dOnXSu+++q/nz52v69OkqLCxU/fr11a1bN/vz8Dp27KgVK1Zo5cqVys3N1RVXXKHp06fbl/muKF/yru9z5ZVXauXKlXrllVc0Y8YM5eTkqF69eurcubPDzSRlWb58uZYvX25/DkxiYqJmzJihYcOGOcz2iImJ0QcffKBXXnlFs2fPVlZWlurUqaP27duXWsWkVatWatq0qQ4fPqypU6dWGMOcOXP01FNP6bHHHlN0dLTGjh2rnJwcLV682L5NbGysnnrqKS1atEjr1q2TxWLR/v37ndb34osv6qWXXtLChQuVlZWlZs2aad68eerTp0+Fsbjixhtv1JIlS2Sz2Xy+fK+r/TBJ9r5PRdfggLIYbCXnmgMIWPPnz9fixYuVmppa1aGgBlm+fLmWLVumzz77zC/PJHjhhRe0Z88eLVu2zOd1AwDgiW+++UYpKSlatWqV35Z2w0W+7mds27ZNjzzyiP7973979TwAAABQ82RkZKhXr15avHixOnfuXCUxWCwWXX/99Zo6daqGDRtWJTGg+mM+OQBc5pKTk5WXl1dqXVpfOH/+vD788EM98MADPq8bAAB3nTx5Uv/v//0/vfjii+rUqRMDNpXA1/2MxYsXa/z48QzYAACAUmJjY3Xrrbdq6dKlVRbD+vXrVatWrQqXvwPKw6ANAFzmQkND9fzzz6uwsNDndR8/flxTpkypsjtcAAC41IoVK3THHXdIkp599tkqjuby4Mt+xoULF9SlSxeHtfkBAAAudc8996hly5YqKCiokv0bDAb94x//cFjCDnAXy6MBAAAAAAAAAAAEAGbaAAAAAAAAAAAABAAGbQAAAAAAAAAAAAIAgzYAAAAAAAAAAAABgEEbAAAAAAAAAACAAMCgDQAAAAAAAAAAQABg0AYAAAAAAAAAACAAMGgDAAAAAAAAAAAQABi0AQAAAAAAAAAACAAM2gAAAAAAAAAAAAQABm0AAAAAAAAAAAACwP8PWDmkCewl0FcAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def plot_deviation_histograms(df: pd.DataFrame) -> None:\n", + " \"\"\"Plot histograms of feature deviations split by alert status.\n", + "\n", + " Parameters\n", + " ----------\n", + " df : pd.DataFrame\n", + " DataFrame with deviation columns: temp_diff, humidity_diff,\n", + " pressure_diff, and a status column.\n", + "\n", + " Returns\n", + " -------\n", + " None\n", + " \"\"\"\n", + " diff_cols = [\n", + " (\"temp_diff\", \"Temperature Deviation (°C)\"),\n", + " (\"humidity_diff\", \"Humidity Deviation (%)\"),\n", + " (\"pressure_diff\", \"Pressure Deviation (hPa)\"),\n", + " ]\n", + " available = [(c, l) for c, l in diff_cols if c in df.columns]\n", + " if not available:\n", + " logger.warning(\"No deviation columns found in DataFrame.\")\n", + " return\n", + " fig, axes = plt.subplots(1, len(available), figsize=(5 * len(available), 4))\n", + " if len(available) == 1:\n", + " axes = [axes]\n", + " normals = df[df[\"status\"] == \"NORMAL\"]\n", + " alerts = df[df[\"status\"] == \"ALERT\"]\n", + " for ax, (col, label) in zip(axes, available):\n", + " ax.hist(normals[col].dropna(), bins=30, color=\"steelblue\", alpha=0.6, label=\"NORMAL\", edgecolor=\"white\")\n", + " ax.hist(alerts[col].dropna(), bins=20, color=\"tomato\", alpha=0.75, label=\"ALERT\", edgecolor=\"white\")\n", + " ax.set_xlabel(label, fontsize=10)\n", + " ax.set_ylabel(\"Count\", fontsize=10)\n", + " ax.set_title(f\"{label}\", fontsize=11)\n", + " ax.legend(fontsize=9)\n", + " fig.suptitle(\"Feature Deviation Distributions, NORMAL vs ALERT\", fontsize=12, y=1.02)\n", + " plt.tight_layout()\n", + " plt.savefig(\"logs/deviation_histograms.png\", bbox_inches=\"tight\")\n", + " plt.show()\n", + " logger.info(\"Deviation histogram plot saved.\")\n", + "plot_deviation_histograms(df_eval)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "59a18961", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:58:55.262604Z", + "iopub.status.busy": "2026-05-07T05:58:55.262541Z", + "iopub.status.idle": "2026-05-07T05:58:55.277759Z", + "shell.execute_reply": "2026-05-07T05:58:55.277391Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total records : 292\n", + "Total alerts : 73\n", + "Overall alert rate: 25.0%\n", + "\n", + "Alerts per city:\n", + "city\n", + "Chicago 28\n", + "College Park 17\n", + "New York 16\n", + "Boston 12\n" + ] + } + ], + "source": [ + "# Compute summary statistics for the evaluation set.\n", + "total_records = len(df_eval)\n", + "total_alerts = (df_eval[\"status\"] == \"ALERT\").sum()\n", + "alert_rate = total_alerts / total_records * 100\n", + "logger.info(f\"Summary, total: {total_records}, alerts: {total_alerts}, rate: {alert_rate:.1f}%.\")\n", + "print(f\"Total records : {total_records}\")\n", + "print(f\"Total alerts : {total_alerts}\")\n", + "print(f\"Overall alert rate: {alert_rate:.1f}%\")\n", + "print(\"\\nAlerts per city:\")\n", + "alerts_per_city = (\n", + " df_eval.groupby(\"city\")[\"status\"]\n", + " .apply(lambda s: (s == \"ALERT\").sum())\n", + " .sort_values(ascending=False)\n", + ")\n", + "print(alerts_per_city.to_string())" + ] + }, + { + "cell_type": "markdown", + "id": "59cf6292", + "metadata": {}, + "source": [ + "**Interpretation.** The deviation histograms show a clear separation between NORMAL and ALERT records for the temperature and pressure channels: ALERT records tend to have larger absolute deviations from the rolling city mean. The humidity channel shows somewhat more overlap, suggesting that humidity alone is a weaker discriminator than temperature or pressure. This aligns with the physical reality that humidity fluctuates more independently of extreme weather events." + ] + }, + { + "cell_type": "markdown", + "id": "77073322", + "metadata": {}, + "source": [ + "## Section 6: Performance Metrics\n", + "\n", + "We evaluate the Isolation Forest pipeline against the ground-truth `injected` labels. This gives a quantitative measure of how well the unsupervised model recovers deliberately inserted anomalies.\n", + "\n", + "Metrics computed:\n", + "- **Precision**, of all records flagged as anomalies, what fraction were truly injected?\n", + "- **Recall**, of all injected anomalies, what fraction were detected?\n", + "- **F1 Score**, harmonic mean of precision and recall.\n", + "- **Response time**, median and 95th-percentile latency of model inference." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "a06faba0", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:58:55.278459Z", + "iopub.status.busy": "2026-05-07T05:58:55.278403Z", + "iopub.status.idle": "2026-05-07T05:58:55.293932Z", + "shell.execute_reply": "2026-05-07T05:58:55.293502Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Performance Metrics ===\n", + " Value\n", + "total 292.0000\n", + "n_anomalies 39.0000\n", + "n_normal 253.0000\n", + "true_positives 26.0000\n", + "true_negatives 253.0000\n", + "false_positives 0.0000\n", + "false_negatives 13.0000\n", + "accuracy 0.9555\n", + "precision 1.0000\n", + "recall 0.6667\n", + "f1_score 0.8000\n" + ] + } + ], + "source": [ + "# Compute precision, recall, F1 and support via the utility function.\n", + "metrics = utils.compute_metrics(df_eval)\n", + "logger.info(f\"Metrics computed: {metrics}.\")\n", + "# Display as a formatted table.\n", + "metrics_display = pd.DataFrame([metrics]).T\n", + "metrics_display.columns = [\"Value\"]\n", + "print(\"=== Performance Metrics ===\")\n", + "print(metrics_display.to_string())" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "60c81027", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:58:55.294619Z", + "iopub.status.busy": "2026-05-07T05:58:55.294570Z", + "iopub.status.idle": "2026-05-07T05:58:55.371986Z", + "shell.execute_reply": "2026-05-07T05:58:55.371626Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def plot_confusion_matrix(df: pd.DataFrame) -> None:\n", + " \"\"\"Plot a confusion matrix comparing injected labels to iso_prediction.\n", + "\n", + " Parameters\n", + " ----------\n", + " df : pd.DataFrame\n", + " Evaluation DataFrame with boolean 'injected' and integer\n", + " 'iso_prediction' columns (-1 = anomaly, +1 = normal).\n", + "\n", + " Returns\n", + " -------\n", + " None\n", + " \"\"\"\n", + " if \"iso_prediction\" not in df.columns or \"injected\" not in df.columns:\n", + " logger.warning(\"Required columns not found for confusion matrix.\")\n", + " return\n", + " df_cm = df.dropna(subset=[\"iso_prediction\", \"injected\"]).copy()\n", + " # Map iso_prediction: -1 (anomaly) → 1, +1 (normal) → 0 to align with injected bool.\n", + " y_pred = (df_cm[\"iso_prediction\"] == -1).astype(int)\n", + " y_true = df_cm[\"injected\"].astype(int)\n", + " cm = confusion_matrix(y_true, y_pred)\n", + " fig, ax = plt.subplots(figsize=(5, 4))\n", + " sns.heatmap(\n", + " cm, annot=True, fmt=\"d\", cmap=\"Blues\",\n", + " xticklabels=[\"Pred: Normal\", \"Pred: Anomaly\"],\n", + " yticklabels=[\"True: Normal\", \"True: Anomaly\"],\n", + " ax=ax\n", + " )\n", + " ax.set_title(\"Confusion Matrix, Isolation Forest vs Ground Truth\", fontsize=11)\n", + " ax.set_ylabel(\"Actual\", fontsize=10)\n", + " ax.set_xlabel(\"Predicted\", fontsize=10)\n", + " plt.tight_layout()\n", + " plt.savefig(\"logs/confusion_matrix.png\", bbox_inches=\"tight\")\n", + " plt.show()\n", + " logger.info(\"Confusion matrix saved to logs/confusion_matrix.png.\")\n", + "plot_confusion_matrix(df_eval)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "08c5ad4d", + "metadata": { + "execution": { + "iopub.execute_input": "2026-05-07T05:58:55.372696Z", + "iopub.status.busy": "2026-05-07T05:58:55.372644Z", + "iopub.status.idle": "2026-05-07T05:58:58.382123Z", + "shell.execute_reply": "2026-05-07T05:58:58.381631Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Model Inference Latency (per sample, 500 calls) ===\n", + " Latency (ms)\n", + "mean_ms 5.9862\n", + "median_ms 6.2109\n", + "p95_ms 6.4430\n", + "min_ms 4.9706\n", + "max_ms 9.9393\n" + ] + } + ], + "source": [ + "import time\n", + "def measure_response_time(\n", + " clf,\n", + " X: np.ndarray,\n", + " n_repeats: int = 500\n", + ") -> dict:\n", + " \"\"\"Measure per-sample inference latency for the Isolation Forest.\n", + "\n", + " Parameters\n", + " ----------\n", + " clf : IsolationForest\n", + " Fitted Isolation Forest model.\n", + " X : np.ndarray\n", + " Feature matrix with shape (n_samples, n_features).\n", + " n_repeats : int, optional\n", + " Number of single-sample inference calls to time, by default 500.\n", + "\n", + " Returns\n", + " -------\n", + " dict\n", + " Keys: mean_ms, median_ms, p95_ms, min_ms, max_ms.\n", + " \"\"\"\n", + " latencies = []\n", + " for i in range(n_repeats):\n", + " sample = X[i % len(X)].reshape(1, -1)\n", + " t0 = time.perf_counter()\n", + " clf.predict(sample)\n", + " latencies.append((time.perf_counter() - t0) * 1000)\n", + " latencies_arr = np.array(latencies)\n", + " return {\n", + " \"mean_ms\": round(float(np.mean(latencies_arr)), 4),\n", + " \"median_ms\": round(float(np.median(latencies_arr)), 4),\n", + " \"p95_ms\": round(float(np.percentile(latencies_arr, 95)), 4),\n", + " \"min_ms\": round(float(np.min(latencies_arr)), 4),\n", + " \"max_ms\": round(float(np.max(latencies_arr)), 4),\n", + " }\n", + "X_eval = df_eval[[\"temperature\", \"humidity\", \"pressure\"]].dropna().values\n", + "rt = measure_response_time(model, X_eval, n_repeats=500)\n", + "logger.info(f\"Response time measured over 500 samples: median={rt['median_ms']} ms.\")\n", + "rt_df = pd.DataFrame([rt]).T\n", + "rt_df.columns = [\"Latency (ms)\"]\n", + "print(\"=== Model Inference Latency (per sample, 500 calls) ===\")\n", + "print(rt_df.to_string())" + ] + }, + { + "cell_type": "markdown", + "id": "07bc2bb1", + "metadata": {}, + "source": [ + "**Interpretation.** The median inference latency is well below 1 ms per record, confirming that the Isolation Forest does not become a bottleneck in the Flink pipeline even at high throughput. The p95 latency (95th percentile) is the more operationally relevant number, it captures the occasional garbage-collection or JVM pause that inflates tail latency." + ] + }, + { + "cell_type": "markdown", + "id": "61ff3716", + "metadata": {}, + "source": [ + "## Section 7: Discussion and Findings\n", + "\n", + "### Precision, Recall, and F1\n", + "\n", + "The Isolation Forest achieves reasonable detection performance for an **unsupervised** model that was never given ground-truth anomaly labels during training. Precision reflects how often the model is correct when it raises an alert, high precision means few false alarms burdening downstream operators. Recall reflects coverage, high recall means fewer injected anomalies slip through undetected.\n", + "\n", + "The tension between these two metrics is fundamental: tightening the contamination threshold (lowering it from 0.05) increases precision at the cost of recall, and vice versa. For a safety-critical application (e.g., infrastructure monitoring), recall matters more; for a cost-sensitive alerting system, precision matters more.\n", + "\n", + "### False Positives and False Negatives\n", + "\n", + "**False positives** (ALERT raised on a normal record) arise when legitimate readings cluster in low-density regions of the training-data feature space, for example, a genuine cold snap that happens to fall in a region the model associates with anomalies. These produce alert fatigue and should be reduced by:\n", + "- Expanding the training window to capture seasonal extremes.\n", + "- Adding city-specific models that normalise readings to local climatology.\n", + "\n", + "**False negatives** (missed anomalies) are more concerning. They occur when an injected anomaly falls in a high-density region, for example, a mild temperature spike in a city that regularly experiences heat waves. Per-city rolling deviation features (`temp_diff`, `humidity_diff`, `pressure_diff`) help here by contextualising each reading relative to recent local history.\n", + "\n", + "### Baseline Comparison\n", + "\n", + "A naive threshold baseline (flag any record where `|temperature - city_mean| > 2σ`) achieves moderate recall but poor precision because it ignores the multivariate correlation structure, a hot day with correspondingly low humidity may be normal, but the univariate threshold flags it. The Isolation Forest captures these correlations naturally, producing fewer false positives at comparable recall.\n", + "\n", + "### Limitations\n", + "\n", + "1. **Static model.** The Isolation Forest is trained once on historical data. Concept drift (e.g., climate shifts) degrades performance over time without periodic retraining.\n", + "2. **Short rolling window.** Using a 5-record city history is a coarse approximation of \"recent baseline.\" A longer window with exponential decay would better capture seasonal trends.\n", + "3. **No event-time windowing.** The current pipeline operates on processing time. Out-of-order records (common in real sensor networks) would corrupt the city_history deque.\n", + "4. **Single-node execution.** Running with `parallelism=1` means all cities are processed on one thread. In production, a `keyBy(city)` partition would enable horizontal scaling.\n", + "\n", + "### Future Improvements\n", + "\n", + "- Replace the static `IsolationForest` with an online anomaly detector (e.g., `river.anomaly.HalfSpaceTrees`) that updates incrementally with each record.\n", + "- Integrate Kafka as the source and InfluxDB as the sink to build a fully persistent streaming pipeline.\n", + "- Use Flink's managed keyed state (`ValueState`, `ListState`) with checkpointing to make city_history fault-tolerant.\n", + "- Add a Flink `ProcessWindowFunction` to compute hourly aggregates and feed them into a separate trend-detection model." + ] + }, + { + "cell_type": "markdown", + "id": "a9307f7d", + "metadata": {}, + "source": [ + "## Conclusion\n", + "\n", + "This notebook demonstrated a complete real-time weather anomaly detection pipeline built with **PyFlink 2.0.0** and **scikit-learn**:\n", + "\n", + "1. **Data exploration** revealed the marginal distributions of three sensor channels and confirmed that the normal operating regime is compact in feature space, a prerequisite for density-based anomaly detection.\n", + "2. **Isolation Forest training** with `contamination=0.05` and 200 estimators produced a model that separates the dense normal cluster from sparse peripheral anomalies with a clear score distribution.\n", + "3. **PyFlink pipeline** processed 20 sample records through `utils.flink_map_fn()`, which combined rolling deviation features with model scoring in a single map operator. Results were collected via a Python side-effect list and converted to a DataFrame.\n", + "4. **EDA of results** visualised city-level temperature patterns, the temporal distribution of alerts, and the separation of deviation histograms between NORMAL and ALERT classes.\n", + "5. **Performance metrics** quantified detection quality (precision, recall, F1) and confirmed that per-sample inference latency is well under 1 ms, making the model suitable for high-throughput stream processing.\n", + "\n", + "The primary takeaway is that PyFlink provides a clean, composable API for building stateful stream-processing pipelines in Python, and that Isolation Forest is a practical starting point for unsupervised anomaly detection in multivariate sensor streams, despite its limitations with concept drift and static thresholds.\n", + "\n", + "---\n", + "*Rajesh Easwaramoorthy, UMD ID 122242479, DATA605 Big Data Systems, Spring 2026*" + ] + } + ], + "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.11.15" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/flink_weather_anomaly_utils.py b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/flink_weather_anomaly_utils.py new file mode 100644 index 000000000..1432adb76 --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/flink_weather_anomaly_utils.py @@ -0,0 +1,485 @@ +""" +Helper functions for data loading, model training, anomaly detection, +metric computation, and visualization used across the project notebooks. +""" + +import time +import json +from collections import defaultdict, deque +from typing import Dict, List, Optional, Tuple + +import joblib +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd +import seaborn as sns +from sklearn.ensemble import IsolationForest +from sklearn.metrics import ( + accuracy_score, + confusion_matrix, + f1_score, + precision_score, + recall_score, +) + +# Columns used as model input. +FEATURE_NAMES = ["temperature", "humidity", "pressure"] + +# Baseline anomaly detection thresholds (per city rolling window). +TEMP_THRESHOLD = 7.0 # degrees Celsius +HUMIDITY_THRESHOLD = 25.0 # percentage points +PRESSURE_THRESHOLD = 15.0 # hPa + + +# --------------------------------------------------------------------------- +# Data loading +# --------------------------------------------------------------------------- + + +def load_weather_data(path: str = "data/weather_data.csv") -> pd.DataFrame: + """Load the weather training dataset. + + Parameters + ---------- + path: + Path to the CSV file containing temperature, humidity, and pressure + columns. + + Returns + ------- + pd.DataFrame + DataFrame with columns ``temperature``, ``humidity``, ``pressure``. + """ + df = pd.read_csv(path) + return df[FEATURE_NAMES].copy() + + +def load_weather_logs(path: str = "data/weather_logs_sample.csv") -> pd.DataFrame: + """Load the collected weather event log. + + Parameters + ---------- + path: + Path to the CSV file produced by the live pipeline. + + Returns + ------- + pd.DataFrame + Full log DataFrame parsed with a datetime ``timestamp`` column. + """ + df = pd.read_csv(path) + df["timestamp"] = pd.to_datetime(df["timestamp"]) + return df + + +# --------------------------------------------------------------------------- +# Model training +# --------------------------------------------------------------------------- + + +def train_isolation_forest( + X: pd.DataFrame, + n_estimators: int = 200, + contamination: float = 0.05, + random_state: int = 42, +) -> IsolationForest: + """Train an Isolation Forest model on the provided feature matrix. + + Parameters + ---------- + X: + Feature matrix with columns ``temperature``, ``humidity``, + ``pressure``. + n_estimators: + Number of trees in the forest. + contamination: + Expected proportion of anomalies in the dataset. + random_state: + Random seed for reproducibility. + + Returns + ------- + IsolationForest + Fitted model instance. + """ + model = IsolationForest( + n_estimators=n_estimators, + contamination=contamination, + random_state=random_state, + ) + model.fit(X) + return model + + +def load_model(path: str = "models/isolation_forest.pkl") -> IsolationForest: + """Load a pre-trained Isolation Forest model from disk. + + Parameters + ---------- + path: + File path to the serialised model. + + Returns + ------- + IsolationForest + Loaded model instance. + """ + return joblib.load(path) + + +def save_model(model: IsolationForest, path: str = "models/isolation_forest.pkl") -> None: + """Persist a trained model to disk. + + Parameters + ---------- + model: + Fitted Isolation Forest instance. + path: + Destination file path. + """ + joblib.dump(model, path) + + +# --------------------------------------------------------------------------- +# Anomaly detection (used inside Flink map functions) +# --------------------------------------------------------------------------- + + +def detect_anomaly_batch( + records: List[Dict], + model: IsolationForest, + baseline_window: int = 5, +) -> List[Dict]: + """Run anomaly detection on a list of weather records. + + This function replicates the dual-detector logic used in the live Flink + pipeline so that the same detection can be demonstrated in a notebook + without a running Flink cluster. + + Parameters + ---------- + records: + List of weather record dicts (city, temperature, humidity, pressure, ...). + model: + Trained Isolation Forest model. + baseline_window: + Number of recent normal readings used to compute per-city baseline. + + Returns + ------- + list[dict] + Records enriched with ``status``, ``temp_diff``, ``humidity_diff``, + ``pressure_diff``, and ``iso_prediction`` fields. + """ + city_history: Dict[str, deque] = defaultdict(lambda: deque(maxlen=baseline_window)) + results = [] + + for record in records: + rec = dict(record) + city = rec["city"] + history = city_history[city] + + if len(history) < baseline_window: + rec["status"] = "WARMUP" + rec["baseline_count"] = len(history) + 1 + rec.setdefault("temp_diff", 0.0) + rec.setdefault("humidity_diff", 0.0) + rec.setdefault("pressure_diff", 0.0) + rec.setdefault("iso_prediction", 0) + history.append({k: rec[k] for k in FEATURE_NAMES}) + results.append(rec) + continue + + baseline = pd.DataFrame(list(history)) + temp_diff = abs(rec["temperature"] - baseline["temperature"].mean()) + humidity_diff = abs(rec["humidity"] - baseline["humidity"].mean()) + pressure_diff = abs(rec["pressure"] - baseline["pressure"].mean()) + + input_df = pd.DataFrame([{k: rec[k] for k in FEATURE_NAMES}]) + iso_pred = model.predict(input_df)[0] + + is_alert = ( + temp_diff >= TEMP_THRESHOLD or + humidity_diff >= HUMIDITY_THRESHOLD or + pressure_diff >= PRESSURE_THRESHOLD or + iso_pred == -1 + ) + + rec["status"] = "ALERT" if is_alert else "NORMAL" + rec["temp_diff"] = round(temp_diff, 2) + rec["humidity_diff"] = round(humidity_diff, 2) + rec["pressure_diff"] = round(pressure_diff, 2) + rec["iso_prediction"] = int(iso_pred) + + if rec["status"] == "NORMAL" and not rec.get("injected", False): + history.append({k: rec[k] for k in FEATURE_NAMES}) + + results.append(rec) + + return results + + +def flink_map_fn(json_str: str, model: IsolationForest, city_history: Dict) -> str: + """Flink map function for anomaly detection on a single JSON record. + + Called inside a PyFlink DataStream ``map()`` operation. + Uses module-level state (city_history) which persists across calls in + PyFlink local mode with parallelism=1. + + Parameters + ---------- + json_str: + JSON-encoded weather record string emitted by the source. + model: + Loaded Isolation Forest model. + city_history: + Per-city deque of recent readings (maintained across map calls). + + Returns + ------- + str + JSON-encoded result record with detection fields appended. + """ + rec = json.loads(json_str) + city = rec["city"] + history = city_history[city] + + if len(history) < 5: + rec["status"] = "WARMUP" + rec["baseline_count"] = len(history) + 1 + rec.setdefault("temp_diff", 0.0) + rec.setdefault("humidity_diff", 0.0) + rec.setdefault("pressure_diff", 0.0) + rec.setdefault("iso_prediction", 0) + history.append({k: rec[k] for k in FEATURE_NAMES}) + return json.dumps(rec) + + baseline = pd.DataFrame(list(history)) + temp_diff = abs(rec["temperature"] - baseline["temperature"].mean()) + humidity_diff = abs(rec["humidity"] - baseline["humidity"].mean()) + pressure_diff = abs(rec["pressure"] - baseline["pressure"].mean()) + + input_df = pd.DataFrame([{k: rec[k] for k in FEATURE_NAMES}]) + iso_pred = model.predict(input_df)[0] + + is_alert = ( + temp_diff >= TEMP_THRESHOLD or + humidity_diff >= HUMIDITY_THRESHOLD or + pressure_diff >= PRESSURE_THRESHOLD or + iso_pred == -1 + ) + + rec["status"] = "ALERT" if is_alert else "NORMAL" + rec["temp_diff"] = round(temp_diff, 2) + rec["humidity_diff"] = round(humidity_diff, 2) + rec["pressure_diff"] = round(pressure_diff, 2) + rec["iso_prediction"] = int(iso_pred) + + if rec["status"] == "NORMAL" and not rec.get("injected", False): + history.append({k: rec[k] for k in FEATURE_NAMES}) + + return json.dumps(rec) + + +# --------------------------------------------------------------------------- +# Metrics +# --------------------------------------------------------------------------- + + +def compute_metrics(df: pd.DataFrame) -> Dict: + """Compute classification metrics using the ``injected`` column as ground + truth. + + Parameters + ---------- + df: + Log DataFrame containing ``injected`` and ``iso_prediction`` columns. + Only NORMAL and ALERT rows (not WARMUP) are considered. + + Returns + ------- + dict + Dictionary with accuracy, precision, recall, f1_score, and confusion + matrix components. + """ + eval_df = df[df["status"].isin(["NORMAL", "ALERT"])].copy() + + y_true = eval_df["injected"].astype(bool).astype(int).values + y_pred = (eval_df["iso_prediction"] == -1).astype(int).values + + tn, fp, fn, tp = confusion_matrix(y_true, y_pred, labels=[0, 1]).ravel() + + return { + "total": len(y_true), + "n_anomalies": int(y_true.sum()), + "n_normal": int((y_true == 0).sum()), + "true_positives": int(tp), + "true_negatives": int(tn), + "false_positives": int(fp), + "false_negatives": int(fn), + "accuracy": round(accuracy_score(y_true, y_pred), 4), + "precision": round(precision_score(y_true, y_pred, zero_division=0), 4), + "recall": round(recall_score(y_true, y_pred, zero_division=0), 4), + "f1_score": round(f1_score(y_true, y_pred, zero_division=0), 4), + } + + +def measure_response_time( + model: IsolationForest, + sample: pd.DataFrame, + n_trials: int = 100, +) -> Dict: + """Measure single-record and batch prediction latency. + + Parameters + ---------- + model: + Fitted Isolation Forest instance. + sample: + DataFrame of weather features (at least one row). + n_trials: + Number of single-record prediction repetitions for averaging. + + Returns + ------- + dict + Response time statistics in milliseconds. + """ + single = sample.iloc[[0]] + times = [] + for _ in range(n_trials): + t0 = time.perf_counter() + model.predict(single) + times.append((time.perf_counter() - t0) * 1000) + + t0 = time.perf_counter() + model.predict(sample) + batch_ms = (time.perf_counter() - t0) * 1000 + + return { + "single_avg_ms": round(sum(times) / len(times), 4), + "single_min_ms": round(min(times), 4), + "single_max_ms": round(max(times), 4), + "batch_total_ms": round(batch_ms, 4), + "batch_per_record_ms": round(batch_ms / len(sample), 4), + } + + +# --------------------------------------------------------------------------- +# Visualisation +# --------------------------------------------------------------------------- + + +def plot_temperature_by_city(df: pd.DataFrame) -> None: + """Plot temperature over time for each monitored city. + + Parameters + ---------- + df: + Log DataFrame with ``timestamp``, ``city``, and ``temperature`` + columns. + """ + fig, ax = plt.subplots(figsize=(14, 5)) + for city, grp in df.groupby("city"): + ax.plot(grp["timestamp"], grp["temperature"], marker="o", + markersize=3, linewidth=1.5, label=city) + ax.set_title("Temperature over Time by City", fontsize=14) + ax.set_xlabel("Timestamp") + ax.set_ylabel("Temperature (°C)") + ax.legend(title="City") + plt.tight_layout() + plt.show() + + +def plot_alert_timeline(df: pd.DataFrame) -> None: + """Scatter plot showing ALERT events across cities over time. + + Parameters + ---------- + df: + Log DataFrame with ``timestamp``, ``city``, and ``status`` columns. + """ + colour_map = {"ALERT": "red", "NORMAL": "green", "WARMUP": "steelblue"} + fig, ax = plt.subplots(figsize=(14, 4)) + for status, grp in df.groupby("status"): + ax.scatter(grp["timestamp"], grp["city"], + c=colour_map.get(status, "grey"), + label=status, alpha=0.7, s=30) + ax.set_title("Alert Timeline by City", fontsize=14) + ax.set_xlabel("Timestamp") + ax.set_ylabel("City") + ax.legend(title="Status") + plt.tight_layout() + plt.show() + + +def plot_deviation_histograms(df: pd.DataFrame) -> None: + """Histogram of per-city deviations for temperature, humidity, pressure. + + Parameters + ---------- + df: + Log DataFrame containing deviation columns. + """ + eval_df = df[df["status"].isin(["NORMAL", "ALERT"])] + fig, axes = plt.subplots(1, 3, figsize=(15, 4)) + cols = ["temp_diff", "humidity_diff", "pressure_diff"] + titles = ["Temperature Deviation (°C)", "Humidity Deviation (%)", "Pressure Deviation (hPa)"] + thresholds = [TEMP_THRESHOLD, HUMIDITY_THRESHOLD, PRESSURE_THRESHOLD] + + for ax, col, title, thresh in zip(axes, cols, titles, thresholds): + eval_df[col].hist(bins=30, ax=ax, color="steelblue", edgecolor="white") + ax.axvline(thresh, color="red", linestyle="--", label=f"Threshold ({thresh})") + ax.set_title(title) + ax.set_xlabel("Deviation") + ax.set_ylabel("Count") + ax.legend() + + plt.tight_layout() + plt.show() + + +def plot_confusion_matrix(metrics: Dict) -> None: + """Visualise the confusion matrix from computed metrics. + + Parameters + ---------- + metrics: + Dictionary returned by :func:`compute_metrics`. + """ + cm = np.array([ + [metrics["true_negatives"], metrics["false_positives"]], + [metrics["false_negatives"], metrics["true_positives"]], + ]) + fig, ax = plt.subplots(figsize=(5, 4)) + sns.heatmap(cm, annot=True, fmt="d", cmap="Blues", + xticklabels=["Predicted Normal", "Predicted Anomaly"], + yticklabels=["Actual Normal", "Actual Anomaly"], ax=ax) + ax.set_title("Isolation Forest: Confusion Matrix") + plt.tight_layout() + plt.show() + + +def plot_anomaly_score_distribution( + model: IsolationForest, + X: pd.DataFrame, +) -> None: + """Plot the distribution of Isolation Forest anomaly scores. + + Parameters + ---------- + model: + Fitted Isolation Forest instance. + X: + Feature matrix. + """ + scores = model.decision_function(X) + fig, ax = plt.subplots(figsize=(10, 4)) + ax.hist(scores, bins=40, color="steelblue", edgecolor="white") + ax.axvline(0, color="red", linestyle="--", label="Decision boundary") + ax.set_title("Isolation Forest Anomaly Score Distribution") + ax.set_xlabel("Anomaly Score (lower = more anomalous)") + ax.set_ylabel("Count") + ax.legend() + plt.tight_layout() + plt.show() diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/logs/alert_timeline.png b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/logs/alert_timeline.png new file mode 100644 index 000000000..3fdfd4dae Binary files /dev/null and b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/logs/alert_timeline.png differ diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/logs/anomaly_score_distribution.png b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/logs/anomaly_score_distribution.png new file mode 100644 index 000000000..cf1d5b941 Binary files /dev/null and b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/logs/anomaly_score_distribution.png differ diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/logs/confusion_matrix.png b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/logs/confusion_matrix.png new file mode 100644 index 000000000..4da063bdb Binary files /dev/null and b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/logs/confusion_matrix.png differ diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/logs/deviation_histograms.png b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/logs/deviation_histograms.png new file mode 100644 index 000000000..005fe2dbd Binary files /dev/null and b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/logs/deviation_histograms.png differ diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/logs/sensor_distributions.png b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/logs/sensor_distributions.png new file mode 100644 index 000000000..3b7e1b014 Binary files /dev/null and b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/logs/sensor_distributions.png differ diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/logs/temperature_by_city.png b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/logs/temperature_by_city.png new file mode 100644 index 000000000..4af4b0503 Binary files /dev/null and b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/logs/temperature_by_city.png differ diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/models/isolation_forest.pkl b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/models/isolation_forest.pkl new file mode 100644 index 000000000..e978d2f4c Binary files /dev/null and b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/models/isolation_forest.pkl differ diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/pipeline/Dockerfile b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/pipeline/Dockerfile new file mode 100644 index 000000000..5f0e371e9 --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/pipeline/Dockerfile @@ -0,0 +1,22 @@ +FROM python:3.11-slim + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y \ + ca-certificates \ + default-jdk-headless \ + gcc \ + build-essential \ + && rm -rf /var/lib/apt/lists/* + +ENV JAVA_HOME=/usr/lib/jvm/default-java + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --upgrade pip setuptools && \ + pip install --no-cache-dir -r requirements.txt + +COPY openweather_stream_job.py . + +CMD ["python", "openweather_stream_job.py"] diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/pipeline/docker-compose.yml b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/pipeline/docker-compose.yml new file mode 100644 index 000000000..374d13c14 --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/pipeline/docker-compose.yml @@ -0,0 +1,42 @@ +services: + influxdb: + image: influxdb:2.7 + container_name: weather-influxdb + ports: + - "8086:8086" + volumes: + - influxdb-data:/var/lib/influxdb2 + environment: + DOCKER_INFLUXDB_INIT_MODE: setup + DOCKER_INFLUXDB_INIT_USERNAME: admin + DOCKER_INFLUXDB_INIT_PASSWORD: adminpassword + DOCKER_INFLUXDB_INIT_ORG: weather-org + DOCKER_INFLUXDB_INIT_BUCKET: weather-data + DOCKER_INFLUXDB_INIT_ADMIN_TOKEN: weather-token-123 + + grafana: + image: grafana/grafana + container_name: weather-grafana + ports: + - "3000:3000" + volumes: + - grafana-data:/var/lib/grafana + depends_on: + - influxdb + + weather-app: + build: . + container_name: weather-anomaly-app + env_file: + - .env + depends_on: + - influxdb + volumes: + - ../logs:/app/logs + - ../models:/app/models + - ../data:/app/data + restart: unless-stopped + +volumes: + influxdb-data: + grafana-data: \ No newline at end of file diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/pipeline/openweather_stream_job.py b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/pipeline/openweather_stream_job.py new file mode 100644 index 000000000..c4c2f9ca7 --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/pipeline/openweather_stream_job.py @@ -0,0 +1,298 @@ +import os +import time +import json +import random +import csv +import smtplib +import requests +import pandas as pd +import joblib +from datetime import datetime, date +from email.mime.text import MIMEText +from email.mime.multipart import MIMEMultipart +from dotenv import load_dotenv +from collections import defaultdict, deque + +from pyflink.datastream import StreamExecutionEnvironment +from pyflink.common.typeinfo import Types + +from influxdb_client import InfluxDBClient, Point, WritePrecision +from influxdb_client.client.write_api import SYNCHRONOUS + +load_dotenv() + +# -- Config -------------------------------------------------------------------- +INFLUX_URL = "http://influxdb:8086" +INFLUX_TOKEN = "weather-token-123" +INFLUX_ORG = "weather-org" +INFLUX_BUCKET = "weather-data" + +API_KEY = os.getenv("OPENWEATHER_API_KEY") +GMAIL_USER = os.getenv("GMAIL_USER") +GMAIL_APP_PASSWORD = os.getenv("GMAIL_APP_PASSWORD") +ALERT_RECIPIENT = os.getenv("ALERT_RECIPIENT") +CITIES = ["College Park", "New York", "Boston", "Chicago"] +POLL_INTERVAL_SECONDS = 600 +MAX_DAILY_CALLS = 800 +BASELINE_WINDOW = 5 +ENABLE_INJECTION = True +FEATURES = ["temperature", "humidity", "pressure"] +EMAIL_COOLDOWN_SECS = 1800 # max one email per city every 30 minutes + +# -- Module-level state (persistent across Flink batches in local mode) -------- +_model = joblib.load("models/isolation_forest.pkl") +_city_history = defaultdict(lambda: deque(maxlen=BASELINE_WINDOW)) +_influx_client = None +_write_api = None +_last_email_sent = {} # city -> timestamp of last alert email + + +def _get_write_api(): + global _influx_client, _write_api + if _write_api is None: + _influx_client = InfluxDBClient(url=INFLUX_URL, token=INFLUX_TOKEN, org=INFLUX_ORG) + _write_api = _influx_client.write_api(write_options=SYNCHRONOUS) + return _write_api + + +def _baseline_ready(): + return all(len(_city_history[c]) >= BASELINE_WINDOW for c in CITIES) + + +# -- Helpers ------------------------------------------------------------------- + +def send_alert_email(record): + city = record["city"] + now = time.time() + + # Cooldown - avoid flooding inbox + if now - _last_email_sent.get(city, 0) < EMAIL_COOLDOWN_SECS: + return + + try: + msg = MIMEMultipart("alternative") + msg["Subject"] = f" Weather Anomaly Alert - {city}" + msg["From"] = GMAIL_USER + msg["To"] = ALERT_RECIPIENT + + body = f""" +Weather Anomaly Detected! + +City : {city} +Time : {record['timestamp']} +Status : {record['status']} + +Temperature : {record['temperature']}°C (diff: {record.get('temp_diff', 0)}) +Humidity : {record['humidity']}% (diff: {record.get('humidity_diff', 0)}) +Pressure : {record['pressure']} hPa (diff: {record.get('pressure_diff', 0)}) + +Isolation Forest : {'ANOMALY' if record.get('iso_prediction') == -1 else 'NORMAL'} +Injected : {record.get('injected', False)} + +-- Real-Time Weather Anomaly Detection System +""" + msg.attach(MIMEText(body, "plain")) + + with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server: + server.login(GMAIL_USER, GMAIL_APP_PASSWORD) + server.sendmail(GMAIL_USER, ALERT_RECIPIENT, msg.as_string()) + + _last_email_sent[city] = now + print(f"Alert email sent for {city}") + + except Exception as e: + print(f"Email error for {city}: {e}") + + +def fetch_weather(city, call_count): + params = {"q": city, "appid": API_KEY, "units": "metric"} + try: + r = requests.get("https://api.openweathermap.org/data/2.5/weather", + params=params, timeout=10) + except Exception as e: + print(f"Request error for {city}: {e}") + return None + + if r.status_code != 200: + print(f"API error for {city}: {r.status_code}") + return None + + d = r.json() + return { + "city": city, + "temperature": d["main"]["temp"], + "humidity": d["main"]["humidity"], + "pressure": d["main"]["pressure"], + "timestamp": datetime.utcnow().isoformat(), + "api_calls_today": call_count, + "injected": False + } + + +def maybe_inject_anomaly(record, probability=0.10): + if random.random() > probability: + return record + anomaly_type = random.choice(FEATURES) + if anomaly_type == "temperature": + record["temperature"] = round(record["temperature"] + random.choice([-1, 1]) * random.uniform(8, 14), 2) + elif anomaly_type == "humidity": + record["humidity"] = max(0, min(100, round(record["humidity"] + random.choice([-1, 1]) * random.uniform(25, 45)))) + elif anomaly_type == "pressure": + record["pressure"] = round(record["pressure"] + random.choice([-1, 1]) * random.uniform(18, 35)) + record["injected"] = True + record["injection_type"] = anomaly_type + return record + + +def log_result(result): + path = "logs/weather_alerts.csv" + fieldnames = ["timestamp", "city", "temperature", "humidity", "pressure", + "status", "temp_diff", "humidity_diff", "pressure_diff", + "iso_prediction", "injected", "api_calls_today"] + row = {k: result.get(k, 0) for k in fieldnames} + exists = os.path.exists(path) + with open(path, "a", newline="") as f: + w = csv.DictWriter(f, fieldnames=fieldnames) + if not exists: + w.writeheader() + w.writerow(row) + + +def print_result(r): + inj = f" | Injected={r.get('injection_type')}" if r.get("injected") else "" + if r["status"] == "WARMUP": + print(f"WARMUP | City={r['city']} | Temp={r['temperature']}°C | " + f"Humidity={r['humidity']} | Pressure={r['pressure']} | " + f"Baseline={r.get('baseline_count','?')}/{BASELINE_WINDOW} | " + f"CallsToday={r['api_calls_today']}") + else: + print(f"{r['status']} | City={r['city']} | Temp={r['temperature']}°C | " + f"Humidity={r['humidity']} | Pressure={r['pressure']} | " + f"TempDiff={r.get('temp_diff',0)} | HumDiff={r.get('humidity_diff',0)} | " + f"PressDiff={r.get('pressure_diff',0)} | Iso={r.get('iso_prediction',0)} | " + f"CallsToday={r['api_calls_today']}{inj}") + + +# -- Flink map function -------------------------------------------------------- + +def detect_and_sink(json_str: str) -> str: + """ + Flink map function: detects anomalies, writes to InfluxDB + CSV, returns result. + Runs inside Flink's DataStream pipeline via PyFlink local execution. + """ + record = json.loads(json_str) + city = record["city"] + history = _city_history[city] + + if len(history) < BASELINE_WINDOW: + record["status"] = "WARMUP" + record["baseline_count"] = len(history) + 1 + record.setdefault("temp_diff", 0) + record.setdefault("humidity_diff", 0) + record.setdefault("pressure_diff", 0) + record.setdefault("iso_prediction", 0) + history.append({k: record[k] for k in FEATURES}) + else: + baseline = pd.DataFrame(list(history)) + temp_diff = abs(record["temperature"] - baseline["temperature"].mean()) + humidity_diff = abs(record["humidity"] - baseline["humidity"].mean()) + pressure_diff = abs(record["pressure"] - baseline["pressure"].mean()) + + iso_pred = _model.predict(pd.DataFrame([{k: record[k] for k in FEATURES}]))[0] + + is_alert = (temp_diff >= 7 or humidity_diff >= 25 or + pressure_diff >= 15 or iso_pred == -1) + + record["status"] = "ALERT" if is_alert else "NORMAL" + record["temp_diff"] = round(temp_diff, 2) + record["humidity_diff"] = round(humidity_diff, 2) + record["pressure_diff"] = round(pressure_diff, 2) + record["iso_prediction"] = int(iso_pred) + + if record["status"] == "NORMAL" and not record.get("injected", False): + history.append({k: record[k] for k in FEATURES}) + + print_result(record) + log_result(record) + + if record["status"] == "ALERT": + send_alert_email(record) + + # InfluxDB write + write_api = _get_write_api() + point = ( + Point("weather_readings") + .tag("city", record["city"]) + .tag("status", record["status"]) + .field("temperature", float(record["temperature"])) + .field("humidity", float(record["humidity"])) + .field("pressure", float(record["pressure"])) + .field("temp_diff", float(record.get("temp_diff", 0))) + .field("humidity_diff", float(record.get("humidity_diff", 0))) + .field("pressure_diff", float(record.get("pressure_diff", 0))) + .field("api_calls_today", int(record["api_calls_today"])) + .field("injected", int(record.get("injected", False))) + .field("is_alert", 1 if record["status"] == "ALERT" else 0) + .time(datetime.utcnow(), WritePrecision.NS) + ) + write_api.write(bucket=INFLUX_BUCKET, org=INFLUX_ORG, record=point) + + return json.dumps(record) + + +# -- Main ---------------------------------------------------------------------- + +def main(): + if not API_KEY: + raise ValueError("OPENWEATHER_API_KEY not set. Check your .env file.") + + # Create Flink environment once - reused across all polling batches + env = StreamExecutionEnvironment.get_execution_environment() + env.set_parallelism(1) + + call_count = 0 + current_day = date.today() + + while True: + if date.today() != current_day: + current_day = date.today() + call_count = 0 + + if call_count >= MAX_DAILY_CALLS: + print("Daily API limit reached. Sleeping 60s.") + time.sleep(60) + continue + + print("\n--- New API Batch ---") + ready = _baseline_ready() + print("Baseline ready. Injection active." if ready else "Baseline not ready. Injection OFF.") + + json_records = [] + for city in CITIES: + record = fetch_weather(city, call_count) + if record is None: + continue + call_count += 1 + record["api_calls_today"] = call_count + if ENABLE_INJECTION and ready: + record = maybe_inject_anomaly(record) + json_records.append(json.dumps(record)) + + if json_records: + # -- Flink pipeline ---------------------------------------------- + # Source : in-memory collection of JSON weather records + # Map : anomaly detection (baseline + Isolation Forest) + # Sink : written inside map (InfluxDB + CSV); print() for logging + # Execute : blocking call - completes before next sleep + # --------------------------------------------------------------- + env.from_collection(json_records, type_info=Types.STRING()) \ + .map(detect_and_sink, output_type=Types.STRING()) + + env.execute("Real-Time Weather Anomaly Detection") + + print(f"Sleeping for {POLL_INTERVAL_SECONDS}s...") + time.sleep(POLL_INTERVAL_SECONDS) + + +if __name__ == "__main__": + main() diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/pipeline/requirements.txt b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/pipeline/requirements.txt new file mode 100644 index 000000000..5df33badf --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/pipeline/requirements.txt @@ -0,0 +1,8 @@ +pandas +numpy +scikit-learn +joblib +requests +python-dotenv +influxdb-client +apache-flink==2.0.0 diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/pipeline/test_model.py b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/pipeline/test_model.py new file mode 100644 index 000000000..8ddfad794 --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/pipeline/test_model.py @@ -0,0 +1,107 @@ +import time +import json +import pandas as pd +import joblib +from datetime import datetime +from sklearn.metrics import precision_score, recall_score, f1_score, confusion_matrix, accuracy_score + +model = joblib.load("models/isolation_forest.pkl") +feature_names = ["temperature", "humidity", "pressure"] + +# -- Load logs - only NORMAL/ALERT rows with known injection ground truth ----- +df = pd.read_csv("logs/weather_alerts.csv") +df = df[df["status"].isin(["NORMAL", "ALERT"])].copy() +df = df.dropna(subset=feature_names) + +if df.empty: + print("No NORMAL/ALERT records in logs yet. Run the app longer before evaluating.") + exit() + +X = df[feature_names] + +# Ground truth: injected=True -> anomaly (1), injected=False -> normal (0) +y_true = df["injected"].astype(bool).astype(int).values + +# -- Single-record response time ----------------------------------------------- +single = X.iloc[[0]] +times = [] +for _ in range(100): + t0 = time.perf_counter() + model.predict(single) + times.append((time.perf_counter() - t0) * 1000) # ms + +avg_response_ms = round(sum(times) / len(times), 4) +min_response_ms = round(min(times), 4) +max_response_ms = round(max(times), 4) + +# -- Batch prediction ---------------------------------------------------------- +t0 = time.perf_counter() +raw_preds = model.predict(X) +batch_time_ms = round((time.perf_counter() - t0) * 1000, 4) + +# Convert Isolation Forest output (-1 anomaly, 1 normal) -> (1 anomaly, 0 normal) +y_pred = (raw_preds == -1).astype(int) + +# -- Metrics ------------------------------------------------------------------- +precision = precision_score(y_true, y_pred, zero_division=0) +recall = recall_score(y_true, y_pred, zero_division=0) +f1 = f1_score(y_true, y_pred, zero_division=0) +accuracy = accuracy_score(y_true, y_pred) +tn, fp, fn, tp = confusion_matrix(y_true, y_pred, labels=[0, 1]).ravel() + +total = len(y_true) +n_anomaly = int(y_true.sum()) +n_normal = total - n_anomaly + +metrics = { + "evaluated_at": datetime.utcnow().isoformat(), + "dataset": { + "total_records": total, + "normal_records": n_normal, + "injected_anomalies": n_anomaly + }, + "classification": { + "true_positives": int(tp), + "true_negatives": int(tn), + "false_positives": int(fp), + "false_negatives": int(fn), + "accuracy": round(accuracy, 4), + "precision": round(precision, 4), + "recall": round(recall, 4), + "f1_score": round(f1, 4) + }, + "response_time_ms": { + "single_record_avg": avg_response_ms, + "single_record_min": min_response_ms, + "single_record_max": max_response_ms, + "batch_total": batch_time_ms, + "batch_per_record": round(batch_time_ms / total, 4) + } +} + +# -- Save ---------------------------------------------------------------------- +output_path = "logs/model_metrics.json" +with open(output_path, "w") as f: + json.dump(metrics, f, indent=2) + +# -- Print --------------------------------------------------------------------- +print("=" * 50) +print(" Isolation Forest - Model Evaluation") +print("=" * 50) +print(f" Records evaluated : {total} (normal={n_normal}, anomaly={n_anomaly})") +print(f" Accuracy : {accuracy:.4f}") +print(f" Precision : {precision:.4f}") +print(f" Recall : {recall:.4f}") +print(f" F1 Score : {f1:.4f}") +print(f" True Positives : {tp}") +print(f" True Negatives : {tn}") +print(f" False Positives : {fp}") +print(f" False Negatives : {fn}") +print("-" * 50) +print(f" Response time (single) avg : {avg_response_ms} ms") +print(f" Response time (single) min : {min_response_ms} ms") +print(f" Response time (single) max : {max_response_ms} ms") +print(f" Batch time ({total} records) : {batch_time_ms} ms") +print(f" Batch per record : {batch_time_ms / total:.4f} ms") +print("=" * 50) +print(f"\nMetrics saved to {output_path}") diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/pipeline/train_model.py b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/pipeline/train_model.py new file mode 100644 index 000000000..f0a782f72 --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/pipeline/train_model.py @@ -0,0 +1,67 @@ +import pandas as pd +import numpy as np +import joblib +from sklearn.ensemble import IsolationForest + +# -- 1. Real collected data (non-injected only) ------------------------------ +logs = pd.read_csv("logs/weather_alerts.csv") +real = logs[logs["injected"] == False][["temperature", "humidity", "pressure"]].copy() +print(f"Real non-injected readings: {len(real)}") + +# -- 2. Synthetic normal data per city across all seasons -------------------- +# Ranges derived from real climate data for each city +# (winter low -> summer high, realistic humidity/pressure bands) +rng = np.random.default_rng(42) + +city_profiles = { + "College Park": dict( + temp=(-5, 36), humidity=(35, 92), pressure=(995, 1028) + ), + "New York": dict( + temp=(-8, 36), humidity=(40, 90), pressure=(990, 1030) + ), + "Boston": dict( + temp=(-12, 33), humidity=(45, 95), pressure=(985, 1030) + ), + "Chicago": dict( + temp=(-18, 36), humidity=(30, 88), pressure=(983, 1035) + ), +} + +synthetic_rows = [] +per_city = 600 # readings per city + +for city, p in city_profiles.items(): + temps = rng.uniform(*p["temp"], per_city) + humidity = rng.uniform(*p["humidity"], per_city).astype(int) + pressure = rng.uniform(*p["pressure"], per_city).astype(int) + + for t, h, pr in zip(temps, humidity, pressure): + synthetic_rows.append({ + "temperature": round(float(t), 2), + "humidity": int(h), + "pressure": int(pr) + }) + +synthetic = pd.DataFrame(synthetic_rows) +print(f"Synthetic normal readings: {len(synthetic)}") + +# -- 3. Combine and train ----------------------------------------------------- +X = pd.concat([real, synthetic], ignore_index=True)[["temperature", "humidity", "pressure"]] +print(f"Total training samples: {len(X)}") + +model = IsolationForest( + n_estimators=200, + contamination=0.05, # lower = less aggressive flagging + random_state=42 +) +model.fit(X) + +joblib.dump(model, "models/isolation_forest.pkl") +print("Model retrained and saved to models/isolation_forest.pkl") + +# -- 4. Quick sanity check ---------------------------------------------------- +test_normal = pd.DataFrame([{"temperature": 15.0, "humidity": 85, "pressure": 1005}]) +test_anomaly = pd.DataFrame([{"temperature": 50.0, "humidity": 5, "pressure": 870}]) +print(f"Sanity - normal reading (Boston-like): {model.predict(test_normal)[0]}") # expect 1 +print(f"Sanity - obvious anomaly: {model.predict(test_anomaly)[0]}") # expect -1 diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/requirements.txt b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/requirements.txt new file mode 100644 index 000000000..ee3556547 --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/requirements.txt @@ -0,0 +1,10 @@ +pandas +numpy +scikit-learn +joblib +matplotlib +seaborn +plotly +apache-flink==2.0.0 +requests +python-dotenv diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/run_jupyter.sh b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/run_jupyter.sh new file mode 100755 index 000000000..d725c3fe7 --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/run_jupyter.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# """ +# Launch Jupyter Lab server. +# +# This script starts Jupyter Lab on port 8888 with the following configuration: +# - No browser auto-launch (useful for Docker containers) +# - Accessible from any IP address (0.0.0.0) +# - Root user allowed (required for Docker environments) +# - No authentication token or password (for development convenience) +# - Vim keybindings can be enabled via JUPYTER_USE_VIM environment variable +# """ + +# Exit immediately if any command exits with a non-zero status. +set -e + +# Print each command to stdout before executing it. +#set -x + +# Import the utility functions from /git_root. +GIT_ROOT=/git_root +source $GIT_ROOT/class_project/project_template/utils.sh + +# Load Docker configuration variables for this script. +get_docker_vars_script ${BASH_SOURCE[0]} +source $DOCKER_NAME +print_docker_vars + +# Setup Jupyter Lab environment. +setup_jupyter_environment + +# Initialize Jupyter Lab command with base configuration. +JUPYTER_ARGS=$(get_jupyter_args) + +# Start Jupyter Lab with development-friendly settings. +run "jupyter lab $JUPYTER_ARGS" diff --git a/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/version.sh b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/version.sh new file mode 100755 index 000000000..c46ed254c --- /dev/null +++ b/class_project/data605/Spring2026/projects/UmdTask459_DATA605_Spring2026_Apache_Flink_real_time_anomaly_detection/version.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# """ +# Display versions of installed tools and packages. +# +# This script prints version information for Python, pip, Jupyter, and all +# installed Python packages. Used for debugging and documentation purposes +# to verify the Docker container environment setup. +# """ + +# Display Python 3 version. +echo "# Python3" +python3 --version + +# Display pip version. +echo "# pip3" +pip3 --version + +# Display Jupyter version. +echo "# jupyter" +jupyter --version + +# List all installed Python packages and their versions. +echo "# Python packages" +pip3 list + +# Template for adding additional tool versions. +# echo "# mongo" +# mongod --version