{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":9994,"databundleVersionId":752467,"sourceType":"competition"},{"sourceId":2838328,"sourceType":"datasetVersion","datasetId":1736649}],"dockerImageVersionId":31234,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# 📊 Optimisation de la consommation énergétique dans les bâtiments de bureaux\n\n## Description du projet\n\nCe projet vise à **analyser et optimiser la consommation énergétique des bâtiments de bureaux** en utilisant les données fournies par le **ASHRAE - Great Energy Predictor III**.\nL’objectif est de **détecter les gaspillages énergétiques et proposer des règles d’optimisation simples** basées sur l’occupation des locaux et la température ambiante.\n\nLe pipeline comprend :\n\n1. Prétraitement et échantillonnage des données volumineuses.\n2. Jointure des données bâtiment et météo.\n3. Simulation de l’occupation des bureaux.\n4. Détection des anomalies dans la consommation électrique avec un modèle non supervisé (`Isolation Forest`).\n5. Estimation du gaspillage énergétique.\n6. Application de règles d’optimisation pour réduire la consommation.\n7. Visualisation et calcul des économies potentielles.","metadata":{}},{"cell_type":"markdown","source":"## 📥 Chargement des bibliothèques et des données","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom sklearn.ensemble import IsolationForest\nfrom sklearn.preprocessing import StandardScaler\nimport warnings\nwarnings.filterwarnings(\"ignore\", category=UserWarning)\nDATA_PATH = \"/kaggle/input/ashrae-energy-prediction/\"\n\n# Chargement des métadonnées des bâtiments et données météo\nbuilding = pd.read_csv(DATA_PATH + \"building_metadata.csv\")\nweather = pd.read_csv(DATA_PATH + \"weather_train.csv\")\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-01-01T21:15:54.612633Z","iopub.execute_input":"2026-01-01T21:15:54.613015Z","iopub.status.idle":"2026-01-01T21:15:54.752605Z","shell.execute_reply.started":"2026-01-01T21:15:54.612988Z","shell.execute_reply":"2026-01-01T21:15:54.751099Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 🔹 Échantillonnage des données volumineuses\n\nPour travailler sur un sous-ensemble représentatif (bureaux et consommation électrique uniquement) :\n\n","metadata":{}},{"cell_type":"code","source":"chunksize = 1_000_000\ntrain_chunks = pd.read_csv(DATA_PATH + \"train.csv\", chunksize=chunksize)\n\nsample_list = []\n\nfor chunk in train_chunks:\n    chunk = chunk[chunk[\"meter\"] == 0]  # électricité uniquement\n    chunk = chunk.merge(building[[\"building_id\", \"site_id\", \"primary_use\"]],\n                        on=\"building_id\", how=\"left\")\n    chunk = chunk[chunk[\"primary_use\"] == \"Office\"]\n    if len(chunk) > 0:\n        chunk = chunk.sample(frac=0.01, random_state=42)\n        sample_list.append(chunk)\n\ntrain = pd.concat(sample_list, ignore_index=True)\nprint(\"Dataset après échantillonnage :\", train.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-01T21:15:54.754205Z","iopub.execute_input":"2026-01-01T21:15:54.754591Z","iopub.status.idle":"2026-01-01T21:16:09.030253Z","shell.execute_reply.started":"2026-01-01T21:15:54.754560Z","shell.execute_reply":"2026-01-01T21:16:09.028869Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 🔹 Jointure avec les données météo","metadata":{}},{"cell_type":"code","source":"train = train.merge(weather, on=[\"site_id\", \"timestamp\"], how=\"left\")\n\n# Conversion en datetime\nweather[\"timestamp\"] = pd.to_datetime(weather[\"timestamp\"])\ntrain[\"timestamp\"] = pd.to_datetime(train[\"timestamp\"])\n\n# Extraction d'informations temporelles\ntrain[\"hour\"] = train[\"timestamp\"].dt.hour\ntrain[\"weekday\"] = train[\"timestamp\"].dt.weekday","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-01T21:16:09.031278Z","iopub.execute_input":"2026-01-01T21:16:09.031552Z","iopub.status.idle":"2026-01-01T21:16:09.134335Z","shell.execute_reply.started":"2026-01-01T21:16:09.031530Z","shell.execute_reply":"2026-01-01T21:16:09.133211Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 🔹 Simulation de l’occupation\nOn simule la présence dans les bureaux selon l’heure et le jour :","metadata":{}},{"cell_type":"code","source":"def simulate_occupancy(row):\n    if row[\"weekday\"] >= 5:  # week-end\n        return 0\n    elif 8 <= row[\"hour\"] < 12 or 13 <= row[\"hour\"] < 18:\n        return 1\n    else:\n        return 0\n\ntrain[\"occupancy\"] = train.apply(simulate_occupancy, axis=1)\n\n# Ajout de bruit pour simuler les capteurs\nnoise = np.random.choice([0, 1], size=len(train), p=[0.1, 0.9])\ntrain[\"occupancy\"] = train[\"occupancy\"] * noise","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-01T21:16:09.136589Z","iopub.execute_input":"2026-01-01T21:16:09.136923Z","iopub.status.idle":"2026-01-01T21:16:09.460429Z","shell.execute_reply.started":"2026-01-01T21:16:09.136860Z","shell.execute_reply":"2026-01-01T21:16:09.459152Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 🔹 Prétraitement des variables","metadata":{}},{"cell_type":"code","source":"train[\"air_temperature\"] = train[\"air_temperature\"].fillna(train[\"air_temperature\"].mean())\ntrain[\"meter_reading\"] = train[\"meter_reading\"].fillna(0)\n\nfeatures = [\"meter_reading\", \"air_temperature\", \"hour\"]\nX = train[features]\n\n# Normalisation\nscaler = StandardScaler()\nX_scaled = pd.DataFrame(scaler.fit_transform(X), columns=features, index=train.index)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-01T21:16:09.461576Z","iopub.execute_input":"2026-01-01T21:16:09.461968Z","iopub.status.idle":"2026-01-01T21:16:09.492429Z","shell.execute_reply.started":"2026-01-01T21:16:09.461945Z","shell.execute_reply":"2026-01-01T21:16:09.491207Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 🔹 Détection des anomalies avec Isolation Forest\n","metadata":{}},{"cell_type":"code","source":"iso = IsolationForest(n_estimators=100, contamination=0.05, random_state=42)\ntrain[\"anomaly\"] = iso.fit_predict(X_scaled)\ntrain[\"anomaly\"] = train[\"anomaly\"].map({1: 0, -1: 1})\n\ntrain[\"waste\"] = ((train[\"anomaly\"] == 1) & (train[\"occupancy\"] == 0)).astype(int)\n\ntotal_kwh = train[\"meter_reading\"].sum()\nwasted_kwh = train.loc[train[\"waste\"] == 1, \"meter_reading\"].sum()\nsaving_pct = (wasted_kwh / total_kwh) * 100\n\nprint(f\"Consommation totale : {total_kwh:.2f} kWh\")\nprint(f\"Gaspillage détecté : {wasted_kwh:.2f} kWh\")\nprint(f\"Économie potentielle : {saving_pct:.2f} %\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-01T21:16:09.493471Z","iopub.execute_input":"2026-01-01T21:16:09.493792Z","iopub.status.idle":"2026-01-01T21:16:11.113738Z","shell.execute_reply.started":"2026-01-01T21:16:09.493761Z","shell.execute_reply":"2026-01-01T21:16:11.112671Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 🔹 Optimisation de la consommation\nApplication de règles simples basées sur l’occupation et la température :","metadata":{}},{"cell_type":"code","source":"train[\"optimized_reading\"] = train[\"meter_reading\"].copy()\n\n# Éteindre appareils si salle vide\ntrain.loc[train[\"occupancy\"]==0, \"optimized_reading\"] *= 0.5\n\n# Réduire consommation si température confortable\nmask_comfort = (train[\"air_temperature\"] >= 20) & (train[\"air_temperature\"] <= 24)\ntrain.loc[mask_comfort, \"optimized_reading\"] *= 0.8\n\noptimized_total = train[\"optimized_reading\"].sum()\noptimized_saving_pct = (total_kwh - optimized_total) / total_kwh * 100\n\nprint(f\"Consommation totale après optimisation : {optimized_total:.2f} kWh\")\nprint(f\"Économie potentielle : {optimized_saving_pct:.2f} %\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-01T21:16:11.114743Z","iopub.execute_input":"2026-01-01T21:16:11.115081Z","iopub.status.idle":"2026-01-01T21:16:11.131355Z","shell.execute_reply.started":"2026-01-01T21:16:11.115052Z","shell.execute_reply":"2026-01-01T21:16:11.130185Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 🔹 Visualisation","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(12,5))\nplt.plot(train[\"timestamp\"], train[\"meter_reading\"], label=\"Consommation réelle\")\nplt.plot(train[\"timestamp\"], train[\"optimized_reading\"], label=\"Consommation optimisée\", alpha=0.8)\nplt.scatter(train.loc[train[\"waste\"]==1, \"timestamp\"],\n            train.loc[train[\"waste\"]==1, \"meter_reading\"],\n            color=\"red\", s=10, label=\"Gaspillage détecté\")\nplt.legend()\nplt.title(\"Optimisation automatique de la consommation énergétique\")\nplt.xlabel(\"Temps\")\nplt.ylabel(\"kWh\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-01T21:16:11.132553Z","iopub.execute_input":"2026-01-01T21:16:11.133011Z","iopub.status.idle":"2026-01-01T21:16:11.900108Z","shell.execute_reply.started":"2026-01-01T21:16:11.132977Z","shell.execute_reply":"2026-01-01T21:16:11.899094Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 🔹 Sauvegarde des données et du modèle","metadata":{}},{"cell_type":"code","source":"train.to_csv(\"/kaggle/working/train_prepared.csv\", index=False)\nimport joblib\njoblib.dump(iso, \"energy_model.pkl\")\nenergy_model = joblib.load(\"energy_model.pkl\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-01T21:16:11.900818Z","iopub.execute_input":"2026-01-01T21:16:11.901106Z","iopub.status.idle":"2026-01-01T21:16:12.238313Z","shell.execute_reply.started":"2026-01-01T21:16:11.901083Z","shell.execute_reply":"2026-01-01T21:16:12.237107Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import joblib\njoblib.dump(energy_model, \"energy_model_fixed.joblib\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}