<commit_before>"""Provides method for efficiently histogramming properties of events in a
region.
"""


# System imports
from uuid import uuid4
from functools import wraps
import gc

# Six imports
from six import string_types

# NumPy imports
import numpy

# ROOT imports
from ROOT import TH1F, TH2F, TH3F, nullptr

# owls-cache imports
from owls_cache.transient import cached as transiently_cached
from owls_cache.persistent import cached as persistently_cached

# owls-parallel imports
from owls_parallel import parallelized

# owls-hep imports
from owls_hep.expression import properties, normalized
from owls_hep.calculation import Calculation


# Set up default exports
__all__ = [
    'Binning',
    'Histogram',
]


class Binning(object):
    """Represents a binning to be used on an axis in a histogram.
    """

    def __init__(self, type, *args):
        """Initializes a new instance of the Binning class.

        Args:
            type: The binning type, either 'fixed' or 'variable'
            *args: The remaining arguments depend on the binning type.  For
                'fixed'-width bins, the remaining arguments should be of the
                form:

                    low_bin_left_edge, high_bin_right_edge, n_bins

                For 'variable'-width bins, the remaining arguments should be of
                the form:

                    low_bin_left_edge,
                    second_bin_left_edge,
                    ...,
                    high_bin_right_edge
        """
        # Validate and store type
        if type not in ('fixed', 'variable'):
            raise ValueError('invalid binning type')
        self._type = type

        # Validate and store edge specifications
        self._low_bin_left_edge = None
        self._high_bin_right_edge = None
        self._n_bins = None
        self._edges = None
        if self._type == 'fixed':
            # Parse
            low_bin_left_edge, high_bin_right_edge, n_bins = args

            # Validate
            if n_bins < 1:
                raise ValueError('must have at least one bin')
            if low_bin_left_edge >= high_bin_right_edge:
                raise ValueError('lower edge must be higher than upper edge')

            # Store
            self._low_bin_left_edge = low_bin_left_edge
            self._high_bin_right_edge = high_bin_right_edge
            self._n_bins = n_bins
        elif self._type == 'variable':
            # Parse
            edges = args

            # Validate
            if len(edges) < 2:
                raise ValueError('must have at least two edges')
            if not all(x < y for x, y in zip(edges, edges[1:])):
                raise ValueError('edges must be strictly increasing')

            # Store
            self._edges = edges

    def __hash__(self):
        """Returns a unique hash for the binning.

        The hash is based on the specifications provided to the constructor, so
        equivalent binnings may have different hashes.
        """
        # HACK: hash(None) is not consistent across Python processes because
        # None is a singleton and its address varies between runs.  I wish I
        # were making this up.  Anyway, we would normally just do a dumb tuple
        # including self._* parameters which are None, but I guess we can't.
        if self._type == 'fixed':
            return hash((self._type,
                         self._low_bin_left_edge,
                         self._high_bin_right_edge,
                         self._n_bins))
        elif self._type == 'variable':
            return hash((self._type, self._edges))

    def edges(self):
        """Returns an explicit NumPy array of doubles representing bin edges.
        """
        if self._type == 'fixed':
            # Expand to a full edge list, adding 1 to the bin count so that at
            # least 2 edges are generated.  Unfortunately there is no way to
            # specify a dtype to linspace, but the implementation is hard-coded
            # to return a float, so we'll go with it.
            return numpy.linspace(self._low_bin_left_edge,
                                  self._high_bin_right_edge,
                                  self._n_bins + 1)
        elif self._type == 'variable':
            # Use existing edges, but ensure they are type as float
            return numpy.array(self._edges, dtype = numpy.float)


# Dummy function to return fake values when parallelizing
def _parallel_mocker(process, region, expressions, binnings):
    # Extract binnings
    edges = tuple((b.edges() for b in binnings))

    # Create a unique name and title for the histogram
    name = title = uuid4().hex
<commit_msg>Small dtype fix.<commit_after>"""Provides method for efficiently histogramming properties of events in a
region.
"""


# System imports
from uuid import uuid4
from functools import wraps
import gc

# Six imports
from six import string_types

# NumPy imports
import numpy

# ROOT imports
from ROOT import TH1F, TH2F, TH3F, nullptr

# owls-cache imports
from owls_cache.transient import cached as transiently_cached
from owls_cache.persistent import cached as persistently_cached

# owls-parallel imports
from owls_parallel import parallelized

# owls-hep imports
from owls_hep.expression import properties, normalized
from owls_hep.calculation import Calculation


# Set up default exports
__all__ = [
    'Binning',
    'Histogram',
]


class Binning(object):
    """Represents a binning to be used on an axis in a histogram.
    """

    def __init__(self, type, *args):
        """Initializes a new instance of the Binning class.

        Args:
            type: The binning type, either 'fixed' or 'variable'
            *args: The remaining arguments depend on the binning type.  For
                'fixed'-width bins, the remaining arguments should be of the
                form:

                    low_bin_left_edge, high_bin_right_edge, n_bins

                For 'variable'-width bins, the remaining arguments should be of
                the form:

                    low_bin_left_edge,
                    second_bin_left_edge,
                    ...,
                    high_bin_right_edge
        """
        # Validate and store type
        if type not in ('fixed', 'variable'):
            raise ValueError('invalid binning type')
        self._type = type

        # Validate and store edge specifications
        self._low_bin_left_edge = None
        self._high_bin_right_edge = None
        self._n_bins = None
        self._edges = None
        if self._type == 'fixed':
            # Parse
            low_bin_left_edge, high_bin_right_edge, n_bins = args

            # Validate
            if n_bins < 1:
                raise ValueError('must have at least one bin')
            if low_bin_left_edge >= high_bin_right_edge:
                raise ValueError('lower edge must be higher than upper edge')

            # Store
            self._low_bin_left_edge = low_bin_left_edge
            self._high_bin_right_edge = high_bin_right_edge
            self._n_bins = n_bins
        elif self._type == 'variable':
            # Parse
            edges = args

            # Validate
            if len(edges) < 2:
                raise ValueError('must have at least two edges')
            if not all(x < y for x, y in zip(edges, edges[1:])):
                raise ValueError('edges must be strictly increasing')

            # Store
            self._edges = edges

    def __hash__(self):
        """Returns a unique hash for the binning.

        The hash is based on the specifications provided to the constructor, so
        equivalent binnings may have different hashes.
        """
        # HACK: hash(None) is not consistent across Python processes because
        # None is a singleton and its address varies between runs.  I wish I
        # were making this up.  Anyway, we would normally just do a dumb tuple
        # including self._* parameters which are None, but I guess we can't.
        if self._type == 'fixed':
            return hash((self._type,
                         self._low_bin_left_edge,
                         self._high_bin_right_edge,
                         self._n_bins))
        elif self._type == 'variable':
            return hash((self._type, self._edges))

    def edges(self):
        """Returns an explicit NumPy array of doubles representing bin edges.
        """
        if self._type == 'fixed':
            # Expand to a full edge list, adding 1 to the bin count so that at
            # least 2 edges are generated.  Unfortunately there is no way to
            # specify a dtype to linspace, but the implementation is hard-coded
            # to return a float, so we'll go with it.
            return numpy.linspace(self._low_bin_left_edge,
                                  self._high_bin_right_edge,
                                  self._n_bins + 1)
        elif self._type == 'variable':
            # Use existing edges, but ensure they are type as float
            return numpy.array(self._edges, dtype = numpy.float64)


# Dummy function to return fake values when parallelizing
def _parallel_mocker(process, region, expressions, binnings):
    # Extract binnings
    edges = tuple((b.edges() for b in binnings))

    # Create a unique name and title for the histogram
    name = title = uuid4().hex

{'fraction_non_alphanumeric': 0.06594226057313306, 'fraction_numerical': 0.002556727388942154, 'mean_word_length': 1.9765377298668358, 'pattern_counts': {'":': 0, '<': 9, '<?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': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6507377', 'n_tokens_mistral': 2527, 'n_tokens_neox': 2391, 'n_words': 916}