{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# A sample of plots\n",
    "\n",
    "The idea of this notebook is to show off a number of plot types, and act as a simple check of the plotting output. It requires `matplotlib` and does not attempt to describe the plots (see the help for the plot constructor for this!)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sherpa import data\n",
    "from sherpa.astro import data as astrodata\n",
    "\n",
    "from sherpa import plot\n",
    "from sherpa.astro import plot as astroplot"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## One dimensional data plots"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 17452 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "x1 = [100, 200, 600, 1200]\n",
    "y1 = [2000, 2100, 1400, 3050]\n",
    "\n",
    "d1 = data.Data1D('oned', x1, y1)\n",
    "\n",
    "plot1 = plot.DataPlot()\n",
    "plot1.prepare(d1)\n",
    "plot1.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can have some fun with the plot options (these are a mixture of generic options, such as `xlog`, and ones specific to the plotting backend - which here is `matplotlib` - such as `marker`)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 29288 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot1.plot(xlog=True, linestyle='dotted', marker='*', markerfacecolor='orange', markersize=20, color='black')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The plot object contains the preferences - here we look at the default plot settings. Note that some plot types have different - and even multiple - preference settings."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'xlog': False,\n",
       " 'ylog': False,\n",
       " 'label': None,\n",
       " 'xerrorbars': False,\n",
       " 'yerrorbars': True,\n",
       " 'color': None,\n",
       " 'linestyle': 'None',\n",
       " 'linewidth': None,\n",
       " 'marker': '.',\n",
       " 'alpha': None,\n",
       " 'markerfacecolor': None,\n",
       " 'markersize': None,\n",
       " 'ecolor': None,\n",
       " 'capsize': None}"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "plot.DataPlot.plot_prefs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Error bars - here on the dependent axis - can be displayed too:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 14640 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "dy1 = [100, 50, 200, 300]\n",
    "d2 = data.Data1D('errors', x1, y1, dy1)\n",
    "\n",
    "plot2 = plot.DataPlot()\n",
    "plot2.prepare(d2)\n",
    "\n",
    "plot2.plot()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 15320 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot2.plot(capsize=4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Histogram-style data (with low and high edges) are handled similarly:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 10300 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "xlo2 = [0.1, 0.2, 0.4, 0.8, 1.5]\n",
    "xhi2 = [0.2, 0.4, 0.6, 1.1, 2.0]\n",
    "y2 = [10, 12, 3, 0, 4]\n",
    "\n",
    "data3 = data.Data1DInt('int1', xlo2, xhi2, y2)\n",
    "\n",
    "plot3 = plot.DataHistogramPlot()\n",
    "plot3.prepare(data3)\n",
    "plot3.plot(xlog=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we want to see the data drawn \"like a histogram\" then we need to set the `linestyle` attribute:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 10724 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot3.plot(xlog=True, linestyle='solid')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The histogram-style plots are an example of a plot using a different name for the preference settings, in this case `histo_prefs`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'xlog': False,\n",
       " 'ylog': False,\n",
       " 'label': None,\n",
       " 'xerrorbars': False,\n",
       " 'yerrorbars': True,\n",
       " 'color': None,\n",
       " 'linestyle': 'None',\n",
       " 'linewidth': None,\n",
       " 'marker': '.',\n",
       " 'alpha': None,\n",
       " 'markerfacecolor': None,\n",
       " 'markersize': None,\n",
       " 'ecolor': None,\n",
       " 'capsize': None}"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "plot.DataHistogramPlot.histo_prefs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Previously we explicitly set the error values, but we can also use one of the chi-square statistics to come up with error values. In this case it's just the square-root of the data value (so, for $x \\sim 1$ bin, we have an error of 0):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 17068 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from sherpa.stats import Chi2DataVar\n",
    "\n",
    "plot4 = plot.DataHistogramPlot()\n",
    "plot4.prepare(data3, stat=Chi2DataVar())\n",
    "plot4.plot(linestyle='dashed', marker=None, ecolor='orange', capsize=4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Axis labelling\n",
    "\n",
    "The axis labels depend on the plot object and the data used in the `prepare` call. Functionality added in release 4.17.0 allows us to change the labels with the `set_xlabel` and `set_ylabel` calls of the `Data` class:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 18352 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# These changes must be made before the call to prepare.\n",
    "data3.set_xlabel(\"distance [kpc]\")\n",
    "data3.set_ylabel(\"Relative abundance\")\n",
    "\n",
    "plot5 = plot.DataHistogramPlot()\n",
    "plot5.prepare(data3)\n",
    "\n",
    "plot5.plot(linestyle=\"solid\", marker=None)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## PHA-related plots\n",
    "\n",
    "We start with an ARF from a rather simple instrument. This time we also use the `SplitPlot` class to create multiple plots (although this could be done just as easily with `matplotlib` functions):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 23632 characters]",
      "text/plain": [
       "<Figure size 640x480 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "energies = np.arange(0.1, 11, 0.01)\n",
    "elo = energies[:-1]\n",
    "ehi = energies[1:]\n",
    "\n",
    "arf = 100 * np.ones_like(elo)\n",
    "arf[elo < 4] = 200\n",
    "\n",
    "darf = astrodata.DataARF('arf', elo, ehi, arf)\n",
    "aplot = astroplot.ARFPlot()\n",
    "aplot.prepare(darf)\n",
    "\n",
    "splot = plot.SplitPlot()\n",
    "\n",
    "splot.addplot(aplot)\n",
    "splot.addplot(aplot, xlog=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The preferences for the split plot are empty by default, because there are no backend-independent settings:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{}"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "splot.plot_prefs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "However, here we are using matplotlib and we can get \"hspace\" \"wspace\" etc as in [plt.subplots_adjust](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.subplots_adjust.html) to tweak the plot layout:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 23696 characters]",
      "text/plain": [
       "<Figure size 640x480 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "splot.reset()\n",
    "splot.plot_prefs['hspace'] = 0.6\n",
    "\n",
    "splot.addplot(aplot)\n",
    "splot.addplot(aplot, xlog=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A PHA, which matches the ARF, can be created (with a sinusoidal pattern just to show something different):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 20184 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "chans = np.arange(1, len(elo) + 1, dtype=np.int16)\n",
    "counts = 5 + 5 * np.sin(elo * 4)\n",
    "counts = counts.astype(int)\n",
    "\n",
    "dpha = astrodata.DataPHA('pha', chans, counts)\n",
    "\n",
    "pplot = astroplot.DataPHAPlot()\n",
    "pplot.prepare(dpha)\n",
    "\n",
    "pplot.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Adding the ARF to the data allows us to change to `energy` units:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 25256 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "dpha.set_arf(darf)\n",
    "dpha.set_analysis('energy')\n",
    "\n",
    "pplot.prepare(dpha)\n",
    "pplot.plot(linestyle='solid', marker=None)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Grouping the data - in this case in 20-channel groups - allows us to check the \"x errorbar\" handling (the 'errors' here just indicate the bin width, and so match the overplotted orange line):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 20672 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "dpha.group_bins(20)\n",
    "\n",
    "pplot.prepare(dpha, stat=Chi2DataVar())\n",
    "pplot.plot(xerrorbars=True, yerrorbars=True)\n",
    "pplot.overplot(linestyle='solid', alpha=0.5, marker=None)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can see how a model looks for this dataset - in this case a simple sinusoidal model which is multiplied by the ARF (shown earlier), and so is not going to match the data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "apply_arf(sin)\n",
      "   Param        Type          Value          Min          Max      Units\n",
      "   -----        ----          -----          ---          ---      -----\n",
      "   sin.period   thawed            4        1e-10           10           \n",
      "   sin.offset   thawed            0            0  3.40282e+38           \n",
      "   sin.ampl     thawed            1        1e-05  3.40282e+38           \n"
     ]
    }
   ],
   "source": [
    "from sherpa.models.basic import Sin\n",
    "from sherpa.astro.instrument import Response1D\n",
    "\n",
    "mdl = Sin()\n",
    "mdl.period = 4\n",
    "\n",
    "# Note that the response information - in this case the ARF and channel-to-energy mapping - needs\n",
    "# to be applied to the model, which is done by the Response1D class in this example.\n",
    "#\n",
    "rsp = Response1D(dpha)\n",
    "full_model = rsp(mdl)\n",
    "print(full_model)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that the `ModelHistogram` class does not use the grouping of the PHA dataset, so it shows the model evaluated per channel:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 40304 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "mplot = astroplot.ModelHistogram()\n",
    "mplot.prepare(dpha, full_model)\n",
    "\n",
    "mplot.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The discontinuity at 4 keV is because of the step function in the ARF (200 cm$^2$ below this energy and 100 cm$^2$ above it).\n",
    "\n",
    "The `ModelPHAHistogram` class does group the model to match the data:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 51300 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "mplot2 = astroplot.ModelPHAHistogram()\n",
    "mplot2.prepare(dpha, full_model)\n",
    "\n",
    "mplot.plot(label='ModelHistogram in original bins')\n",
    "mplot2.overplot(label='ModelPHAHistogram matching data binning')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Object-less plots\n",
    "\n",
    "There are a number of plot classes that don't need a data object, such as scatter plots:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 45848 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "rng = np.random.default_rng(1273)\n",
    "\n",
    "# I've never used the Wald distribution before, so let's see how it looks...\n",
    "#\n",
    "z1 = rng.wald(1000, 20, size=1000)\n",
    "z2 = rng.wald(1000, 2000, size=1000)\n",
    "\n",
    "# We take advantage of matplotlib functionality in labelling the axes and title.\n",
    "#\n",
    "splot = plot.ScatterPlot()\n",
    "splot.prepare(z1, z2, xlabel='z$_1$', ylabel='z$_2$', name='(z$_1$, z$_2$)')\n",
    "splot.plot(xlog=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "and cumulative plots:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 34328 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "cplot = plot.CDFPlot()\n",
    "\n",
    "cplot.prepare(z1, xlabel='z', name='z')\n",
    "cplot.plot(xlog=True)\n",
    "\n",
    "cplot.prepare(z2)\n",
    "cplot.overplot()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that this is a small sampling of the available plot types (although most are variants of plotting either the data or a model)."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
