diff --git a/analysis/demand-metric-plot.ipynb b/analysis/demand-metric-plot.ipynb
index 90ef227dbf6a4566760329b615d5f59b4cc2bc25..a50929d76b407904c9eca51cbc9c7d4374ccc8e2 100644
--- a/analysis/demand-metric-plot.ipynb
+++ b/analysis/demand-metric-plot.ipynb
@@ -1,6 +1,7 @@
 {
  "cells": [
   {
+   "cell_type": "markdown",
    "source": [
     "# Theodolite Analysis - Plotting the Demand Metric\n",
     "\n",
@@ -8,21 +9,18 @@
     "\n",
     "The notebook takes a CSV file for each plot mapping load intensities to minimum required resources, computed by the `demand-metric-plot.ipynb` notebook."
    ],
-   "cell_type": "markdown",
    "metadata": {}
   },
   {
+   "cell_type": "markdown",
    "source": [
     "First, we need to import some libraries, which are required for creating the plots."
    ],
-   "cell_type": "markdown",
    "metadata": {}
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
+   "execution_count": 9,
    "source": [
     "import os\n",
     "import pandas as pd\n",
@@ -30,74 +28,123 @@
     "import matplotlib.pyplot as plt\n",
     "from matplotlib.ticker import FuncFormatter\n",
     "from matplotlib.ticker import MaxNLocator"
-   ]
+   ],
+   "outputs": [],
+   "metadata": {}
   },
   {
+   "cell_type": "markdown",
    "source": [
     "We need to specify the directory, where the demand CSV files can be found, and a dictionary that maps a system description (e.g. its name) to the corresponding CSV file (prefix). To use Unicode narrow non-breaking spaces in the description format it as `u\"1000\\u202FmCPU\"`."
    ],
-   "cell_type": "markdown",
    "metadata": {}
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
+   "execution_count": 11,
    "source": [
     "results_dir = '<path-to>/results'\n",
+    "plot_name = '<plot-name>'\n",
     "\n",
     "experiments = {\n",
     "    'System XYZ': 'exp200',\n",
     "}\n"
-   ]
+   ],
+   "outputs": [],
+   "metadata": {}
   },
   {
+   "cell_type": "markdown",
    "source": [
     "Now, we combie all systems described in `experiments`."
    ],
-   "cell_type": "markdown",
    "metadata": {}
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
+   "execution_count": 12,
    "source": [
     "dataframes = [pd.read_csv(os.path.join(results_dir, f'{v}_demand.csv')).set_index('load').rename(columns={\"resources\": k}) for k, v in experiments.items()]\n",
     "\n",
     "df = reduce(lambda df1,df2: df1.join(df2,how='outer'), dataframes)"
-   ]
+   ],
+   "outputs": [],
+   "metadata": {}
   },
   {
+   "cell_type": "markdown",
    "source": [
     "We might want to display the mappings before we plot it."
    ],
-   "cell_type": "markdown",
    "metadata": {}
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
+   "execution_count": 13,
    "source": [
     "df"
-   ]
+   ],
+   "outputs": [
+    {
+     "output_type": "execute_result",
+     "data": {
+      "text/html": [
+       "<div>\n",
+       "<style scoped>\n",
+       "    .dataframe tbody tr th:only-of-type {\n",
+       "        vertical-align: middle;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe tbody tr th {\n",
+       "        vertical-align: top;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe thead th {\n",
+       "        text-align: right;\n",
+       "    }\n",
+       "</style>\n",
+       "<table border=\"1\" class=\"dataframe\">\n",
+       "  <thead>\n",
+       "    <tr style=\"text-align: right;\">\n",
+       "      <th></th>\n",
+       "      <th>System XYZ</th>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>load</th>\n",
+       "      <th></th>\n",
+       "    </tr>\n",
+       "  </thead>\n",
+       "  <tbody>\n",
+       "    <tr>\n",
+       "      <th>50000</th>\n",
+       "      <td>1</td>\n",
+       "    </tr>\n",
+       "  </tbody>\n",
+       "</table>\n",
+       "</div>"
+      ],
+      "text/plain": [
+       "       System XYZ\n",
+       "load             \n",
+       "50000           1"
+      ]
+     },
+     "metadata": {},
+     "execution_count": 13
+    }
+   ],
+   "metadata": {}
   },
   {
+   "cell_type": "markdown",
    "source": [
     "The following code creates a MatPlotLib figure showing the scalability plots for all specified systems. You might want to adjust its styling etc. according to your preferences. Make sure to also set a filename."
    ],
-   "cell_type": "markdown",
    "metadata": {}
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
+   "execution_count": 14,
    "source": [
     "plt.style.use('ggplot')\n",
     "plt.rcParams['pdf.fonttype'] = 42 # TrueType fonts\n",
@@ -137,25 +184,42 @@
     "ax.yaxis.set_major_locator(MaxNLocator(integer=True))\n",
     "ax.xaxis.set_major_formatter(FuncFormatter(load_formatter))\n",
     "\n",
-    "plt.savefig('temp.pdf', bbox_inches='tight')"
-   ]
+    "plt.savefig(results_dir + '/' + plot_name + '.pdf', bbox_inches='tight')"
+   ],
+   "outputs": [
+    {
+     "output_type": "display_data",
+     "data": {
+      "image/png": "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",
+      "text/plain": [
+       "<Figure size 432x288 with 1 Axes>"
+      ]
+     },
+     "metadata": {}
+    }
+   ],
+   "metadata": {}
   },
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {},
+   "source": [],
    "outputs": [],
-   "source": []
+   "metadata": {}
   }
  ],
  "metadata": {
   "language_info": {
    "name": "python",
+   "version": "3.7.12",
+   "mimetype": "text/x-python",
    "codemirror_mode": {
     "name": "ipython",
     "version": 3
    },
-   "version": "3.8.5-final"
+   "pygments_lexer": "ipython3",
+   "nbconvert_exporter": "python",
+   "file_extension": ".py"
   },
   "orig_nbformat": 2,
   "file_extension": ".py",
@@ -165,9 +229,11 @@
   "pygments_lexer": "ipython3",
   "version": 3,
   "kernelspec": {
-   "name": "python37064bitvenvvenv6c432ee1239d4f3cb23f871068b0267d",
-   "display_name": "Python 3.7.0 64-bit ('.venv': venv)",
-   "language": "python"
+   "name": "python3",
+   "display_name": "Python 3.7.12 64-bit ('theodolite-venv': venv)"
+  },
+  "interpreter": {
+   "hash": "a52a208d39582630a5c8f3f944b955c1bf9503ec3936114be1f708238321db18"
   }
  },
  "nbformat": 4,
diff --git a/analysis/demand-metric.ipynb b/analysis/demand-metric.ipynb
index bcea129b7cb07465fa99f32b6f8b2b6115e8a0aa..bd574fe84d2e204f8043e751db13af078b9f3f4e 100644
--- a/analysis/demand-metric.ipynb
+++ b/analysis/demand-metric.ipynb
@@ -1,6 +1,7 @@
 {
  "cells": [
   {
+   "cell_type": "markdown",
    "source": [
     "# Theodolite Analysis - Demand Metric\n",
     "\n",
@@ -10,10 +11,10 @@
     "\n",
     "The final output when running this notebook will be a CSV file, providig this mapping. It can be used to create nice plots of a system's scalability using the `demand-metric-plot.ipynb` notebook."
    ],
-   "cell_type": "markdown",
    "metadata": {}
   },
   {
+   "cell_type": "markdown",
    "source": [
     "In the following cell, we need to specifiy:\n",
     "\n",
@@ -23,83 +24,109 @@
     "* `measurement_dir`: The directory where the measurement data files are to be found.\n",
     "* `results_dir`: The directory where the computed demand CSV files are to be stored."
    ],
-   "cell_type": "markdown",
    "metadata": {}
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
+   "execution_count": 1,
    "source": [
     "exp_id = 200\n",
     "warmup_sec = 60\n",
     "max_lag_trend_slope = 2000\n",
     "measurement_dir = '<path-to>/measurements'\n",
     "results_dir = '<path-to>/results'\n"
-   ]
+   ],
+   "outputs": [],
+   "metadata": {}
   },
   {
+   "cell_type": "markdown",
    "source": [
     "With the following call, we compute our demand mapping."
    ],
-   "cell_type": "markdown",
    "metadata": {}
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
+   "execution_count": 2,
    "source": [
     "from src.demand import demand\n",
     "\n",
     "demand = demand(exp_id, measurement_dir, max_lag_trend_slope, warmup_sec)"
-   ]
+   ],
+   "outputs": [],
+   "metadata": {}
   },
   {
+   "cell_type": "markdown",
    "source": [
     "We might already want to plot a simple visualization here:"
    ],
-   "cell_type": "markdown",
    "metadata": {}
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
+   "execution_count": 3,
    "source": [
     "demand.plot(kind='line',x='load',y='resources')"
-   ]
+   ],
+   "outputs": [
+    {
+     "output_type": "execute_result",
+     "data": {
+      "text/plain": [
+       "<matplotlib.axes._subplots.AxesSubplot at 0x7f58e20d8c10>"
+      ]
+     },
+     "metadata": {},
+     "execution_count": 3
+    },
+    {
+     "output_type": "display_data",
+     "data": {
+      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXoAAAEGCAYAAABrQF4qAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjAsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8GearUAAAU2ElEQVR4nO3df5BV5Z3n8fdXaIVERYUey4ArWKOWiKKkYXQcgytV/pyB0WxWrURBncJEk6rsVpxomRRZddadOLOZWGNBuRuiBjfikHXLjE6EwXGISYg2xN+IdBhdGx1pdMQlFkbgu3/cg7m03XQ3XPrCw/tVdarPfZ7n3Pt8b9/+9Lnn3D4dmYkkqVwHNHsCkqQ9y6CXpMIZ9JJUOINekgpn0EtS4YY2ewLdjRo1KseOHdvsaUjSPmXFihUbMrO1p769LujHjh1Le3t7s6chSfuUiHittz4P3UhS4Qx6SSqcQS9JhdvrjtFLKs+HH35IZ2cnmzdvbvZU9nnDhg1jzJgxtLS09Hsbg17SHtfZ2ckhhxzC2LFjiYhmT2eflZm8/fbbdHZ2Mm7cuH5v56EbSXvc5s2bGTlypCG/myKCkSNHDvidkUEvaVAY8o2xK8+jQS9JhTPoJalwBr2k/U5msm3btkF5rC1btgzK4+yMQS9pv/Dqq69ywgkncOWVVzJhwgRuvfVWJk+ezCmnnMKcOXMA+M1vfsNFF13ExIkTmTBhAgsXLgRg6dKlnHbaaZx88slcffXVfPDBB0Dtki0bNmwAoL29nbPPPhuAb33rW1xxxRWceeaZXHHFFbz11ltcfPHFTJw4kYkTJ/Lzn/8cgAULFjBlyhROPfVUrr32WrZu3crWrVuZNWsWEyZM4OSTT+Y73/nObtfuxyslDar/8uMXeemN9xp6n+M/dShz/uSkPsetWbOGe++9l/fee49Fixbx1FNPkZlMnz6dZcuW0dXVxac+9SkeeeQRADZu3MjmzZuZNWsWS5cu5fjjj+fKK69k7ty5fPWrX93pY7300ks8+eSTDB8+nEsvvZSpU6fy0EMPsXXrVjZt2sSqVatYuHAhP/vZz2hpaeG6667j/vvv56STTmLdunW88MILALz77ru7/fy4Ry9pv3HMMcdw+umns3jxYhYvXsxpp53GpEmTePnll1mzZg0nn3wyS5Ys4etf/zo//elPGTFiBKtXr2bcuHEcf/zxAMycOZNly5b1+VjTp09n+PDhADz++ON86UtfAmDIkCGMGDGCpUuXsmLFCiZPnsypp57K0qVLWbt2Lcceeyxr167lK1/5Cj/5yU849NBDd7tu9+glDar+7HnvKZ/85CeB2jH6m266iWuvvfZjY1auXMmjjz7KN77xDaZNm8aMGTN6vb+hQ4d+dKy/+2fbtz9WbzKTmTNncvvtt3+s79lnn+Wxxx5j3rx5PPjgg8yfP7/P2nbGPXpJ+53zzjuP+fPns2nTJgDWrVvH+vXreeONN/jEJz7BF77wBW644QZWrlzJCSecwKuvvkpHRwcAP/jBD5g6dSpQO0a/YsUKAH70ox/1+njTpk1j7ty5AGzdupWNGzcybdo0Fi1axPr16wF45513eO2119iwYQPbtm3js5/9LLfddhsrV67c7Xrdo5e03zn33HNZtWoVZ5xxBgAHH3wwCxYsoKOjgxtuuIEDDjiAlpYW5s6dy7Bhw/j+97/P5z73ObZs2cLkyZP54he/CMCcOXO45ppr+OY3v/nRidiefPe732X27Nl873vfY8iQIcydO5czzjiD2267jXPPPZdt27bR0tLCXXfdxfDhw7nqqqs+eqfQ0x7/QEVm7vadNFJbW1v6j0eksqxatYoTTzyx2dMoRk/PZ0SsyMy2nsZ76EaSCmfQS1LhDHpJg2JvO0y8r9qV59Ggl7THDRs2jLffftuw303br0c/bNiwAW3np24k7XFjxoyhs7OTrq6uZk9ln7f9P0wNhEEvaY9raWkZ0H9EUmN56EaSCmfQS1Lh+gz6iJgfEesj4oVe+iMi7oyIjoh4LiImdes/NCI6I+JvGzVpSVL/9WeP/h7g/J30XwAcVy2zgbnd+m8F+r7UmyRpj+gz6DNzGfDOTobMAO7LmuXAYRFxFEBEfBo4EljciMlKkgauEcfoRwOv193uBEZHxAHAXwNf6+sOImJ2RLRHRLsfv5KkxtqTJ2OvAx7NzM6+Bmbm3ZnZlpltra2te3BKkrT/acTn6NcBR9fdHlO1nQGcFRHXAQcDB0bEpsy8sQGPKUnqp0YE/cPAlyPiAeAPgI2Z+Sbw+e0DImIW0GbIS9Lg6zPoI+KHwNnAqIjoBOYALQCZOQ94FLgQ6ADeB67aU5OVJA1cn0GfmZf30Z/A9X2MuYfaxzQlSYPMv4yVpMIZ9JJUOINekgpn0EtS4Qx6SSqcQS9JhTPoJalwBr0kFc6gl6TCGfSSVDiDXpIKZ9BLUuEMekkqnEEvSYUz6CWpcAa9JBXOoJekwhn0klQ4g16SCmfQS1LhDHpJKpxBL0mFM+glqXAGvSQVzqCXpMIZ9JJUOINekgpn0EtS4Qx6SSqcQS9Jhesz6CNifkSsj4gXeumPiLgzIjoi4rmImFS1nxoRv4iIF6v2Sxs9eUlS3/qzR38PcP5O+i8AjquW2cDcqv194MrMPKna/m8i4rBdn6okaVcM7WtAZi6LiLE7GTIDuC8zE1geEYdFxFGZ+UrdfbwREeuBVuDd3ZyzJGkAGnGMfjTwet3tzqrtIxExBTgQ+HUDHk+SNAB7/GRsRBwF/AC4KjO39TJmdkS0R0R7V1fXnp6SJO1XGhH064Cj626PqdqIiEOBR4CbM3N5b3eQmXdnZltmtrW2tjZgSpKk7RoR9A8DV1afvjkd2JiZb0bEgcBD1I7fL2rA40iSdkGfJ2Mj4ofA2cCoiOgE5gAtAJk5D3gUuBDooPZJm6uqTf8j8BlgZETMqtpmZeYzDZy/JKkP/fnUzeV99CdwfQ/tC4AFuz41SVIj+JexklQ4g16SCmfQS1LhDHpJKpxBL0mFM+glqXAGvSQVzqCXpMIZ9JJUOINekgpn0EtS4Qx6SSqcQS9JhTPoJalwBr0kFc6gl6TCGfSSVDiDXpIKZ9BLUuEMekkqnEEvSYUz6CWpcAa9JBXOoJekwhn0klQ4g16SCmfQS1LhDHpJKpxBL0mFM+glqXAGvSQVrs+gj4j5EbE+Il7opT8i4s6I6IiI5yJiUl3fzIhYUy0zGzlxSVL/9GeP/h7g/J30XwAcVy2zgbkAEXEEMAf4A2AKMCciDt+dyUqSBq7PoM/MZcA7OxkyA7gva5YDh0XEUcB5wJLMfCcz/w1Yws5/YUiS9oBGHKMfDbxed7uzauut/WMiYnZEtEdEe1dXVwOmJEnabq84GZuZd2dmW2a2tba2Nns6klSURgT9OuDouttjqrbe2iVJg6gRQf8wcGX16ZvTgY2Z+SbwGHBuRBxenYQ9t2qTJA2ioX0NiIgfAmcDoyKik9onaVoAMnMe8ChwIdABvA9cVfW9ExG3Ak9Xd3VLZu7spK4kaQ/oM+gz8/I++hO4vpe++cD8XZuaJKkR9oqTsZKkPcegl6TCGfSSVDiDXpIKZ9BLUuEMekkqnEEvSYUz6CWpcAa9JBXOoJekwhn0klQ4g16SCmfQS1LhDHpJKpxBL0mFM+glqXAGvSQVzqCXpMIZ9JJUOINekgpn0EtS4Qx6SSqcQS9JhTPoJalwBr0kFc6gl6TCGfSSVDiDXpIKZ9BLUuEMekkqXL+CPiLOj4jVEdERETf20H9MRCyNiOci4omIGFPX9+2IeDEiVkXEnRERjSxAkrRzfQZ9RAwB7gIuAMYDl0fE+G7D/gq4LzNPAW4Bbq+2/UPgTOAUYAIwGZjasNlLkvrUnz36KUBHZq7NzN8CDwAzuo0ZDzxerf9TXX8Cw4ADgYOAFuCt3Z20JKn/+hP0o4HX6253Vm31ngUuqdYvBg6JiJGZ+Qtqwf9mtTyWmat2b8qSpIFo1MnYrwFTI+JX1A7NrAO2RsTvAycCY6j9cjgnIs7qvnFEzI6I9oho7+rqatCUJEnQv6BfBxxdd3tM1faRzHwjMy/JzNOAm6u2d6nt3S/PzE2ZuQn4B+CM7g+QmXdnZltmtrW2tu5iKZKknvQn6J8GjouIcRFxIHAZ8HD9gIgYFRHb7+smYH61/n+p7ekPjYgWanv7HrqRpEHUZ9Bn5hbgy8Bj1EL6wcx8MSJuiYjp1bCzgdUR8QpwJPAXVfsi4NfA89SO4z+bmT9ubAmSpJ2JzGz2HHbQ1taW7e3tzZ6GJO1TImJFZrb11OdfxkpS4Qx6SSqcQS9JhTPoJalwBr0kFc6gl6TCGfSSVDiDXpIKZ9BLUuEMekkqnEEvSYUz6CWpcAa9JBXOoJekwhn0klQ4g16SCmfQS1LhDHpJKpxBL0mFM+glqXAGvSQVzqCXpMIZ9JJUOINekgpn0EtS4Qx6SSqcQS9JhTPoJalwBr0kFc6gl6TCGfSSVLh+BX1EnB8RqyOiIyJu7KH/mIhYGhHPRcQTETGmru/fRcTiiFgVES9FxNjGTV+S1Jc+gz4ihgB3ARcA44HLI2J8t2F/BdyXmacAtwC31/XdB9yRmScCU4D1jZi4JKl/+rNHPwXoyMy1mflb4AFgRrcx44HHq/V/2t5f/UIYmplLADJzU2a+35CZS5L6pT9BPxp4ve52Z9VW71ngkmr9YuCQiBgJHA+8GxH/OyJ+FRF3VO8QdhARsyOiPSLau7q6Bl6FJKlXjToZ+zVgakT8CpgKrAO2AkOBs6r+ycCxwKzuG2fm3ZnZlpltra2tDZqSJAn6F/TrgKPrbo+p2j6SmW9k5iWZeRpwc9X2LrW9/2eqwz5bgP8DTGrIzCVJ/dKfoH8aOC4ixkXEgcBlwMP1AyJiVERsv6+bgPl12x4WEdt3088BXtr9aUuS+qvPoK/2xL8MPAasAh7MzBcj4paImF4NOxtYHRGvAEcCf1Ftu5XaYZulEfE8EMD/aHgVkqReRWY2ew47aGtry/b29mZPQ5L2KRGxIjPbeurzL2MlqXAGvSQVzqCXpMIZ9JJUOINekgpn0EtS4Qx6SSqcQS9JhTPoJalwBr0kFc6gl6TCGfSSVDiDXpIKZ9BLUuEMekkqnEEvSYUz6CWpcAa9JBXOoJekwhn0klQ4g16SCmfQS1LhDHpJKpxBL0mFi8xs9hx2EBFdwGvNnscuGAVsaPYkBpk17x+sed9wTGa29tSx1wX9vioi2jOzrdnzGEzWvH+w5n2fh24kqXAGvSQVzqBvnLubPYEmsOb9gzXv4zxGL0mFc49ekgpn0EtS4Qz6HkTEkIj4VUT8fXV7WkSsjIhnIuLJiPj9qv2giFgYER0R8cuIGFt3HzdV7asj4ry69vOrto6IuHGwa+tNDzWfU9X8QkTcGxFDq/aIiDur+T8XEZPq7mNmRKyplpl17Z+OiOerbe6MiBj8CncUEa9Wc3omItqrtiMiYkk1/yURcXjVXnLNn4uIFyNiW0S0dRs/oNdwRIyrfg46qp+LAwevup71UvMdEfFy9b18KCIOqxu/z9fco8x06bYA/xn4X8DfV7dfAU6s1q8D7qlbn1etXwYsrNbHA88CBwHjgF8DQ6rl18CxwIHVmPHNrrd7zdR2AF4Hjq/6bgGuqdYvBP4BCOB04JdV+xHA2urr4dX64VXfU9XYqLa9YC+o91VgVLe2bwM3Vus3An+5H9R8InAC8ATQVtc+4Ncw8CBwWbU+D/jSXlrzucDQav0v677PRdTc0+IefTcRMQa4CPifdc0JHFqtjwDeqNZnAPdW64uAadWe2wzggcz8IDP/BegAplRLR2auzczfAg9UY5uqh5pHAr/NzFeq20uAz1brM4D7smY5cFhEHAWcByzJzHcy89+qbc6v+g7NzOVZ+2m4D/jTwalswOq/n/fyu3kWW3NmrsrM1T10Deg1XL3uz6H2cwA7Pn97lcxcnJlbqpvLgTHVerE1G/Qf9zfAnwPb6tr+DHg0IjqBK4D/VrWPprbnS/XC2UgtJD9qr3RWbb21N1v3mjcAQ+veyv8H4OhqfaC1ja7Wu7c3WwKLI2JFRMyu2o7MzDer9X8FjqzWS665NwOteSTwbl2A7is1X03tHReUU/PHGPR1IuKPgfWZuaJb138CLszMMcD3gf8+6JPbQ3qqudoLvQz4TkQ8Bfw/YGuTprin/FFmTgIuAK6PiM/Ud1bPQWmfPd5pzYXqteaIuBnYAtzfrMkNFoN+R2cC0yPiVWpvz86JiEeAiZn5y2rMQuAPq/V1VHu61cnKEcDb9e2VMVVbb+3N1FPNCzLzF5l5VmZOAZZRO08BA69tHb97a1zf3lSZua76uh54iNrb87eqwy5UX9dXw0uuuTcDrfltaoe0hnZrb6reao6IWcAfA5+vfqlDITX3qNknCfbWBTib2onJodQOZWw/MXkN8KNq/Xp2PBn7YLV+Ejue1FlL7YTO0Gp9HL87qXNSs2vtXnO1/nvV14OApcA51e2L2PHE5FNV+xHAv1A7KXl4tX5E1df9xOSFTa7zk8Ahdes/B84H7mDHk7HfLr3muv4n2PFk7IBfw8DfseOJyev2xpqr5SWgtdv4fb7mXp+LZk9gb126hd7FwPPVN/gJ4NiqfVj1je6ofrCPrdv+Zmpn6ldT94kLap/geKXqu7nZde6k5juAVdX8v1o3JoC7qvk/3y0crq6eiw7gqrr2NuCFapu/pfqL7CbWeWz1vXwWeHH794HaMdelwBrgH/ldaJdc88XUji1/ALwFPLarr+HqMZ6qnou/Aw7aS2vuoHbM/ZlqmVdKzb0tXgJBkgrnMXpJKpxBL0mFM+glqXAGvSQVzqCXpMIZ9BIQEZsadD/fioivNeK+pEYx6CWpcAa9VKe69vwdUbsO//MRcWnVfnBELI3aNfqfj4gZddvcHBGvRMST1C75K+1VhvY9RNqvXAKcCkwERgFPR8QyoAu4ODPfi4hRwPKIeBiYRO3yF6dS+3laCXS/KJ7UVO7RSzv6I+CHmbk1M98C/hmYTO0yCP81Ip6jdnmE0dQuY3wW8FBmvp+Z7wEPN2neUq/co5f65/NAK/DpzPywutrnsOZOSeof9+ilHf0UuDRq/0O3FfgMtYtWjaB23f4PI+LfA8dU45cBfxoRwyPiEOBPmjJraSfco5d29BBwBrUrHibw55n5rxFxP/DjiHgeaAdeBsjMlRGxsBq/Hni6OdOWeufVKyWpcB66kaTCGfSSVDiDXpIKZ9BLUuEMekkqnEEvSYUz6CWpcP8fMAW1bK9K0kQAAAAASUVORK5CYII=",
+      "text/plain": [
+       "<Figure size 432x288 with 1 Axes>"
+      ]
+     },
+     "metadata": {
+      "needs_background": "light"
+     }
+    }
+   ],
+   "metadata": {}
   },
   {
+   "cell_type": "markdown",
    "source": [
     "Finally we store the results in a CSV file."
    ],
-   "cell_type": "markdown",
    "metadata": {}
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
+   "execution_count": 4,
    "source": [
     "import os\n",
     "\n",
     "demand.to_csv(os.path.join(results_dir, f'exp{exp_id}_demand.csv'), index=False)"
-   ]
+   ],
+   "outputs": [],
+   "metadata": {}
   }
  ],
  "metadata": {
   "language_info": {
    "name": "python",
+   "version": "3.7.12",
+   "mimetype": "text/x-python",
    "codemirror_mode": {
     "name": "ipython",
     "version": 3
    },
-   "version": "3.8.5-final"
+   "pygments_lexer": "ipython3",
+   "nbconvert_exporter": "python",
+   "file_extension": ".py"
   },
   "orig_nbformat": 2,
   "file_extension": ".py",
@@ -109,9 +136,11 @@
   "pygments_lexer": "ipython3",
   "version": 3,
   "kernelspec": {
-   "name": "python37064bitvenvvenv6c432ee1239d4f3cb23f871068b0267d",
-   "display_name": "Python 3.7.0 64-bit ('.venv': venv)",
-   "language": "python"
+   "name": "python3",
+   "display_name": "Python 3.7.12 64-bit ('theodolite-venv': venv)"
+  },
+  "interpreter": {
+   "hash": "a52a208d39582630a5c8f3f944b955c1bf9503ec3936114be1f708238321db18"
   }
  },
  "nbformat": 4,
diff --git a/analysis/src/demand.py b/analysis/src/demand.py
index dfb20c05af8e9a134eedd2cdb584c961a82369f5..85034a53365039898dc8f59b0c29253aa2fb3094 100644
--- a/analysis/src/demand.py
+++ b/analysis/src/demand.py
@@ -1,34 +1,30 @@
 import os
 from datetime import datetime, timedelta, timezone
 import pandas as pd
+from pandas.core.frame import DataFrame
 from sklearn.linear_model import LinearRegression
 
 def demand(exp_id, directory, threshold, warmup_sec):
     raw_runs = []
 
     # Compute SL, i.e., lag trend, for each tested configuration
-    filenames = [filename for filename in os.listdir(directory) if filename.startswith(f"exp{exp_id}") and filename.endswith("totallag.csv")]
+    filenames = [filename for filename in os.listdir(directory) if filename.startswith(f"exp{exp_id}") and filename.__contains__("lag-trend") and filename.endswith(".csv")]
     for filename in filenames:
         #print(filename)
         run_params = filename[:-4].split("_")
-        dim_value = run_params[2]
-        instances = run_params[3]
+        dim_value = run_params[1]
+        instances = run_params[2]
 
         df = pd.read_csv(os.path.join(directory, filename))
-        #input = df.loc[df['topic'] == "input"]
         input = df
-        #print(input)
+
         input['sec_start'] = input.loc[0:, 'timestamp'] - input.iloc[0]['timestamp']
-        #print(input)
-        #print(input.iloc[0, 'timestamp'])
+    
         regress = input.loc[input['sec_start'] >= warmup_sec] # Warm-Up
-        #regress = input
 
-        #input.plot(kind='line',x='timestamp',y='value',color='red')
-        #plt.show()
+        X = regress.iloc[:, 1].values.reshape(-1, 1)  # values converts it into a numpy array
+        Y = regress.iloc[:, 2].values.reshape(-1, 1)  # -1 means that calculate the dimension of rows, but have 1 column
 
-        X = regress.iloc[:, 2].values.reshape(-1, 1)  # values converts it into a numpy array
-        Y = regress.iloc[:, 3].values.reshape(-1, 1)  # -1 means that calculate the dimension of rows, but have 1 column
         linear_regressor = LinearRegression()  # create object for the class
         linear_regressor.fit(X, Y)  # perform linear regression
         Y_pred = linear_regressor.predict(X)  # make predictions
@@ -42,18 +38,19 @@ def demand(exp_id, directory, threshold, warmup_sec):
 
     runs = pd.DataFrame(raw_runs)
 
-    # Set suitable = True if SLOs are met, i.e., lag trend is below threshold
-    runs["suitable"] =  runs.apply(lambda row: row['trend_slope'] < threshold, axis=1)
-
-    # Sort results table (unsure if required)
-    runs.columns = runs.columns.str.strip()
-    runs.sort_values(by=["load", "resources"])
+    # Group by the load and resources to handle repetitions, and take from the reptitions the median
+    # for even reptitions the the average of the two middle values is used
+    medians = runs.groupby(by=['load', 'resources'], as_index=False).median()
 
-    # Filter only suitable configurations
-    filtered = runs[runs.apply(lambda x: x['suitable'], axis=1)]
-
-    # Compute demand per load intensity
-    grouped = filtered.groupby(['load'])['resources'].min()
-    demand_per_load = grouped.to_frame().reset_index()
+    # Set suitable = True if SLOs are met, i.e., lag trend is below threshold_ratio
+    # Calculate the absolute threshold for each row based on threshold_ratio and check if lag is below this threshold
+    medians["suitable"] =  medians.apply(lambda row: row['trend_slope'] < threshold, axis=1)
 
+    suitable = medians[medians.apply(lambda x: x['suitable'], axis=1)]
+    #print(suitable)
+    
+    # Compute minimal demand per load intensity
+    demand_per_load = suitable.groupby(by=['load'], as_index=False)['resources'].min()
+    
     return demand_per_load
+