Spaces:
Sleeping
Sleeping
| 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 | |
| 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 | |
| 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.") | |