Exemplo n.º 1
0
    def test__setitem__(self):
        # Set an entire field with data of not the same length.
        with self.assertRaises(ValueError):
            new_field2 = np.array([2.51, 2.12, 2.33, 2.44], dtype=np.float)
            self.arr['field2'] = new_field2

        # Set an entire field with new values.
        new_field2 = np.array([2.51, 2.12, 2.33, 2.44, 2.25], dtype=np.float)
        self.arr['field2'] = new_field2
        assert_array_almost_equal(self.arr['field2'], new_field2)

        # Reset the array.
        self.setUp()

        # Set selected rows with new values by indices.
        idx = np.array([1,4,2])
        new_data = dict(
            field1 = self.field1[idx],
            field2 = new_field2[idx],
            field3 = self.field3[idx]
        )
        new_arr = DataFieldRecordArray(new_data)
        self.arr[idx] = new_arr
        assert_array_almost_equal(self.arr['field1'], self.field1)
        assert_array_almost_equal(
            self.arr['field2'],
            np.array([2.5, 2.12, 2.33, 2.4, 2.25], dtype=np.float))
        assert_array_almost_equal(self.arr['field3'], self.field3)

        # Reset the array.
        self.setUp()

        # Set selected rows with new values by mask.
        mask = np.array([True, True, False, True, False])
        new_data = dict(
            field1 = self.field1[mask],
            field2 = new_field2[mask],
            field3 = self.field3[mask]
        )
        new_arr = DataFieldRecordArray(new_data)
        self.arr[mask] = new_arr
        assert_array_almost_equal(self.arr['field1'], self.field1)
        assert_array_almost_equal(
            self.arr['field2'],
            np.array([2.51, 2.12, 2.3, 2.44, 2.2], dtype=np.float))
        assert_array_almost_equal(self.arr['field3'], self.field3)

        # Reset the array.
        self.setUp()

        # Add a new field.
        new_field = np.array([4.2, 4.5, 4.1, 4.3, 4.4], dtype=np.float)
        self.arr['field4'] = new_field
        self.assertTrue('field4' in self.arr)
        self.assertTrue('field4' in self.arr.field_name_list)
        assert_array_almost_equal(
            self.arr['field4'],
            new_field)
Exemplo n.º 2
0
 def setUp(self):
     self.field1 = np.array([1.4, 1.3, 1.5, 1.1, 1.2], dtype=np.float)
     self.field2 = np.array([2.5, 2.1, 2.3, 2.4, 2.2], dtype=np.float)
     self.field3 = np.array([3.2, 3.5, 3.1, 3.3, 3.4], dtype=np.float)
     data = dict(
         field1 = self.field1,
         field2 = self.field2,
         field3 = self.field3
     )
     self.arr = DataFieldRecordArray(data)
     self.arr_len = 5
Exemplo n.º 3
0
    def plot(self, src_hypo_group_manager, axes):
        """Plots the spatial PDF. It uses the sin(dec) binning of the PDF to
        propperly represent the resolution of the PDF in the drawing.

        Parameters
        ----------
        src_hypo_group_manager : instance of SourceHypoGroupManager
            The instance of SourceHypoGroupManager that defines the source
            hypotheses.
        axes : mpl.axes.Axes
            The matplotlib Axes object on which the PDF should get drawn to.

        Returns
        -------
        img : instance of mpl.AxesImage
            The AxesImage instance showing the PDF image.
        """
        if(not isinstance(src_hypo_group_manager, SourceHypoGroupManager)):
            raise TypeError('The src_hypo_group_manager argument must be an '
                'instance of SourceHypoGroupManager!')
        if(not isinstance(axes, Axes)):
            raise TypeError('The axes argument must be an instance of '
                'matplotlib.axes.Axes!')

        # By construction the BackgroundI3SpatialPDF does not depend on
        # right-ascention. Hence, we only need a single bin for the
        # right-ascention.
        sin_dec_binning = self.pdf.get_binning('sin_dec')
        pdfprobs = np.zeros((1, sin_dec_binning.nbins))

        sin_dec_points = sin_dec_binning.bincenters
        events = DataFieldRecordArray(np.zeros((pdfprobs.size,),
                          dtype=[('sin_dec', np.float)]))
        for (i, sin_dec) in enumerate(sin_dec_points):
            events['sin_dec'][i] = sin_dec

        self._tdm.initialize_for_new_trial(src_hypo_group_manager, events)

        event_probs = self._pdf.get_prob(self._tdm)

        for i in range(len(events)):
            pdfprobs[0,i] = event_probs[i]

        ra_axis = self.pdf.axes.get_axis('ra')
        (left, right, bottom, top) = (
            ra_axis.vmin, ra_axis.vmax,
            sin_dec_binning.lower_edge, sin_dec_binning.upper_edge)
        img = axes.imshow(pdfprobs.T, extent=(left, right, bottom, top), origin='lower',
                    norm=LogNorm(), interpolation='none')
        axes.set_xlabel('ra')
        axes.set_ylabel('sin_dec')
        axes.set_title(classname(self.pdf))

        return img
Exemplo n.º 4
0
class DataFieldRecordArray_TestCase(unittest.TestCase):
    def setUp(self):
        self.field1 = np.array([1.4, 1.3, 1.5, 1.1, 1.2], dtype=np.float)
        self.field2 = np.array([2.5, 2.1, 2.3, 2.4, 2.2], dtype=np.float)
        self.field3 = np.array([3.2, 3.5, 3.1, 3.3, 3.4], dtype=np.float)
        data = dict(
            field1 = self.field1,
            field2 = self.field2,
            field3 = self.field3
        )
        self.arr = DataFieldRecordArray(data)
        self.arr_len = 5

    def test__contains__(self):
        self.assertFalse('field0' in self.arr)
        self.assertTrue('field1' in self.arr)
        self.assertTrue('field2' in self.arr)
        self.assertTrue('field3' in self.arr)

    def test__getitem__(self):
        # Access a non-existing field.
        with self.assertRaises(KeyError):
            self.arr['field0']

        # Access entire fields.
        assert_array_almost_equal(self.arr['field1'], self.field1)
        assert_array_almost_equal(self.arr['field2'], self.field2)
        assert_array_almost_equal(self.arr['field3'], self.field3)

        # Access rows of the dataset via indices.
        idx = np.array([1,4,2])
        sub_arr = self.arr[idx]
        assert_array_almost_equal(sub_arr['field1'], self.field1[idx])
        assert_array_almost_equal(sub_arr['field2'], self.field2[idx])
        assert_array_almost_equal(sub_arr['field3'], self.field3[idx])

        # Access rows of the dataset via a boolean mask.
        mask = np.array([True, True, False, True, False])
        sub_arr = self.arr[mask]
        assert_array_almost_equal(sub_arr['field1'], self.field1[mask])
        assert_array_almost_equal(sub_arr['field2'], self.field2[mask])
        assert_array_almost_equal(sub_arr['field3'], self.field3[mask])

    def test__setitem__(self):
        # Set an entire field with data of not the same length.
        with self.assertRaises(ValueError):
            new_field2 = np.array([2.51, 2.12, 2.33, 2.44], dtype=np.float)
            self.arr['field2'] = new_field2

        # Set an entire field with new values.
        new_field2 = np.array([2.51, 2.12, 2.33, 2.44, 2.25], dtype=np.float)
        self.arr['field2'] = new_field2
        assert_array_almost_equal(self.arr['field2'], new_field2)

        # Reset the array.
        self.setUp()

        # Set selected rows with new values by indices.
        idx = np.array([1,4,2])
        new_data = dict(
            field1 = self.field1[idx],
            field2 = new_field2[idx],
            field3 = self.field3[idx]
        )
        new_arr = DataFieldRecordArray(new_data)
        self.arr[idx] = new_arr
        assert_array_almost_equal(self.arr['field1'], self.field1)
        assert_array_almost_equal(
            self.arr['field2'],
            np.array([2.5, 2.12, 2.33, 2.4, 2.25], dtype=np.float))
        assert_array_almost_equal(self.arr['field3'], self.field3)

        # Reset the array.
        self.setUp()

        # Set selected rows with new values by mask.
        mask = np.array([True, True, False, True, False])
        new_data = dict(
            field1 = self.field1[mask],
            field2 = new_field2[mask],
            field3 = self.field3[mask]
        )
        new_arr = DataFieldRecordArray(new_data)
        self.arr[mask] = new_arr
        assert_array_almost_equal(self.arr['field1'], self.field1)
        assert_array_almost_equal(
            self.arr['field2'],
            np.array([2.51, 2.12, 2.3, 2.44, 2.2], dtype=np.float))
        assert_array_almost_equal(self.arr['field3'], self.field3)

        # Reset the array.
        self.setUp()

        # Add a new field.
        new_field = np.array([4.2, 4.5, 4.1, 4.3, 4.4], dtype=np.float)
        self.arr['field4'] = new_field
        self.assertTrue('field4' in self.arr)
        self.assertTrue('field4' in self.arr.field_name_list)
        assert_array_almost_equal(
            self.arr['field4'],
            new_field)

    def test__str__(self):
        try:
            str(self.arr)
        except:
            self.fail('The __str__ method raised an exception!')

    def test_field_name_list(self):
        self.assertEqual(len(self.arr.field_name_list), 3)
        self.assertTrue('field1' in self.arr.field_name_list)
        self.assertTrue('field2' in self.arr.field_name_list)
        self.assertTrue('field3' in self.arr.field_name_list)

    def test_indices(self):
        assert_array_almost_equal(
            self.arr.indices,
            np.array([0, 1, 2, 3, 4]))

    def test_len(self):
        self.assertEqual(len(self.arr), self.arr_len)

    def test_rename_fields(self):
        self.arr.rename_fields({'field2': 'new_field2'})
        self.assertTrue('field1' in self.arr)
        self.assertFalse('field2' in self.arr)
        self.assertTrue('new_field2' in self.arr)
        self.assertTrue('field3' in self.arr)
        self.assertEqual(len(self.arr.field_name_list), 3)
        self.assertTrue('field1' in self.arr.field_name_list)
        self.assertTrue('new_field2' in self.arr.field_name_list)
        self.assertTrue('field3' in self.arr.field_name_list)

    def test_sort_by_field(self):
        self.arr.sort_by_field('field2')
        assert_array_almost_equal(
            self.arr['field1'],
            np.array([1.3, 1.2, 1.5, 1.1, 1.4], dtype=np.float))
        assert_array_almost_equal(
            self.arr['field2'],
            np.array([2.1, 2.2, 2.3, 2.4, 2.5], dtype=np.float))
        assert_array_almost_equal(
            self.arr['field3'],
            np.array([3.5, 3.4, 3.1, 3.3, 3.2], dtype=np.float))

    def test_tidy_up(self):
        self.arr.tidy_up('field2')
        self.assertEqual(len(self.arr.field_name_list), 1)
        self.assertTrue('field2' in self.arr.field_name_list)
        self.assertFalse('field1' in self.arr)
        self.assertTrue('field2' in self.arr)
        self.assertFalse('field3' in self.arr)

        # Reset the array.
        self.setUp()

        self.arr.tidy_up(('field2','field3'))
        self.assertEqual(len(self.arr.field_name_list), 2)
        self.assertTrue('field2' in self.arr.field_name_list)
        self.assertTrue('field3' in self.arr.field_name_list)
        self.assertFalse('field1' in self.arr)
        self.assertTrue('field2' in self.arr)
        self.assertTrue('field3' in self.arr)
Exemplo n.º 5
0
    def plot(self, src_hypo_group_manager, axes, fitparams, **kwargs):
        """Plots the PDF ratio for the given set of fit paramater values.

        Parameters
        ----------
        src_hypo_group_manager : instance of SourceHypoGroupManager
            The instance of SourceHypoGroupManager that defines the source
            hypotheses.
        axes : mpl.axes.Axes
            The matplotlib Axes object on which the PDF ratio should get drawn
            to.
        fitparams : dict
            The dictionary with the set of fit paramater values.

        Additional Keyword Arguments
        ----------------------------
        Any additional keyword arguments will be passed to the `mpl.imshow`
        function.

        Returns
        -------
        img : instance of mpl.AxesImage
            The AxesImage instance showing the PDF ratio image.
        """
        if(not isinstance(src_hypo_group_manager, SourceHypoGroupManager)):
            raise TypeError('The src_hypo_group_manager argument must be an '
                'instance of SourceHypoGroupManager!')
        if(not isinstance(axes, Axes)):
            raise TypeError('The axes argument must be an instance of '
                'matplotlib.axes.Axes!')
        if(not isinstance(fitparams, dict)):
            raise TypeError('The fitparams argument must be an instance of '
                'dict!')

        # Get the binning for the axes. We use the background PDF to get it
        # from. By construction, all PDFs use the same binning. We know that
        # the PDFs are 2-dimensional.
        (xbinning, ybinning) = self._pdfratio.backgroundpdf.binnings

        # Create a 2D array with the ratio values. We put one event into each
        # bin.
        ratios = np.zeros((xbinning.nbins, ybinning.nbins), dtype=np.float)
        events = DataFieldRecordArray(np.zeros((ratios.size,),
            dtype=[('ix', np.int), (xbinning.name, np.float),
                   ('iy', np.int), (ybinning.name, np.float)]))
        for (i, ((ix,x),(iy,y))) in enumerate(itertools.product(
                enumerate(xbinning.bincenters),
                enumerate(ybinning.bincenters))):
            events['ix'][i] = ix
            events[xbinning.name][i] = x
            events['iy'][i] = iy
            events[ybinning.name][i] = y

        self._tdm.initialize_for_new_trial(src_hypo_group_manager, events)

        event_ratios = self.pdfratio.get_ratio(self._tdm, fitparams)
        for i in range(len(events)):
            ratios[events['ix'][i],events['iy'][i]] = event_ratios[i]

        (left, right, bottom, top) = (xbinning.lower_edge, xbinning.upper_edge,
                                      ybinning.lower_edge, ybinning.upper_edge)
        img = axes.imshow(ratios.T, extent=(left, right, bottom, top),
            origin='lower', norm=LogNorm(), interpolation='none', **kwargs)
        axes.set_xlabel(xbinning.name)
        axes.set_ylabel(ybinning.name)
        axes.set_title(classname(self._pdfratio))

        return img
Exemplo n.º 6
0
    def plot(self, src_hypo_group_manager, axes, **kwargs):
        """Plots the PDF object.

        Parameters
        ----------
        src_hypo_group_manager : instance of SourceHypoGroupManager
            The instance of SourceHypoGroupManager that defines the source
            hypotheses.
        axes : mpl.axes.Axes
            The matplotlib Axes object on which the PDF ratio should get drawn
            to.
        fitparams : dict
            The dictionary with the set of fit paramater values.

        Additional Keyword Arguments
        ----------------------------
        Any additional keyword arguments will be passed to the `mpl.imshow`
        function.

        Returns
        -------
        img : instance of mpl.AxesImage
            The AxesImage instance showing the PDF ratio image.
        """
        if (not isinstance(src_hypo_group_manager, SourceHypoGroupManager)):
            raise TypeError('The src_hypo_group_manager argument must be an '
                            'instance of SourceHypoGroupManager!')
        if (not isinstance(axes, Axes)):
            raise TypeError('The axes argument must be an instance of '
                            'matplotlib.axes.Axes!')

        # The I3EnergyPDF object has two axes, one for log10_energy and sin_dec.
        (xbinning, ybinning) = self._pdf.binnings

        pdf_values = np.zeros((xbinning.nbins, ybinning.nbins), dtype=np.float)
        events = DataFieldRecordArray(
            np.zeros((pdf_values.size, ),
                     dtype=[('ix', np.int), (xbinning.name, np.float),
                            ('iy', np.int), (ybinning.name, np.float)]))
        for (i, ((ix, x), (iy, y))) in enumerate(
                itertools.product(enumerate(xbinning.bincenters),
                                  enumerate(ybinning.bincenters))):
            events['ix'][i] = ix
            events[xbinning.name][i] = x
            events['iy'][i] = iy
            events[ybinning.name][i] = y

        self._tdm.initialize_for_new_trial(src_hypo_group_manager, events)

        event_pdf_values = self._pdf.get_prob(self._tdm)
        pdf_values[events['ix'], events['iy']] = event_pdf_values

        (left, right, bottom, top) = (xbinning.lower_edge, xbinning.upper_edge,
                                      ybinning.lower_edge, ybinning.upper_edge)
        img = axes.imshow(pdf_values.T,
                          extent=(left, right, bottom, top),
                          origin='lower',
                          norm=LogNorm(),
                          interpolation='none',
                          **kwargs)
        axes.set_xlabel(xbinning.name)
        axes.set_ylabel(ybinning.name)
        axes.set_title(classname(self._pdf))

        return img
Exemplo n.º 7
0
    def generate_signal_events(self, rss, mean, poisson=True):
        """Generates a given number of signal events from the signal candidate
        monte-carlo events.

        Parameters
        ----------
        rss : instance of RandomStateService
            The instance of RandomStateService providing the random number
            generator state.
        mean : float
            The mean number of signal events. If the ``poisson`` argument is set
            to True, the actual number of generated signal events will be drawn
            from a Poisson distribution with this given mean value of signal
            events.
        poisson : bool
            If set to True, the actual number of generated signal events will
            be drawn from a Poisson distribution with the given mean value of
            signal events.
            If set to False, the argument ``mean`` specifies the actual number
            of generated signal events.

        Returns
        -------
        n_signal : int
            The number of generated signal events.
        signal_events_dict : dict of DataFieldRecordArray
            The dictionary holding the DataFieldRecordArray instancs with the
            generated signal events. Each key of this dictionary represents the
            dataset index for which the signal events have been generated.
        """
        if(poisson):
            mean = rss.random.poisson(float_cast(
                mean, 'The mean argument must be castable to type of float!'))

        n_signal = int_cast(
            mean, 'The mean argument must be castable to type of int!')

        # Draw n_signal signal candidates according to their weight.
        sig_events_meta = rss.random.choice(
            self._sig_candidates,
            size=n_signal,
            p=self._sig_candidates['weight']
        )
        # Get the list of unique dataset and source hypothesis group indices of
        # the drawn signal events.
        # Note: This code does not assume the same format for each of the
        #       individual MC dataset numpy record arrays, thus might be a bit
        #       slower. If one could assume the same MC dataset format, one
        #       could gather all the MC events of all the datasets first and do
        #       the signal event post processing for all datasets at once.
        signal_events_dict = dict()
        ds_idxs = np.unique(sig_events_meta['ds_idx'])
        for ds_idx in ds_idxs:
            mc = self._data_list[ds_idx].mc
            ds_mask = sig_events_meta['ds_idx'] == ds_idx
            n_sig_events_ds = np.count_nonzero(ds_mask)

            data = dict(
                [(fname, np.empty(
                    (n_sig_events_ds,),
                    dtype=mc.get_field_dtype(fname))
                 ) for fname in mc.field_name_list])
            sig_events = DataFieldRecordArray(data, copy=False)

            fill_start_idx = 0
            # Get the list of unique source hypothesis group indices for the
            # current dataset.
            shg_idxs = np.unique(sig_events_meta[ds_mask]['shg_idx'])
            for shg_idx in shg_idxs:
                shg = self._src_hypo_group_manager.src_hypo_group_list[shg_idx]
                shg_mask = sig_events_meta['shg_idx'] == shg_idx
                # Get the MC events for the drawn signal events.
                ds_shg_mask = ds_mask & shg_mask
                shg_sig_events_meta = sig_events_meta[ds_shg_mask]
                n_shg_sig_events = len(shg_sig_events_meta)
                ev_idx = shg_sig_events_meta['ev_idx']
                # Get the signal MC events of the current dataset and source
                # hypothesis group.
                shg_sig_events = mc.get_selection(ev_idx)

                # Do the signal event post sampling processing.
                shg_sig_events = shg.sig_gen_method.signal_event_post_sampling_processing(
                    shg, shg_sig_events_meta, shg_sig_events)

                indices = np.indices((n_shg_sig_events,))[0] + fill_start_idx
                sig_events.set_selection(indices, shg_sig_events)

                #sig_events[fill_start_idx:fill_start_idx+n_shg_sig_events] = shg_sig_events
                fill_start_idx += n_shg_sig_events

            signal_events_dict[ds_idx] = sig_events

        return (n_signal, signal_events_dict)
Exemplo n.º 8
0
    def plot(self,
             src_hypo_group_manager,
             axes,
             source_idx=None,
             sin_dec=True,
             log=True,
             **kwargs):
        """Plots the signal spatial PDF for the specified source.

        Parameters
        ----------
        axes : mpl.axes.Axes
            The matplotlib Axes object on which the PDF ratio should get drawn
            to.
        source_idx : int | None
            The index of the source for which the PDF ratio should get plotted.
            If set to None and the signal PDF depends on the source, index 0
            will be used.
        sin_dec : bool
            Flag if the plot should be made in right-ascention vs. declination
            (False), or in right-ascention vs. sin(declination) (True).

        Additional Keyword Arguments
        ----------------------------
        Any additional keyword arguments will be passed to the `mpl.imshow`
        function.

        Returns
        -------
        img : instance of mpl.AxesImage
            The AxesImage instance showing the PDF ratio image.
        """
        if (not isinstance(src_hypo_group_manager, SourceHypoGroupManager)):
            raise TypeError('The src_hypo_group_manager argument must be an '
                            'instance of SourceHypoGroupManager!')
        if (not isinstance(axes, Axes)):
            raise TypeError('The axes argument must be an instance of '
                            'matplotlib.axes.Axes!')

        if (source_idx is None):
            source_idx = 0

        # Define the binning for ra, dec, and sin_dec.
        delta_ra_deg = 0.5
        delta_dec_deg = 0.5
        delta_sin_dec = 0.01
        # Define the event spatial uncertainty.
        sigma_deg = 0.5

        # Create a grid of signal probabilities in right-ascention and
        # declination/sin(declination) and fill it with probabilities from
        # events that fall into these bins.
        raaxis = self.pdf.axes.get_axis('ra')
        rabins = int(np.ceil(raaxis.length / np.deg2rad(delta_ra_deg)))
        ra_binedges = np.linspace(raaxis.vmin, raaxis.vmax, rabins + 1)
        ra_bincenters = 0.5 * (ra_binedges[:-1] + ra_binedges[1:])

        decaxis = self.pdf.axes.get_axis('dec')
        if (sin_dec is True):
            (dec_min, dec_max) = (np.sin(decaxis.vmin), np.sin(decaxis.vmax))
            decbins = int(np.ceil((dec_max - dec_min) / delta_sin_dec))
        else:
            (dec_min, dec_max) = (decaxis.vmin, decaxis.vmax)
            decbins = int(np.ceil(decaxis.length / np.deg2rad(delta_dec_deg)))
        dec_binedges = np.linspace(dec_min, dec_max, decbins + 1)
        dec_bincenters = 0.5 * (dec_binedges[:-1] + dec_binedges[1:])

        probs = np.zeros((rabins, decbins), dtype=np.float)

        # Generate events that fall into the probability bins.
        events = DataFieldRecordArray(
            np.zeros((probs.size, ),
                     dtype=[('ira', np.int), ('ra', np.float),
                            ('idec', np.int), ('dec', np.float),
                            ('ang_err', np.float)]))
        for (i, ((ira, ra), (idec, dec))) in enumerate(
                itertools.product(enumerate(ra_bincenters),
                                  enumerate(dec_bincenters))):
            events['ira'][i] = ira
            events['ra'][i] = ra
            events['idec'][i] = idec
            if (sin_dec is True):
                events['dec'][i] = np.arcsin(dec)
            else:
                events['dec'][i] = dec
            events['ang_err'][i] = np.deg2rad(sigma_deg)

        self._tdm.initialize_for_new_trial(src_hypo_group_manager, events)

        event_probs = self._pdf.get_prob(self._tdm)

        # Select only the probabilities for the requested source.
        if (event_probs.ndim == 2):
            event_probs = event_probs[source_idx]

        # Fill the probs grid array.
        probs[events['ira'], events['idec']] = event_probs

        (left, right, bottom, top) = (raaxis.vmin, raaxis.vmax, dec_min,
                                      dec_max)
        norm = None
        if (log):
            norm = LogNorm()
        img = axes.imshow(probs.T,
                          extent=(left, right, bottom, top),
                          origin='lower',
                          norm=norm,
                          interpolation='none',
                          **kwargs)
        axes.set_xlabel(raaxis.name)
        if (sin_dec is True):
            axes.set_ylabel('sin(' + decaxis.name + ')')
        else:
            axes.set_ylabel(decaxis.name)
        axes.set_title(classname(self._pdf))

        return img
Exemplo n.º 9
0
    def plot(self,
             src_hypo_group_manager,
             axes,
             source_idx=None,
             log=True,
             **kwargs):
        """Plots the spatial PDF ratio. If the signal PDF depends on the source,
        source_idx specifies the index of the source for which the PDF should
        get plotted.

        Parameters
        ----------
        src_hypo_group_manager : instance of SourceHypoGroupManager
            The instance of SourceHypoGroupManager that defines the source
            hypotheses.
        axes : mpl.axes.Axes
            The matplotlib Axes object on which the PDF ratio should get drawn
            to.
        source_idx : int | None
            The index of the source for which the PDF ratio should get plotted.
            If set to None and the signal PDF depends on the source, index 0
            will be used.

        Additional Keyword Arguments
        ----------------------------
        Any additional keyword arguments will be passed to the `mpl.imshow`
        function.

        Returns
        -------
        img : instance of mpl.AxesImage
            The AxesImage instance showing the PDF ratio image.
        """
        if (not isinstance(axes, Axes)):
            raise TypeError(
                'The axes argument must be an instance of matplotlib.axes.Axes!'
            )

        if (source_idx is None):
            source_idx = 0

        # Define the binning for ra, dec, and sin_dec.
        delta_ra_deg = 0.5
        delta_dec_deg = 0.5

        raaxis = self._pdfratio.signalpdf.axes.get_axis('ra')
        decaxis = self._pdfratio.signalpdf.axes.get_axis('dec')

        # Create a grid of ratio in right-ascention and declination and fill it
        # with PDF ratio values from events that fall into these bins.
        # Use a binning for 1/2 degree.
        rabins = int(np.ceil(raaxis.length / np.deg2rad(delta_ra_deg)))
        decbins = int(np.ceil(decaxis.length / np.deg2rad(delta_dec_deg)))

        ratios = np.zeros((rabins, decbins), dtype=np.float)

        ra_binedges = np.linspace(raaxis.vmin, raaxis.vmax, rabins + 1)
        ra_bincenters = 0.5 * (ra_binedges[:-1] + ra_binedges[1:])

        dec_binedges = np.linspace(decaxis.vmin, decaxis.vmax, decbins + 1)
        dec_bincenters = 0.5 * (dec_binedges[:-1] + dec_binedges[1:])

        # Generate events that fall into the ratio bins.
        events = DataFieldRecordArray(
            np.zeros((ratios.size, ),
                     dtype=[('ira', np.int), ('ra', np.float),
                            ('idec', np.int), ('dec', np.float),
                            ('sin_dec', np.float), ('ang_err', np.float)]))
        for (i, ((ira, ra), (idec, dec))) in enumerate(
                itertools.product(enumerate(ra_bincenters),
                                  enumerate(dec_bincenters))):
            events['ira'][i] = ira
            events['ra'][i] = ra
            events['idec'][i] = idec
            events['dec'][i] = dec
            events['sin_dec'][i] = np.sin(dec)
            events['ang_err'][i] = np.deg2rad(0.5)

        self._tdm.initialize_for_new_trial(src_hypo_group_manager, events)

        event_ratios = self._pdfratio.get_ratio(self._tdm)

        # Select only the ratios for the requested source.
        if (event_ratios.ndim == 2):
            event_ratios = event_ratios[source_idx]

        ratios[events['ira'], events['idec']] = event_ratios

        (left, right, bottom, top) = (raaxis.vmin, raaxis.vmax, decaxis.vmin,
                                      decaxis.vmax)
        norm = LogNorm() if log else None
        img = axes.imshow(ratios.T,
                          extent=(left, right, bottom, top),
                          origin='lower',
                          norm=norm,
                          interpolation='none',
                          **kwargs)
        axes.set_xlabel(raaxis.name)
        axes.set_ylabel(decaxis.name)
        axes.set_title(classname(self._pdfratio))

        return img