<commit_before># Licensed under a 3-clause BSD style license - see LICENSE.rst
"""
This module provides classes to store the results of isophote fits.
"""

from astropy.table import QTable
import astropy.units as u
import numpy as np

from .harmonics import (first_and_second_harmonic_function,
                        fit_first_and_second_harmonics, fit_upper_harmonic)
from ..utils._misc import _get_version_info

__all__ = ['Isophote', 'IsophoteList']


class Isophote:
    """
    Container class to store the results of single isophote fit.

    The extracted data sample at the given isophote (sampled intensities
    along the elliptical path on the image) is also kept as an attribute
    of this class. The container concept helps in segregating
    information directly related to the sample, from information that
    more closely relates to the fitting process, such as status codes,
    errors for isophote parameters, and the like.

    Parameters
    ----------
    sample : `~photutils.isophote.EllipseSample` instance
        The sample information.
    niter : int
        The number of iterations used to fit the isophote.
    valid : bool
        The status of the fitting operation.
    stop_code : int
        The fitting stop code:

           *  0: Normal.
           *  1: Fewer than the pre-specified fraction of the extracted
              data points are valid.
           *  2: Exceeded maximum number of iterations.
           *  3: Singular matrix in harmonic fit, results may not be
              valid.  This also signals an insufficient number of
              data points to fit.
           *  4: Small or wrong gradient, or ellipse diverged. Subsequent
              ellipses at larger or smaller semimajor axis may have
              the same constant geometric parameters. It's also used
              when the user turns off the fitting algorithm via the
              ``maxrit`` fitting parameter (see the
              `~photutils.isophote.Ellipse` class).
           *  5: Ellipse diverged; not even the minimum number of
              iterations could be executed. Subsequent ellipses at
              larger or smaller semimajor axis may have the same
              constant geometric parameters.
           * -1: Internal use.

    Attributes
    ----------
    rms : float
        The root-mean-square of intensity values along the elliptical
        path.
    int_err : float
        The error of the mean (rms / sqrt(# data points)).
    ellip_err : float
        The ellipticity error.
    pa_err : float
        The position angle error (radians).
    x0_err : float
        The error associated with the center x coordinate.
    y0_err : float
        The error associated with the center y coordinate.
    pix_stddev : float
        The estimate of pixel standard deviation (rms * sqrt(average
        sector integration area)).
    grad : float
        The local radial intensity gradient.
    grad_error : float
        The measurement error of the local radial intensity gradient.
    grad_r_error : float
        The relative error of local radial intensity gradient.
    tflux_e : float
        The sum of all pixels inside the ellipse.
    npix_e : int
        The total number of valid pixels inside the ellipse.
    tflux_c : float
        The sum of all pixels inside a circle with the same ``sma`` as
        the ellipse.
    npix_c : int
        The total number of valid pixels inside a circle with the same
        ``sma`` as the ellipse.
    sarea : float
        The average sector area on the isophote (pixel**2).
    ndata : int
        The number of extracted data points.
    nflag : int
        The number of discarded data points. Data points can be
        discarded either because they are physically outside the image
        frame boundaries, because they were rejected by sigma-clipping,
        or they are masked.
    a3, b3, a4, b4 : float
        The higher order harmonics that measure the deviations from a
        perfect ellipse.  These values are actually the raw harmonic
        amplitudes divided by the local radial gradient and the
        semimajor axis length, so they can directly be compared with
        each other.
    a3_err, b3_err, a4_err, b4_err : float
        The errors associated with the ``a3``, ``b3``, ``a4``, and
        ``b4`` attributes.
    """

    def __init__(self, sample, niter, valid, stop_code):
        self.sample = sample
        self.niter = niter
        self.valid = valid
        self.stop_code = stop_code

        self.intens = sample.mean
        self.rms = np.std(sample.values[2])
        self.int_err = self.rms / np.sqrt(sample.actual_points)
        self.pix_stddev = self.rms * np.sqrt(sample.sector_area)
        self.grad = sample.gradient
        self.grad_error = sample.gradient_error

        self.grad_r_error = sample.gradient_relative_error
        self.sarea = sample.sector_area
        self.ndata = sample.actual_points
        self.nflag = sample.total_points - sample.actual_points

        # flux contained inside ellipse and circle
        (self.tflux_e, self.tflux_c, self.npix_e,
         self.npix_c) = self._compute_fluxes()

        self._compute_errors()

        # deviations from a perfect ellipse
        (self.a3, self.b3, self.a3_err,
         self.b3_err) = self._compute_deviations(sample, 3)
        (self.a4, self.b4, self.a4_err,
         self.b4_err) = self._compute_deviations(sample, 4)

    # This method is useful for sorting lists of instances. Note
    # that __lt__ is the python3 way of supporting sorting.
    def __lt__(self, other):
        if hasattr(other, 'sma'):
            return self.sma < other.sma
        raise ValueError('Comparison object does not have a "sma" attribute.')

    def __str__(self):
        return str(self.to_table())

    @property
    def sma(self):<commit_msg>Add missing Isophote comparison operators<commit_after># Licensed under a 3-clause BSD style license - see LICENSE.rst
"""
This module provides classes to store the results of isophote fits.
"""

from astropy.table import QTable
import astropy.units as u
import numpy as np

from .harmonics import (first_and_second_harmonic_function,
                        fit_first_and_second_harmonics, fit_upper_harmonic)
from ..utils._misc import _get_version_info

__all__ = ['Isophote', 'IsophoteList']


class Isophote:
    """
    Container class to store the results of single isophote fit.

    The extracted data sample at the given isophote (sampled intensities
    along the elliptical path on the image) is also kept as an attribute
    of this class. The container concept helps in segregating
    information directly related to the sample, from information that
    more closely relates to the fitting process, such as status codes,
    errors for isophote parameters, and the like.

    Parameters
    ----------
    sample : `~photutils.isophote.EllipseSample` instance
        The sample information.
    niter : int
        The number of iterations used to fit the isophote.
    valid : bool
        The status of the fitting operation.
    stop_code : int
        The fitting stop code:

           *  0: Normal.
           *  1: Fewer than the pre-specified fraction of the extracted
              data points are valid.
           *  2: Exceeded maximum number of iterations.
           *  3: Singular matrix in harmonic fit, results may not be
              valid.  This also signals an insufficient number of
              data points to fit.
           *  4: Small or wrong gradient, or ellipse diverged. Subsequent
              ellipses at larger or smaller semimajor axis may have
              the same constant geometric parameters. It's also used
              when the user turns off the fitting algorithm via the
              ``maxrit`` fitting parameter (see the
              `~photutils.isophote.Ellipse` class).
           *  5: Ellipse diverged; not even the minimum number of
              iterations could be executed. Subsequent ellipses at
              larger or smaller semimajor axis may have the same
              constant geometric parameters.
           * -1: Internal use.

    Attributes
    ----------
    rms : float
        The root-mean-square of intensity values along the elliptical
        path.
    int_err : float
        The error of the mean (rms / sqrt(# data points)).
    ellip_err : float
        The ellipticity error.
    pa_err : float
        The position angle error (radians).
    x0_err : float
        The error associated with the center x coordinate.
    y0_err : float
        The error associated with the center y coordinate.
    pix_stddev : float
        The estimate of pixel standard deviation (rms * sqrt(average
        sector integration area)).
    grad : float
        The local radial intensity gradient.
    grad_error : float
        The measurement error of the local radial intensity gradient.
    grad_r_error : float
        The relative error of local radial intensity gradient.
    tflux_e : float
        The sum of all pixels inside the ellipse.
    npix_e : int
        The total number of valid pixels inside the ellipse.
    tflux_c : float
        The sum of all pixels inside a circle with the same ``sma`` as
        the ellipse.
    npix_c : int
        The total number of valid pixels inside a circle with the same
        ``sma`` as the ellipse.
    sarea : float
        The average sector area on the isophote (pixel**2).
    ndata : int
        The number of extracted data points.
    nflag : int
        The number of discarded data points. Data points can be
        discarded either because they are physically outside the image
        frame boundaries, because they were rejected by sigma-clipping,
        or they are masked.
    a3, b3, a4, b4 : float
        The higher order harmonics that measure the deviations from a
        perfect ellipse.  These values are actually the raw harmonic
        amplitudes divided by the local radial gradient and the
        semimajor axis length, so they can directly be compared with
        each other.
    a3_err, b3_err, a4_err, b4_err : float
        The errors associated with the ``a3``, ``b3``, ``a4``, and
        ``b4`` attributes.
    """

    def __init__(self, sample, niter, valid, stop_code):
        self.sample = sample
        self.niter = niter
        self.valid = valid
        self.stop_code = stop_code

        self.intens = sample.mean
        self.rms = np.std(sample.values[2])
        self.int_err = self.rms / np.sqrt(sample.actual_points)
        self.pix_stddev = self.rms * np.sqrt(sample.sector_area)
        self.grad = sample.gradient
        self.grad_error = sample.gradient_error

        self.grad_r_error = sample.gradient_relative_error
        self.sarea = sample.sector_area
        self.ndata = sample.actual_points
        self.nflag = sample.total_points - sample.actual_points

        # flux contained inside ellipse and circle
        (self.tflux_e, self.tflux_c, self.npix_e,
         self.npix_c) = self._compute_fluxes()

        self._compute_errors()

        # deviations from a perfect ellipse
        (self.a3, self.b3, self.a3_err,
         self.b3_err) = self._compute_deviations(sample, 3)
        (self.a4, self.b4, self.a4_err,
         self.b4_err) = self._compute_deviations(sample, 4)

    # This method is useful for sorting lists of instances. Note
    # that __lt__ is the python3 way of supporting sorting.
    def __lt__(self, other):
        try:
            return self.sma < other.sma
        except AttributeError as err:
            raise AttributeError('Comparison object does not have a "sma" '
                                 'attribute.') from err

    def __gt__(self, other):
        try:
            return self.sma > other.sma
        except AttributeError as err:
            raise AttributeError('Comparison object does not have a "sma" '
                                 'attribute.') from err

    def __le__(self, other):
        try:
            return self.sma <= other.sma
        except AttributeError as err:
            raise AttributeError('Comparison object does not have a "sma" '
                                 'attribute.') from err

    def __ge__(self, other):
        try:
            return self.sma >= other.sma
        except AttributeError as err:
            raise AttributeError('Comparison object does not have a "sma" '
                                 'attribute.') from err

    def __eq__(self, other):
        try:
            return self.sma == other.sma
        except AttributeError as err:
            raise AttributeError('Comparison object does not have a "sma" '
                                 'attribute.') from err

    def __ne__(self, other):
        try:
            return self.sma != other.sma
        except AttributeError as err:
            raise AttributeError('Comparison object does not have a "sma" '
                                 'attribute.') from err

    def __str__(self):
        return str(self.to_table())

    @property
    def sma(self):
{'fraction_non_alphanumeric': 0.062399877946449, 'fraction_numerical': 0.005339842856053093, 'mean_word_length': 2.148414985590778, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4203375', 'n_tokens_mistral': 3594, 'n_tokens_neox': 3419, 'n_words': 1397}