{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# How Many Units Will We Use?\n",
    "\n",
    "home_energy.py\n",
    "\n",
    "AI project on Statistical Data: predicting how many units of electricity a\n",
    "home will use in a summer month, from what is in the home.\n",
    "\n",
    "CBSE Class X, Artificial Intelligence (417), Part C project work.\n",
    "SDG 7 (Affordable and Clean Energy) and SDG 13 (Climate Action).\n",
    "\n",
    "This is a REGRESSION project: the answer is a number, not a group. Three\n",
    "models are compared, each a little cleverer than the last:\n",
    "  A. always guess the average home\n",
    "  B. a line of best fit through one feature (AC hours a day)\n",
    "  C. a weighted sum of every feature - each feature times a weight, plus a\n",
    "     bias, the same sum a perceptron works out in Unit 2\n",
    "\n",
    "They are judged the way the curriculum judges a regression model:\n",
    "  error = |actual - predicted|,  error rate = error / actual,\n",
    "  accuracy = 1 - error rate,  model accuracy = mean of all the accuracies.\n",
    "\n",
    "Libraries: pandas (the data), numpy (the arithmetic), matplotlib (charts)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Stage 1 - Problem scoping"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"=\" * 60)\n",
    "print(\"HOW MANY UNITS WILL WE USE?\")\n",
    "print(\"=\" * 60)\n",
    "print(\"Who   : families paying the electricity bill\")\n",
    "print(\"What  : the summer bill arrives as a shock, too late to act\")\n",
    "print(\"Where : homes in a hot city, in the months the AC runs\")\n",
    "print(\"Why   : knowing in advance helps a family save power\")\n",
    "print()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Stage 2 - Data acquisition"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "pd.set_option(\"display.width\", 120)\n",
    "pd.set_option(\"display.max_columns\", 20)\n",
    "\n",
    "data = pd.read_csv(\"home_energy.csv\")\n",
    "\n",
    "print(\"--- Stage 2: Data acquisition ---\")\n",
    "print(\"Rows read    :\", len(data))\n",
    "print(\"Columns read :\", len(data.columns))\n",
    "print()\n",
    "print(\"The first 10 rows:\")\n",
    "print(data.head(10))\n",
    "print()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Stage 3 - Data exploration"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"--- Stage 3: Data exploration ---\")\n",
    "\n",
    "# A home with no meter reading cannot teach the model anything.\n",
    "missing = data[\"units\"].isnull().sum()\n",
    "data = data.dropna(subset=[\"units\"])\n",
    "print(\"Rows with no meter reading       :\", missing, \"(removed)\")\n",
    "\n",
    "# A day has 24 hours, so 60 or 80 AC hours a day is a typing mistake.\n",
    "impossible = (data[\"ac_hours\"] > 24).sum()\n",
    "data = data[data[\"ac_hours\"] <= 24]\n",
    "print(\"Rows with more than 24 AC hours  :\", impossible, \"(removed)\")\n",
    "print(\"Rows left                        :\", len(data))\n",
    "print()\n",
    "\n",
    "# One new feature, made from two: how many AC-hours the home runs in a day.\n",
    "# Two ACs for 5 hours use about as much power as one AC for 10.\n",
    "data[\"ac_total_hours\"] = data[\"ac_count\"] * data[\"ac_hours\"]\n",
    "\n",
    "units = data[\"units\"]\n",
    "print(\"Units in a month - mean   :\", round(np.mean(units), 1))\n",
    "print(\"Units in a month - median :\", np.median(units))\n",
    "print(\"Units in a month - lowest :\", units.min(), \" highest :\", units.max())\n",
    "print()\n",
    "\n",
    "by_members = data.groupby(\"members\")[\"units\"].mean().round(1)\n",
    "print(\"Average units, by number of people in the home:\")\n",
    "print(by_members)\n",
    "print()\n",
    "\n",
    "# Chart 1: AC hours a day against units.\n",
    "plt.figure(figsize=(7, 5))\n",
    "plt.scatter(data[\"ac_total_hours\"], data[\"units\"], alpha=0.6, s=18, color=\"#f28e2b\")\n",
    "plt.title(\"AC hours a day and units used in the month\")\n",
    "plt.xlabel(\"AC hours a day (all ACs added up)\")\n",
    "plt.ylabel(\"Units (kWh) in the month\")\n",
    "plt.grid(True, linestyle=\"--\", alpha=0.4)\n",
    "plt.tight_layout()\n",
    "plt.savefig(\"chart1_ac_hours.png\")\n",
    "plt.close()\n",
    "\n",
    "# Chart 2: average units by family size.\n",
    "plt.figure(figsize=(7, 4))\n",
    "plt.bar(by_members.index, by_members.values, color=\"#4e79a7\")\n",
    "plt.title(\"Average units used, by number of people\")\n",
    "plt.xlabel(\"People in the home\")\n",
    "plt.ylabel(\"Average units (kWh)\")\n",
    "plt.tight_layout()\n",
    "plt.savefig(\"chart2_members.png\")\n",
    "plt.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Stage 4 - Modelling"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"--- Stage 4: Modelling ---\")\n",
    "\n",
    "features = [\"members\", \"rooms\", \"ac_total_hours\", \"geyser\"]\n",
    "label = \"units\"\n",
    "\n",
    "data = data.sample(frac=1, random_state=5)\n",
    "cut = int(len(data) * 0.8)\n",
    "train = data[:cut]\n",
    "test = data[cut:]\n",
    "print(\"Training rows :\", len(train))\n",
    "print(\"Testing rows  :\", len(test))\n",
    "print()\n",
    "\n",
    "# Model A: always guess the average of the training homes.\n",
    "average = train[label].mean()\n",
    "predict_a = np.full(len(test), average)\n",
    "print(\"Model A guesses every home will use\", round(average, 1), \"units\")\n",
    "\n",
    "# Model B: the straight line that fits AC hours and units best.\n",
    "# np.polyfit with 1 finds the slope and the starting point of that line.\n",
    "slope, start = np.polyfit(train[\"ac_total_hours\"], train[label], 1)\n",
    "predict_b = slope * test[\"ac_total_hours\"] + start\n",
    "print(\"Model B line : units =\", round(start, 1), \"+\", round(slope, 1), \"x AC hours a day\")\n",
    "\n",
    "# Model C: a weighted sum of all four features, plus a bias.\n",
    "#     units = bias + w1 x members + w2 x rooms + w3 x AC hours + w4 x geyser\n",
    "# np.linalg.lstsq finds the weights that make the predictions on the\n",
    "# training homes as close as possible to the real units. The column of 1s\n",
    "# is what lets it find the bias, exactly like the bias input of a perceptron.\n",
    "X_train = train[features].copy()\n",
    "X_train[\"bias\"] = 1\n",
    "weights = np.linalg.lstsq(X_train.values, train[label].values.astype(float), rcond=None)[0]\n",
    "\n",
    "print()\n",
    "print(\"What model C learned (one weight for each feature):\")\n",
    "for i in range(len(features)):\n",
    "    print(\"  \" + features[i].ljust(16), round(weights[i], 1))\n",
    "print(\"  \" + \"bias\".ljust(16), round(weights[4], 1))\n",
    "print()\n",
    "\n",
    "X_test = test[features].copy()\n",
    "X_test[\"bias\"] = 1\n",
    "predict_c = X_test.values.dot(weights)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Stage 5 - Evaluation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"--- Stage 5: Evaluation ---\")\n",
    "actual = test[label].values.astype(float)\n",
    "\n",
    "names = [\"A: average\", \"B: one line\", \"C: weighted sum\"]\n",
    "model_accuracy = []\n",
    "mean_error = []\n",
    "for predicted in [predict_a, predict_b, predict_c]:\n",
    "    error = np.abs(actual - predicted)          # error = |actual - predicted|\n",
    "    error_rate = error / actual                 # error rate = error / actual\n",
    "    accuracy = 1 - error_rate                   # accuracy = 1 - error rate\n",
    "    model_accuracy.append(np.mean(accuracy) * 100)\n",
    "    mean_error.append(np.mean(error))\n",
    "\n",
    "for i in range(3):\n",
    "    print(\"Model \" + names[i].ljust(16), \"average error :\", str(round(mean_error[i], 1)).rjust(6),\n",
    "          \"units   accuracy :\", round(model_accuracy[i], 1), \"%\")\n",
    "print()\n",
    "print(\"Average error of model C :\", round(mean_error[2], 1), \"units\")\n",
    "print(\"Accuracy of model C      :\", round(model_accuracy[2], 1), \"%\")\n",
    "print(\"Accuracy of model B      :\", round(model_accuracy[1], 1), \"%\")\n",
    "print(\"Accuracy of model A      :\", round(model_accuracy[0], 1), \"%\")\n",
    "print()\n",
    "\n",
    "# Chart 3: model C's predictions against the real units.\n",
    "plt.figure(figsize=(6, 6))\n",
    "plt.scatter(actual, predict_c, alpha=0.7, s=20, color=\"#59a14f\")\n",
    "top = max(actual.max(), predict_c.max())\n",
    "plt.plot([0, top], [0, top], color=\"#999999\", linestyle=\"--\", label=\"Perfect prediction\")\n",
    "plt.title(\"Model C: predicted against actual units\")\n",
    "plt.xlabel(\"Actual units\")\n",
    "plt.ylabel(\"Predicted units\")\n",
    "plt.legend()\n",
    "plt.grid(True, linestyle=\"--\", alpha=0.4)\n",
    "plt.tight_layout()\n",
    "plt.savefig(\"chart3_predicted_actual.png\")\n",
    "plt.close()\n",
    "\n",
    "# Chart 4: the three models compared.\n",
    "plt.figure(figsize=(7, 4))\n",
    "plt.bar(names, model_accuracy, color=[\"#bab0ac\", \"#f28e2b\", \"#59a14f\"])\n",
    "plt.ylim(0, 100)\n",
    "plt.title(\"Accuracy of the three models (testing homes)\")\n",
    "plt.ylabel(\"Model accuracy (%)\")\n",
    "for i in range(3):\n",
    "    plt.text(i, model_accuracy[i] + 2, str(round(model_accuracy[i], 1)) + \"%\", ha=\"center\")\n",
    "plt.tight_layout()\n",
    "plt.savefig(\"chart4_models.png\")\n",
    "plt.close()\n",
    "\n",
    "# Chart 5: how far off each testing home was, for model C.\n",
    "plt.figure(figsize=(7, 4))\n",
    "plt.hist(actual - predict_c, bins=15, color=\"#59a14f\")\n",
    "plt.title(\"Model C: how far off each prediction was\")\n",
    "plt.xlabel(\"Actual minus predicted (units)\")\n",
    "plt.ylabel(\"Number of homes\")\n",
    "plt.tight_layout()\n",
    "plt.savefig(\"chart5_errors.png\")\n",
    "plt.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Stage 6 - Deployment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"--- Stage 6: Deployment ---\")\n",
    "homes = pd.DataFrame({\n",
    "    \"members\":        [4, 2, 6],\n",
    "    \"rooms\":          [3, 1, 4],\n",
    "    \"ac_total_hours\": [16, 0, 24],\n",
    "    \"geyser\":         [1, 0, 1],\n",
    "})\n",
    "homes[\"bias\"] = 1\n",
    "forecast = homes.values.dot(weights)\n",
    "for i in range(len(homes)):\n",
    "    h = homes.iloc[i]\n",
    "    print(\"People\", h[\"members\"], \" rooms\", h[\"rooms\"], \" AC hours a day\", h[\"ac_total_hours\"],\n",
    "          \" geyser\", h[\"geyser\"], \"->\", round(forecast[i]), \"units\")\n",
    "\n",
    "# What would two hours less AC a day save the first home?\n",
    "fewer = homes.iloc[[0]].copy()\n",
    "fewer[\"ac_total_hours\"] = fewer[\"ac_total_hours\"] - 2\n",
    "saving = forecast[0] - fewer.values.dot(weights)[0]\n",
    "print(\"Saving from two AC hours a day less :\", round(saving), \"units a month for the first home\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
