Skip to content
Snippets Groups Projects
demand-metric-plot.ipynb 18.5 KiB
Newer Older
   "source": [
    "# Theodolite Analysis - Plotting the Demand Metric\n",
    "\n",
    "This notebook creates a plot, showing scalability as a function that maps load intensities to the resources required for processing them. It is able to combine multiple such plots in one figure, for example, to compare multiple systems or configurations.\n",
    "\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."
   ],
   "metadata": {}
  },
  {
   "source": [
    "First, we need to import some libraries, which are required for creating the plots."
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "source": [
    "import os\n",
    "import pandas as pd\n",
    "from functools import reduce\n",
    "import matplotlib.pyplot as plt\n",
    "from matplotlib.ticker import FuncFormatter\n",
    "from matplotlib.ticker import MaxNLocator"
   "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\"`."
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "source": [
    "results_dir = '<path-to>/results'\n",
    "\n",
    "experiments = {\n",
    "    'System XYZ': 'exp200',\n",
    "}\n"
   "source": [
    "Now, we combie all systems described in `experiments`."
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "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)"
   "source": [
    "We might want to display the mappings before we plot it."
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "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": {}
   "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."
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "source": [
    "plt.style.use('ggplot')\n",
    "plt.rcParams['pdf.fonttype'] = 42 # TrueType fonts\n",
    "plt.rcParams['ps.fonttype'] = 42 # TrueType fonts\n",
    "plt.rcParams['axes.facecolor']='w'\n",
    "plt.rcParams['axes.edgecolor']='555555'\n",
    "#plt.rcParams['ytick.color']='black'\n",
    "plt.rcParams['grid.color']='dddddd'\n",
    "plt.rcParams['axes.spines.top']='false'\n",
    "plt.rcParams['axes.spines.right']='false'\n",
    "plt.rcParams['legend.frameon']='true'\n",
    "plt.rcParams['legend.framealpha']='1'\n",
    "plt.rcParams['legend.edgecolor']='1'\n",
    "plt.rcParams['legend.borderpad']='1'\n",
    "\n",
    "@FuncFormatter\n",
    "def load_formatter(x, pos):\n",
    "    return f'{(x/1000):.0f}k'\n",
    "\n",
    "markers = ['s', 'D', 'o', 'v', '^', '<', '>', 'p', 'X']\n",
    "\n",
    "def splitSerToArr(ser):\n",
    "    return [ser.index, ser.as_matrix()]\n",
    "\n",
    "plt.figure()\n",
    "#plt.figure(figsize=(4.8, 3.6)) # For other plot sizes\n",
    "#ax = df.plot(kind='line', marker='o')\n",
    "for i, column in enumerate(df):\n",
    "    plt.plot(df[column].dropna(), marker=markers[i], label=column)\n",
    "plt.legend()\n",
    "ax = plt.gca()\n",
    "#ax = df.plot(kind='line',x='dim_value', legend=False, use_index=True)\n",
    "ax.set_ylabel('number of instances')\n",
    "ax.set_xlabel('messages/second')\n",
    "ax.set_ylim(ymin=0)\n",
    "#ax.set_xlim(xmin=0)\n",
    "ax.yaxis.set_major_locator(MaxNLocator(integer=True))\n",
    "ax.xaxis.set_major_formatter(FuncFormatter(load_formatter))\n",
    "\n",
    "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,
   "outputs": [],
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "version": "3.7.12",
   "mimetype": "text/x-python",
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "pygments_lexer": "ipython3",
   "nbconvert_exporter": "python",
   "file_extension": ".py"
  },
  "orig_nbformat": 2,
  "file_extension": ".py",
  "mimetype": "text/x-python",
  "name": "python",
  "npconvert_exporter": "python",
  "pygments_lexer": "ipython3",
  "version": 3,
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3.7.12 64-bit ('theodolite-venv': venv)"
  },
  "interpreter": {
   "hash": "a52a208d39582630a5c8f3f944b955c1bf9503ec3936114be1f708238321db18"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}