================================================================================
  Experiment 17: Random Forest Air Quality Classification (city_day.csv)
================================================================================

FULL QUESTION:
Develop a Random Forest model to classify air quality into Good, Satisfactory, Moderate, Poor, Very Poor, or Severe using PM2.5, PM10, NO2, CO, SO2, and O3 data. Preprocess the dataset, train and evaluate the model, and predict the air quality category for a new set of pollutant readings.

SHORT QUESTION:
Random Forest 6-class air quality classifier trained on city_day.csv with evaluation and interactive single-sample prediction.

================================================================================
README & SETUP INSTRUCTIONS:
================================================================================
# Experiment 17: Random Forest Air Quality Classification

## Objective
Train a Random Forest classifier to categorize ambient air quality into Good, Satisfactory, Moderate, Poor, Very Poor, or Severe using PM2.5, PM10, NO2, CO, SO2, and O3.

## Dataset
Kaggle Indian Air Quality dataset (`city_day.csv`). Ensure `city_day.csv` is present in this folder.

## Requirements
```bash
pip install pandas scikit-learn streamlit
```

## How to Run
- **CLI Mode:**
  ```bash
  python air_quality_cli.py
  ```
- **Streamlit Web Dashboard:**
  ```bash
  streamlit run air_quality_app.py
  ```

================================================================================
SOURCE CODE FILES:
================================================================================

1. air_quality_cli.py
--------------------------------------------------------------------------------
"""
================================================================================
IOT EXPERIMENT 17: Random Forest Multi-Class Air Quality Classifier (CLI Pipeline)
================================================================================
Develop a Random Forest model to classify air quality into Good, Satisfactory,
Moderate, Poor, Very Poor, or Severe using PM2.5, PM10, NO2, CO, SO2, and O3
data. Preprocess the dataset, train and evaluate the model, and predict the
air quality category for a new set of pollutant readings.
================================================================================
"""

# Short Description: Trains an ensemble Random Forest Classifier on Indian air quality data (city_day.csv) to classify air pollution into 6 official AQI hazard tiers.

"""
README / BEFORE RUNNING:
- Prerequisites: Python 3.x, pandas, numpy, scikit-learn (pip install pandas numpy scikit-learn).
- Dataset: Uses 'city_day.csv' bundled in the directory.
- How to Run: python air_quality_cli.py
- Expected Behavior: Cleans dataset, trains Random Forest, evaluates precision/recall across all 6 AQI buckets, and predicts classification for custom pollutant inputs.
"""

import os
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
data_path = os.path.join(BASE_DIR, "city_day.csv") if os.path.exists(os.path.join(BASE_DIR, "city_day.csv")) else "city_day.csv"
data = pd.read_csv(data_path)

features = ["PM2.5", "PM10", "NO2", "CO", "SO2", "O3"]
target = "AQI_Bucket"

required_columns = features + [target]
missing_columns = [col for col in required_columns if col not in data.columns]
if missing_columns:
    print("Missing columns:", missing_columns)
    exit()

data = data[features + [target]].dropna()

X = data[features]
y = data[target]

print("\nAir Quality Category Distribution:")
print(y.value_counts())

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.20, random_state=42, stratify=y
)

model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)

y_pred = model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)

print("\nModel Accuracy: {:.2f}%".format(accuracy * 100))

print("\nConfusion Matrix:")
cm = confusion_matrix(y_test, y_pred)
print(cm)

print("\nClassification Report:")
print(classification_report(y_test, y_pred))

print("\nFeature Importances:")
for feat, imp in zip(features, model.feature_importances_):
    print("{:6s}: {:.4f}".format(feat, imp))

def prompt_float(prompt_text: str, default_val: float) -> float:
    inp = input(f"{prompt_text} [default {default_val}]: ").strip()
    return float(inp) if inp else default_val

print("\nEnter New Pollutant Readings:")
pm25 = prompt_float("PM2.5 (ug/m3) ", 65.0)
pm10 = prompt_float("PM10 (ug/m3)  ", 115.0)
no2  = prompt_float("NO2 (ug/m3)   ", 35.0)
co   = prompt_float("CO (mg/m3)    ", 1.1)
so2  = prompt_float("SO2 (ug/m3)   ", 14.5)
o3   = prompt_float("O3 (ug/m3)    ", 38.0)

new_data = pd.DataFrame({
    "PM2.5": [pm25],
    "PM10":  [pm10],
    "NO2":   [no2],
    "CO":    [co],
    "SO2":   [so2],
    "O3":    [o3]
})

prediction = model.predict(new_data)
probability = model.predict_proba(new_data)

print("\nPredicted Category:", prediction[0])
print("\nPrediction Probabilities:")
for cat, prob in zip(model.classes_, probability[0]):
    print("{:14s}: {:.2f}%".format(cat, prob * 100))
--------------------------------------------------------------------------------

2. air_quality_app.py
--------------------------------------------------------------------------------
"""
================================================================================
IOT EXPERIMENT 17: Random Forest Air Quality Classifier (Web App)
================================================================================
Develop a Random Forest model to classify air quality into Good, Satisfactory,
Moderate, Poor, Very Poor, or Severe using PM2.5, PM10, NO2, CO, SO2, and O3
data. Preprocess the dataset, train and evaluate the model, and predict the
air quality category for a new set of pollutant readings.
================================================================================
"""

# Short Description: Interactive web dashboard to predict air quality category (Good, Satisfactory, Moderate, Poor, Very Poor, Severe) for user-entered pollutant readings.

"""
README / BEFORE RUNNING:
- Prerequisites: Python 3.x, flask, pandas, scikit-learn (pip install flask pandas scikit-learn).
- How to Run: python air_quality_app.py (open http://localhost:5000 in your browser).
- Expected Behavior: Launches interactive web app predicting official AQI classification for PM2.5, PM10, NO2, CO, SO2, and O3 values.
"""

import os
import streamlit as st
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, confusion_matrix

st.set_page_config(page_title="Air Quality Classification", layout="wide")
st.title("Air Quality Classification")
st.write("Random Forest Classification using Pollutant Readings")

features = ["PM2.5", "PM10", "NO2", "CO", "SO2", "O3"]
target = "AQI_Bucket"

@st.cache_resource
def load_and_train():
    base_dir = os.path.dirname(os.path.abspath(__file__))
    data_path = os.path.join(base_dir, "city_day.csv") if os.path.exists(os.path.join(base_dir, "city_day.csv")) else "city_day.csv"
    data = pd.read_csv(data_path)
    data = data[features + [target]].dropna()

    X = data[features]
    y = data[target]

    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.20, random_state=42, stratify=y
    )

    model = RandomForestClassifier(n_estimators=100, random_state=42)
    model.fit(X_train, y_train)

    y_pred = model.predict(X_test)
    accuracy = accuracy_score(y_test, y_pred)
    cm = confusion_matrix(y_test, y_pred, labels=model.classes_)

    return model, accuracy, cm, model.classes_

try:
    with st.spinner("Training Random Forest model on city_day.csv..."):
        model, accuracy, cm, classes = load_and_train()
except Exception as e:
    st.error(f"Error loading dataset: {e}")
    st.stop()

# Performance Display
st.subheader("Model Performance")
st.metric("Model Accuracy", f"{accuracy * 100:.2f}%")

col_a, col_b = st.columns(2)
with col_a:
    st.write("**Confusion Matrix**")
    st.dataframe(pd.DataFrame(cm, index=classes, columns=classes), use_container_width=True)

with col_b:
    st.write("**Feature Importances**")
    feat_df = pd.DataFrame({"Feature": features, "Importance": model.feature_importances_})
    st.dataframe(feat_df.sort_values("Importance", ascending=False), hide_index=True, use_container_width=True)

st.divider()

# Input Form & Prediction
st.subheader("Enter Pollutant Readings for Prediction")
c1, c2 = st.columns(2)
with c1:
    pm25 = st.number_input("PM2.5 (ug/m3)", min_value=0.0, value=75.0, step=1.0)
    pm10 = st.number_input("PM10 (ug/m3)", min_value=0.0, value=140.0, step=1.0)
    no2  = st.number_input("NO2 (ug/m3)", min_value=0.0, value=42.0, step=1.0)
with c2:
    co   = st.number_input("CO (mg/m3)", min_value=0.0, value=1.4, step=0.1)
    so2  = st.number_input("SO2 (ug/m3)", min_value=0.0, value=16.0, step=1.0)
    o3   = st.number_input("O3 (ug/m3)", min_value=0.0, value=48.0, step=1.0)

if st.button("Predict Air Quality Category"):
    new_data = pd.DataFrame({
        "PM2.5": [pm25], "PM10": [pm10], "NO2": [no2],
        "CO": [co], "SO2": [so2], "O3": [o3]
    })
    prediction = model.predict(new_data)[0]
    st.success(f"Predicted Air Quality: **{prediction}**")
--------------------------------------------------------------------------------
