# Licensed under a 3-clause BSD style license - see LICENSE.rst
from __future__ import absolute_import, division, print_function, unicode_literals
import numpy as np
import astropy.units as u
from astropy.units import Quantity
from astropy.coordinates import SkyCoord
from .obs_stats import ObservationStats
__all__ = [
'ObservationTableSummary',
'ObservationSummary',
]
[docs]class ObservationTableSummary(object):
"""Observation table summary.
Class allowing to summarize informations contained in
Observation index table (`~gammapy.data.ObservationTable`)
Parameters
----------
obs_table : `~gammapy.data.ObservationTable`
Observation index table
target_pos : `~astropy.coordinates.SkyCoord`
Target position
"""
def __init__(self, obs_table, target_pos=None):
self.obs_table = obs_table
self.target_pos = target_pos
@property
def offset(self):
"""Observation pointing ot target offset (`~astropy.coordinates.Angle`).
"""
pnt_pos = SkyCoord(self.obs_table['RA_PNT'],
self.obs_table['DEC_PNT'],
unit='deg')
offset = pnt_pos.separation(self.target_pos)
return offset
def __str__(self):
"""Summary report (`str`).
"""
ss = '*** Observation summary ***\n'
ss += 'Target position: {}\n'.format(self.target_pos)
ss += 'Number of observations: {}\n'.format(len(self.obs_table))
livetime = Quantity(sum(self.obs_table['LIVETIME']), 'second')
ss += 'Livetime: {:.2f}\n'.format(livetime.to('hour'))
zenith = self.obs_table['ZEN_PNT']
ss += 'Zenith angle: (mean={:.2f}, std={:.2f})\n'.format(
zenith.mean(), zenith.std())
offset = self.offset
ss += 'Offset: (mean={:.2f}, std={:.2f})\n'.format(
offset.mean(), offset.std())
return ss
[docs] def plot_zenith_distribution(self, ax=None, bins=None):
"""Construct the zenith distribution of the observations.
Parameters
----------
ax : `~matplotlib.axes.Axes` or None, optional.
The `~matplotlib.axes.Axes` object to be drawn on.
If None, uses the current `~matplotlib.axes.Axes`.
bins : integer or array_like, optional
Binning specification, passed to `matplotlib.pyplot.hist`.
By default, 30 bins from 0 deg to max zenith + 5 deg is used.
Returns
--------
ax : `~matplotlib.axes.Axes`
Axis
"""
import matplotlib.pyplot as plt
ax = plt.gca() if ax is None else ax
zenith = self.obs_table['ZEN_PNT']
if bins is None:
bins = np.linspace(0, zenith.max() + 5, 30)
ax.hist(zenith, bins=bins)
ax.set_title('Zenith distribution')
ax.set_xlabel('Zenith (Deg)')
ax.set_ylabel('#Entries')
return ax
[docs] def plot_offset_distribution(self, ax=None, bins=None):
"""Construct the offset distribution of the observations.
Parameters
----------
ax : `~matplotlib.axes.Axes` or None, optional.
The `~matplotlib.axes.Axes` object to be drawn on.
If None, uses the current `~matplotlib.axes.Axes`.
bins : integer or array_like, optional
Binning specification, passed to `matplotlib.pyplot.hist`.
By default, 10 bins from 0 deg to max offset + 0.5 deg is used.
Returns
-------
ax : `~matplotlib.axes.Axes`
Axis
"""
import matplotlib.pyplot as plt
ax = plt.gca() if ax is None else ax
offset = self.offset
if bins is None:
bins = np.linspace(0, offset.degree.max() + 0.5, 10)
ax.hist(offset.degree, bins=bins)
ax.set_title('Offset distribution')
ax.set_xlabel('Offset (Deg)')
ax.set_ylabel('#Entries')
return ax
[docs]class ObservationSummary(object):
"""Summary of observations.
For a list of observation stats, this class can make a
table and do summary printout and plots.
* TODO: Data should be stored in `~astropy.table.Table`!
* TODO: there should be a per-run version of the stats
in addition to the cumulative version that's there now.
Parameters
----------
obs_stats : list
Python list of `~gammapy.data.ObservationStats`
"""
def __init__(self, obs_stats):
self.obs_stats = obs_stats
self.obs_id = np.zeros(len(self.obs_stats))
self.livetime = np.zeros(len(self.obs_stats)) * u.s
self.n_on = np.zeros(len(self.obs_stats))
self.n_off = np.zeros(len(self.obs_stats))
self.alpha = np.zeros(len(self.obs_stats))
self.background = np.zeros(len(self.obs_stats))
self.excess = np.zeros(len(self.obs_stats))
self.sigma = np.zeros(len(self.obs_stats))
self.gamma_rate = np.zeros(len(self.obs_stats)) / u.min
self.bg_rate = np.zeros(len(self.obs_stats)) / u.min
self._init_values()
def _init_values(self):
"""Initialise vector attributes for plotting methods."""
stats_list = []
for index, obs in enumerate(self.obs_stats):
# per observation stat
self.obs_id[index] = obs.obs_id # keep track of the observation
self.gamma_rate[index] = obs.gamma_rate
self.bg_rate[index] = obs.bg_rate
# cumulative information
stats_list.append(obs)
stack = ObservationStats.stack(stats_list)
self.livetime[index] = stack.livetime
self.n_on[index] = stack.n_on
self.n_off[index] = stack.n_off
self.alpha[index] = stack.alpha
self.background[index] = stack.background
self.excess[index] = stack.excess
self.sigma[index] = stack.sigma
[docs] def obs_wise_summary(self):
"""Observation wise summary report (`str`)."""
ss = '*** Observation Wise summary ***\n'
for obs in self.obs_stats:
ss += '{}\n'.format(obs)
return ss
def __str__(self):
"""Observation summary report (`str`)."""
stack = ObservationStats.stack(self.obs_stats)
ss = '*** Observation summary ***\n'
ss += '{}\n'.format(stack)
return ss
[docs] def plot_significance_vs_livetime(self, ax=None, **kwargs):
"""Plot significance as a function of livetime.
Parameters
----------
ax : `~matplotlib.axes.Axes` or None, optional.
The `~matplotlib.axes.Axes` object to be drawn on.
Returns
-------
ax : `~matplotlib.axes.Axes`
Axis
"""
import matplotlib.pyplot as plt
ax = plt.gca() if ax is None else ax
ax.plot(self.livetime.to(u.h), self.sigma, "o", **kwargs)
ax.set_xlabel('Livetime ({})'.format(u.h))
ax.set_ylabel('Significance ($\sigma$)')
ax.axis([0., np.amax(self.livetime.to(u.h).value) * 1.2,
0., np.amax(self.sigma) * 1.2])
ax.set_title('Significance evolution')
return ax
[docs] def plot_excess_vs_livetime(self, ax=None, **kwargs):
"""Plot excess as a function of livetime.
Parameters
----------
ax : `~matplotlib.axes.Axes` or None, optional.
The `~matplotlib.axes.Axes` object to be drawn on.
Returns
-------
ax : `~matplotlib.axes.Axes`
Axis
"""
import matplotlib.pyplot as plt
ax = plt.gca() if ax is None else ax
ax.plot(self.livetime.to(u.h), self.excess, "o", **kwargs)
ax.set_xlabel('Livetime ({})'.format(u.h))
ax.set_ylabel('Excess')
ax.axis([0., np.amax(self.livetime.to(u.h).value) * 1.2,
0., np.amax(self.excess) * 1.2])
ax.set_title('Excess evolution')
return ax
[docs] def plot_background_vs_livetime(self, ax=None, **kwargs):
"""Plot background as a function of livetime.
Parameters
----------
ax : `~matplotlib.axes.Axes` or None, optional.
The `~matplotlib.axes.Axes` object to be drawn on.
Returns
-------
ax : `~matplotlib.axes.Axes`
Axis
"""
import matplotlib.pyplot as plt
ax = plt.gca() if ax is None else ax
ax.plot(self.livetime.to(u.h), self.background, "o", **kwargs)
ax.set_xlabel('Livetime ({})'.format(u.h))
ax.set_ylabel('Background')
ax.axis([0., np.amax(self.livetime.to(u.h).value) * 1.2,
0., np.amax(self.background) * 1.2])
ax.set_title('Background evolution')
return ax
[docs] def plot_gamma_rate(self, ax=None, **kwargs):
"""Plot gamma rate for each observation.
Parameters
----------
ax : `~matplotlib.axes.Axes` or None, optional.
The `~matplotlib.axes.Axes` object to be drawn on.
Returns
-------
ax : `~matplotlib.axes.Axes`
Axis
"""
import matplotlib.pyplot as plt
ax = plt.gca() if ax is None else ax
xtick_vals, xtick_labels = self._get_xtick_info()
ax.plot(xtick_vals, self.gamma_rate, "o", **kwargs)
ax.set_xlabel('Observation Ids')
ax.set_xticks(xtick_vals)
ax.set_xticklabels(xtick_labels, rotation=-22.5)
ax.set_ylabel('$\gamma$ rate ({})'.format(self.gamma_rate.unit))
ax.axis([0, len(self.gamma_rate),
0., np.amax(self.gamma_rate.value) * 1.2])
ax.set_title('$\gamma$ rates')
return ax
[docs] def plot_background_rate(self, ax=None, **kwargs):
"""Plot background rate for each observation.
Parameters
----------
ax : `~matplotlib.axes.Axes` or None, optional.
The `~matplotlib.axes.Axes` object to be drawn on.
Returns
-------
ax : `~matplotlib.axes.Axes`
Axis
"""
import matplotlib.pyplot as plt
ax = plt.gca() if ax is None else ax
xtick_vals, xtick_labels = self._get_xtick_info()
ax.plot(xtick_vals, self.bg_rate, "o", **kwargs)
ax.set_xlabel('Observation Ids')
ax.set_xticks(xtick_vals)
ax.set_xticklabels(xtick_labels, rotation=-22.5)
ax.set_ylabel('Background rate ({})'.format(self.bg_rate.unit))
ax.axis([0, len(self.bg_rate),
0., np.amax(self.bg_rate.value) * 1.2])
ax.set_title('Background rates')
return ax
def _get_xtick_info(self):
idxs = list(range(len(self.obs_stats)))
vals = [idx + 0.5 for idx in idxs]
labels = [str(int(self.obs_id[idx])) for idx in idxs]
return vals, labels