grid_demo / app.py
OlamideKayode's picture
Upload app.py
4f21f44 verified
import os
import time
import pandas as pd
import numpy as np
import joblib
import requests
import streamlit as st
from streamlit_autorefresh import st_autorefresh
from sklearn.metrics import mean_absolute_error
import plotly.express as px
# Auto-refresh every 5 seconds
st_autorefresh(interval=5000, key="refresh")
# Load model
@st.cache_resource
def load_model():
return joblib.load("rf_model.pkl")
model = load_model()
# Supabase config
SUPABASE_URL = os.environ["SUPABASE_URL"]
SUPABASE_KEY = os.environ["SUPABASE_KEY"]
TABLE = "smart_meter_readings_1year"
# Initialize session state
if "row_index" not in st.session_state:
st.session_state.row_index = 0
if "history" not in st.session_state:
st.session_state.history = pd.DataFrame()
# Fetch all data
@st.cache_data
def fetch_all_data():
url = f"{SUPABASE_URL}/rest/v1/{TABLE}?select=*&order=timestamp.asc"
headers = {
"apikey": SUPABASE_KEY,
"Authorization": f"Bearer {SUPABASE_KEY}"
}
r = requests.get(url, headers=headers)
if r.ok:
return pd.DataFrame(r.json())
else:
st.error(f"โŒ Error fetching data: {r.status_code}")
return pd.DataFrame()
df_all = fetch_all_data()
# Debug sidebar
st.sidebar.title("๐Ÿ›  Debug Info")
st.sidebar.write("Row index:", st.session_state.row_index)
st.sidebar.write("Total rows:", len(df_all))
if not df_all.empty and st.session_state.row_index < len(df_all):
st.sidebar.write("Next row:", df_all.iloc[st.session_state.row_index].to_dict())
# Get next row
def get_next_row():
if st.session_state.row_index < len(df_all):
row = df_all.iloc[[st.session_state.row_index]]
st.session_state.row_index += 1
return row
return pd.DataFrame()
def forecast_next(df, model, steps=5):
forecasts = []
df_copy = df.copy()
expected_features = model.feature_names_in_.tolist()
for i in range(steps):
df_copy = engineer(df_copy).dropna()
# Ensure all expected features are present
for col in expected_features:
if col not in df_copy.columns:
df_copy[col] = 0
# Select and order input features
input_row = df_copy.iloc[[-1]][expected_features]
y_pred = model.predict(input_row)[0]
# Prepare next row
next_timestamp = df_copy.iloc[-1]["timestamp"] + 1800
new_row = df_copy.iloc[-1].copy()
new_row["power_consumption_kwh"] = y_pred
new_row["timestamp"] = next_timestamp
df_copy = pd.concat([df_copy, pd.DataFrame([new_row])], ignore_index=True)
forecasts.append({"timestamp": next_timestamp, "forecast_kwh": y_pred})
return pd.DataFrame(forecasts)
# Forecast ahead logic (5 steps, 30min intervals)
def forecast_next(df, model, steps=5):
forecasts = []
df_copy = df.copy()
expected_features = model.feature_names_in_.tolist()
for i in range(steps):
df_copy = engineer(df_copy).dropna()
# Ensure columns are aligned to model input
input_row = df_copy.iloc[[-1]][expected_features]
y_pred = model.predict(input_row)[0]
# Update the last row's power consumption with prediction
df_copy.at[df_copy.index[-1], "power_consumption_kwh"] = y_pred
# Prepare the next timestamp: add 1800 seconds (30 mins)
next_timestamp = df_copy.iloc[-1]["timestamp"] + 1800
# Prepare new row: copy last row features but update timestamp and power consumption
new_row = df_copy.iloc[-1].copy()
new_row["power_consumption_kwh"] = y_pred
new_row["timestamp"] = next_timestamp
# Append new row safely
df_copy = pd.concat([df_copy, pd.DataFrame([new_row])], ignore_index=True)
forecasts.append({"timestamp": next_timestamp, "forecast_kwh": y_pred})
return pd.DataFrame(forecasts)
# UI Layout
st.set_page_config(layout="wide")
st.title("โšก Gridflux: Real-Time Smart Meter Dashboard")
# Layout structure
col1, col2 = st.columns([2, 1])
# Data ingestion and prediction
new_row = get_next_row()
if not new_row.empty:
st.session_state.history = pd.concat([st.session_state.history, new_row], ignore_index=True)
df_feat = engineer(st.session_state.history).dropna()
if not df_feat.empty:
# Align features exactly to what model expects
expected_features = model.feature_names_in_.tolist()
for col in expected_features:
if col not in df_feat.columns:
df_feat[col] = 0
latest_input = df_feat[expected_features].iloc[[-1]]
prediction = model.predict(latest_input)[0]
actual = new_row["power_consumption_kwh"].values[0]
mae = mean_absolute_error([actual], [prediction])
with col1:
st.subheader("๐Ÿ“Š Real-Time Power Usage")
chart_df = st.session_state.history.copy()
chart_df["datetime"] = pd.to_datetime(chart_df["timestamp"])
chart_df.set_index("datetime", inplace=True)
st.line_chart(chart_df["power_consumption_kwh"], use_container_width=True)
st.subheader("๐Ÿ”ฎ Forecast (Next 2.5 Hours)")
future_df = forecast_next(df_feat, model, steps=5)
future_df["datetime"] = pd.to_datetime(future_df["timestamp"])
fig = px.line(future_df, x="datetime", y="forecast_kwh", title="Forecasted Power Usage (Next 2.5h)")
st.plotly_chart(fig, use_container_width=True)
with col2:
st.metric("๐Ÿ”ฎ Predicted Power Usage (kWh)", f"{prediction:.3f}")
st.metric("โœ… Actual Power Usage (kWh)", f"{actual:.3f}")
st.metric("๐Ÿ“ MAE", f"{mae:.3f}")
st.subheader("๐ŸŒ Region-Wise Forecast")
for region in ["east", "west", "north", "south"]:
regional = df_feat[df_feat[f"region_{region}"] == 1]
if not regional.empty:
preds = model.predict(regional[expected_features])
st.write(f"**{region.title()} Region**: Avg Forecast: {np.mean(preds):.3f} kWh")
st.subheader("๐Ÿ  Property Type Forecast")
for region in ["east", "west", "north", "south"]:
for ptype in ["commercial", "residential"]:
filtered = df_feat[(df_feat[f"region_{region}"] == 1) & (df_feat[f"property_type_{ptype}"] == 1)]
if not filtered.empty:
preds = model.predict(filtered[expected_features])
st.write(f"{region.title()} / {ptype.title()}: {np.mean(preds):.2f} kWh")
else:
st.success("โœ… All data processed.")