Source code for galaximview.classeshists

from .basefuncs import *


[docs]class HistOfX: """Class for one-dimensional histograms with nbins+1 linear bins edges in funcofx of array xarr computed in initialization, except if edges are given. Parameters ---------- xarr : ndarray (N,) array to bin. xrange : (float, float) or (`None`, `None`) Range on which to bin xarr. If (`None`, `None`), chosen by function xrange_from_xarr. Not used if edges is not `None`. nbins : int, default = 128 Number of bins. Not used if edges is not `None`. funcofx : weights: ndarray, optionaldefault = :func:`~galaximview.basefuncs.identity_function` Function such that funcofx(xarr) is binned. edges : ndarray or `None`, default = `None` Edges of histogram. Used if given. If `None`, edges set with xrange and nbins by function :func:`~galaximview.classeshists.linearly_spaced_edges_in_function_from_bin_size`, so that they are linearly spaced in funcofx. fill_range : bool, default = ` False` If True and edges is not `None`, bins are added to cover full range of xarr. Used to keep a constant bin width. """ def __init__(self, xarr, xrange, nbins=128, funcofx=identity_function, edges=None, fill_range=False): CheckArrays.check_masslike_array(xarr) self.funcofx = funcofx self.xarr = xarr if (edges is not None): if fill_range: self.edges = fill_range_around_edges(xarr, funcofx, edges) else: self.edges = edges else: xrange = xrange_from_xarr(xarr, xrange, funcofx) self.edges = linearly_spaced_edges_in_function_from_nb(xrange, funcofx, nbins)
[docs] def get_midbins(self): """Returns middle-points of (nbins+1) edges computed in self.__init__. Returns ------- ndarray (nbins,) middle points. """ return 0.5 * (self.edges[1:] + self.edges[:-1])
[docs] def get_bins_widths(self): """Returns width of bins of (nbins+1) edges computed in self.__init__. Returns ------- ndarray (nbins,) widths. """ return self.edges[1:] - self.edges[:-1]
[docs] def hist_of_f_of_x(self, weights=None): """Returns middle-points of nbins bins of (nbins+1,) edges computed in self.__init__ and number of array members of funcofx(xarr) in bins if weights is `None`, or else sum of weights of array members of funcofx(xarr) in bins. Parameters ---------- weights : ndarray, optional (N,) weights. Returns ------- (ndarray, ndarray) (nbins,) bins middle-points and histogram of funcofx(xarr). """ if (weights is None): weights = np.ones_like(self.xarr) else: CheckArrays.check_masslike_array(weights) if np.shape(self.xarr)[0] != np.shape(weights)[0]: raise ValueError("Should be given arrays of same shape (N,).") hm, ed = np.histogram(self.funcofx(self.xarr), self.edges, weights=weights) x = self.get_midbins() return x, hm
[docs] def binned_y_of_f_of_x(self, tobin, weights=None): """Returns middle-points of nbins bins of (nbins+1,) edges computed in self.__init__ and average of tobin array in bins, weighted if weights is not `None`. Parameters ---------- tobin : ndarray (N,) array to bin. weights : ndarray, optional (N,) weights. Returns ------- (ndarray, ndarray) (nbins,) bins middle-points and mass-weighted average of tobin array in bins. """ CheckArrays.check_masslike_array(tobin) if (weights is None): weights = np.ones_like(tobin) CheckArrays.check_masslike_array(weights) if np.shape(tobin)[0] != np.shape(weights)[0]: raise ValueError("Should be given arrays of same shape (N,).") rad = self.funcofx(self.xarr) h, ed = np.histogram(rad, self.edges, weights=tobin * weights) x, hm = self.hist_of_f_of_x(weights=weights) inds0 = hm == 0 h[~inds0] /= hm[~inds0] h[inds0] = np.nan return x, h
[docs] def binned_dispersion_of_y_of_f_of_x(self, tobin, weights=None): """Returns middle-points of bins and dispersion of tobin array in bins, weighted if weights is not `None`. Parameters ---------- tobin : ndarray (N,) array to bin. weights : ndarray, optional (N,) weights. Returns ------- (ndarray, ndarray) (nbins,) bins middle-points and weighted dispersion of tobin array in bins. """ CheckArrays.check_masslike_array(tobin) if (weights is None): weights = np.ones_like(tobin) CheckArrays.check_masslike_array(weights) if np.shape(tobin)[0] != np.shape(weights)[0]: raise ValueError("Should be given arrays of same shape (N,).") x, hofavsquares = self.binned_y_of_f_of_x(tobin ** 2, weights) x, hav = self.binned_y_of_f_of_x(tobin, weights) h = hofavsquares - hav ** 2 inds0 = h <= 0 h[~inds0] = np.sqrt(h[~inds0]) h[inds0] = np.nan return x, h
[docs] def volumes_of_bin(self, volstr): """Attributes a "volume" to bins from their edges: 'width' for the normal width, (left edge) - (right edge) 'annulus_surface' for pi * (right edge) ** 2 - (left edge) ** 2 'spherical_shell' for 4/3 * pi * (right edge) ** 3 - (left edge) ** 3 Parameters ---------- volstr : {'width', 'annulus_surface', 'spherical_shell'} Type of cell volume. Returns ------- ndarray (nbins,) array of cells 'volume'. """ edges = inverse_function(self.funcofx, self.edges) if (volstr == 'width'): voldr = edges[1:] - edges[:-1] elif (volstr == 'annulus_surface'): voldr = edges[1:] ** 2 - edges[:-1] ** 2 voldr *= np.pi elif (volstr == 'spherical_shell'): voldr = edges[1:] ** 3 - edges[:-1] ** 3 voldr *= 4. / 3. * np.pi else: ValueError("Unknown volstr.") voldr = 0 return voldr
[docs] def volumic_hist_of_f_of_x(self, weights=None, volstr='width'): """Computes a histogram (weighted if weights is not `None`) of func(xarr) divided by the "volume" of bins. Parameters ---------- weights : ndarray, optional (N,) weights. volstr : {'width', 'annulus_surface', 'spherical_shell'} Type of bin volume. See `~galaximview.classeshists.HistOfX.volumes_of_bin`. Returns ------- (ndarray, ndarray) (nbins,) bins middle-points and histogram of funcofx(xarr) divided by cells 'volume'. """ if (weights is None): weights = np.ones_like(self.xarr) else: CheckArrays.check_masslike_array(weights) if np.shape(self.xarr)[0] != np.shape(weights)[0]: raise ValueError("Should be given arrays of same shape (N,).") voldr = self.volumes_of_bin(volstr) rad = self.funcofx(self.xarr) h, ed = np.histogram(rad, self.edges, weights=weights) h /= voldr return self.get_midbins(), h
[docs]class Hist2DOfXY: """Class for one-dimensional histograms with nbins+1 linear bins edges in funcofx of array xarr computed in initialization, except if edges are given. Parameters ---------- xcoords : ndarray (N,) array to bin. ycoords : ndarray (M,) array to bin. xrange : (float, float) or (None, None) Range on which to bin xcoords. If (None, None), chosen by function xrange_from_xarr. Not used if xedges and yedges are not None. yrange : (float, float) or (None, None) Range on which to bin ycoords. If (None, None), chosen by function xrange_from_xarr. Not used if xedges and yedges are not None. xedges : ndarray or None, optional Edges of histogram. Used if given. If None, xedges set with xrange and nbx by function func:`~galaximview.classeshists.linearly_spaced_edges_in_function_from_bin_size`, so that they are linearly spaced in funcofx. yedges : ndarray or None, optional Edges of histogram. Used if given. If None, yedges set with yrange and nby by function func:`~galaximview.classeshists.linearly_spaced_edges_in_function_from_bin_size`, so that they are linearly spaced in funcofy. """ def __init__(self, xcoords, ycoords, xrange, yrange, nbx=128, nby=128, funcofx=identity_function, funcofy=identity_function, yedges=None, xedges=None): CheckArrays.check_masslike_array(xcoords) CheckArrays.check_masslike_array(ycoords) self.xcoords = xcoords self.ycoords = ycoords self.funcofx = funcofx self.funcofy = funcofy if ((xedges is not None) & (yedges is not None)): self.xedges = xedges self.yedges = yedges else: xrange = xrange_from_xarr(xcoords, xrange, funcofx) yrange = xrange_from_xarr(ycoords, yrange, funcofy) if (nby is None): nby = int((yrange[1] - yrange[0]) / (xrange[1] - xrange[0]) * nbx) self.xedges = linearly_spaced_edges_in_function_from_nb(xrange, funcofx, nbx) self.yedges = linearly_spaced_edges_in_function_from_nb(yrange, funcofy, nby)
[docs] def get_x_midbins(self): """Returns middle-points of (nbins+1) x edges computed in self.__init__. Returns ------- ndarray (nbins,) middle points.""" return 0.5 * (self.xedges[1:] + self.xedges[:-1])
[docs] def get_y_midbins(self): """Returns middle-points of (nbins+1) y edges computed in self.__init__. Returns ------- ndarray (nbins,) middle points.""" return 0.5 * (self.yedges[1:] + self.yedges[:-1])
[docs] def get_x_binswidth(self): """Returns width of bins of (nbx+1) edges computed in self.__init__. Returns ------- ndarray (nbx,) widths. """ return self.xedges[1:] - self.xedges[:-1]
[docs] def get_y_binswidth(self): """Returns width of bins of (nby+1) edges computed in self.__init__. Returns ------- ndarray (nby,) widths. """ return self.yedges[1:] - self.yedges[:-1]
[docs] def get_xy_grid(self): """Returns meshgrid of bins middle-points. Returns ------- ndarray (nbx, nby) """ midx = self.get_x_midbins() midy = self.get_y_midbins() return np.meshgrid(midx, midy)
[docs] def get_x_grid(self): """Returns x part of meshgrid of bins middle-points.""" return self.get_xy_grid()[0]
[docs] def get_y_grid(self): """Returns y part of meshgrid of bins middle-points.""" return self.get_xy_grid()[1]
[docs] def hist2d(self, weights=None): """Returns histogram and middle-points of nby bins of (nby+1,) yedges and of nbx bins of (nbx+1,) xedges, and number of points of funcofx(xcoords) and funcofy(ycoords) in 2D bins if weights is None, or else sum of weights of points. Parameters ---------- weights: ndarray, optional (N,) weights. Returns ------- (ndarray, ndarray, ndarray) (nby, nbx) histogram, (nby,) y bins middle-points and (nbx,) x bins middle-points. """ if (weights is None): weights = np.ones_like(self.xcoords) else: CheckArrays.check_masslike_array(weights) if np.shape(self.xcoords)[0] != np.shape(weights)[0]: raise ValueError("Should be given arrays of same shape (N,).") xcoords = self.funcofx(self.xcoords) ycoords = self.funcofy(self.ycoords) hm2d, edy, edx = np.histogram2d(ycoords, xcoords, [self.yedges, self.xedges], weights=weights) return hm2d, edy, edx
[docs] def binned_in_2d_array(self, tobin, weights=None): """Returns average of tobin array in 2d bins, weighted if weights is not None, middle-points of y bins and of x bins. Parameters ---------- tobin : ndarray (N,) array to bin. weights : ndarray, optional (N,) weights. Returns ------- (ndarray, ndarray, ndarray) (nby, nbx) histogram, (nby,) y bins middle-points and (nbx,) x bins middle-points. """ CheckArrays.check_masslike_array(tobin) if (weights is None): weights = np.ones_like(tobin) CheckArrays.check_masslike_array(weights) if np.shape(tobin)[0] != np.shape(weights)[0]: raise ValueError("Should be given arrays of same shape (N,).") xcoords = self.funcofx(self.xcoords) ycoords = self.funcofy(self.ycoords) h2d, edy, edx = np.histogram2d(ycoords, xcoords, [self.yedges, self.xedges], weights=tobin * weights) hm2d, edy, edx = self.hist2d(weights=weights) indsnonzero = (hm2d != 0) h2d[indsnonzero] /= hm2d[indsnonzero] h2d[~indsnonzero] = np.nan return h2d, edy, edx
[docs] def binned_in_2d_dispersion_of_array(self, tobin, weights=None): """Returns dispersion of tobin array in 2d bins, weighted if weights is not None, y edges and x edges. Parameters ---------- tobin : ndarray (N,) array to bin. weights : ndarray, optional (N,) weights. Returns ------- (ndarray, ndarray, ndarray) (nby, nbx) histogram, (nby,) y bins middle-points and (nbx,) x bins middle-points. """ CheckArrays.check_masslike_array(tobin) if (weights is None): weights = np.ones_like(tobin) CheckArrays.check_masslike_array(weights) if np.shape(tobin)[0] != np.shape(weights)[0]: raise ValueError("Should be given arrays of same shape (N,).") hofavsquares, edy, edx = self.binned_in_2d_array(tobin ** 2, weights=weights) hav, edy, edx = self.binned_in_2d_array(tobin, weights=weights) h = hofavsquares - hav ** 2 h = np.sqrt(h) return h, edy, edx
[docs] def volumes_of_bins(self, volstrx, volstry): """Attributes a "volume" to 2D bins from their edges: 'width' for the normal width, (left edge) - (right edge) of x or y dimension 'annulus_surface' for pi * (right edge) ** 2 - (left edge) ** 2 of x or y dimension 'spherical_shell' for 4/3 * pi * (right edge) ** 3 - (left edge) ** 3 of x or y dimension Parameters ---------- volstrx : {'width', 'annulus_surface', 'spherical_shell'} Type of cell volume. volstry : {'width', 'annulus_surface', 'spherical_shell'} Type of cell volume. Returns ------- ndarray (nby,nbx) array of bins 'volume'. """ xedges = inverse_function(self.funcofx, self.xedges) yedges = inverse_function(self.funcofy, self.yedges) if (volstrx == 'width'): xl = xedges[1:] - xedges[:-1] elif (volstrx == 'annulus_surface'): xl = np.pi * (xedges[1:] ** 2 - xedges[:-1] ** 2) elif (volstrx == 'spherical_shell'): xl = 4. / 3. * np.pi * (xedges[1:] ** 3 - xedges[:-1] ** 3) else: print("unknown volstrx") xl = 0 if (volstry == 'width'): yl = yedges[1:] - yedges[:-1] elif (volstry == 'annulus_surface'): yl = np.pi * (yedges[1:] ** 2 - yedges[:-1] ** 2) elif (volstry == 'spherical_shell'): yl = 4. / 3. * np.pi * (yedges[1:] ** 3 - yedges[:-1] ** 3) else: print("unknown volstry") yl = 0 surf = yl.reshape((-1, 1)) * xl return surf
[docs] def volumic_hist2d(self, weights=None, volstrx='width', volstry='width'): """Computes a histogram (weighted if weights is not None) divided by the "volume" of bins. Parameters ---------- weights : ndarray, optional (N,) weights. volstrx : {'width', 'annulus_surface', 'spherical_shell'} Type of bin volume. See `~galaximview.classeshists.Hist2DOfXY.volumes_of_bin`. volstry : {'width', 'annulus_surface', 'spherical_shell'} Type of bin volume. See `~galaximview.classeshists.Hist2DOfXY.volumes_of_bin`. Returns ------- (ndarray, ndarray, ndarray) (nby, nbx) histogram divided by cells 'volumes', (nby,) y bins middle-points and (nbx,) x bins middle-points. """ if (weights is None): weights = np.ones_like(self.xcoords) else: CheckArrays.check_masslike_array(weights) if np.shape(self.xcoords)[0] != np.shape(weights)[0]: raise ValueError("Should be given arrays of same shape (N,).") surf = self.volumes_of_bins(volstrx, volstry) hm2d, edy, edx = self.hist2d(weights=weights) hm2d /= surf return hm2d, edy, edx
[docs]class CicHistOfX(HistOfX): """Class for one-dimensional cloud-in-cell histograms with nb+1 linear bins edges from rmin to rmax.""" def __init__(self, rad, xrange, nbins=128): HistOfX.__init__(self, rad, xrange, nbins=nbins, edges=None) self.lcell = self.edges[1] - self.edges[0] self.xg = self.xarr - 0.5 * self.lcell self.xd = self.xarr + 0.5 * self.lcell self.lg = np.floor(self.xg / self.lcell) + 1 - self.xg / self.lcell self.ld = self.xd / self.lcell - np.floor(self.xd / self.lcell)
[docs] def hist_of_f_of_x(self, weights=None): """Returns middle-points of bins and CIC histogram. Parameters ---------- weights """ if (weights is None): weights = np.ones_like(self.xarr) else: CheckArrays.check_masslike_array(weights) if np.shape(self.xarr)[0] != np.shape(weights)[0]: raise ValueError("Should be given arrays of same shape (N,).") hg, ed = np.histogram(self.xg, self.edges, weights=self.lg * weights) hd, ed = np.histogram(self.xd, self.edges, weights=self.ld * weights) x = self.get_midbins() return x, hg + hd
[docs] def binned_y_of_f_of_x(self, tobin, weights=None): """Returns middle-points of bins and mass-weighted average of tobin array in rad bins. Parameters ---------- weights : ndarray (N,) masses. tobin : Returns ------- """ CheckArrays.check_masslike_array(tobin) if (weights is None): weights = np.ones_like(tobin) CheckArrays.check_masslike_array(weights) if np.shape(tobin)[0] != np.shape(weights)[0]: raise ValueError("Should be given arrays of same shape (N,).") hg, ed = np.histogram(self.xg, self.edges, weights=self.lg * weights * tobin) hd, ed = np.histogram(self.xd, self.edges, weights=self.ld * weights * tobin) x, hm = self.hist_of_f_of_x(weights=weights) h = (hg + hd) / hm return x, h
[docs] def binned_dispersion_of_y_of_f_of_x(self, tobin, weights=None): """Returns middle-points of bins and mass-weighted dispersion of tobin array in rad bins. Parameters ---------- weights : ndarray (N,) masses. tobin : Returns ------- """ CheckArrays.check_masslike_array(tobin) if (weights is None): weights = np.ones_like(tobin) CheckArrays.check_masslike_array(weights) if np.shape(tobin)[0] != np.shape(weights)[0]: raise ValueError("Should be given arrays of same shape (N,).") x, hofavsquares = self.binned_y_of_f_of_x(tobin ** 2, weights) x, hav = self.binned_y_of_f_of_x(tobin, weights=weights) h = hofavsquares - hav ** 2 h = np.sqrt(h) return x, h
[docs]class CicHist2DOfXY(Hist2DOfXY): """Class for two-dimensional cloud-in-cell histograms with nbx,y+1 linear bins edges from x,ymin to x,ymax.""" def __init__(self, xcoords, ycoords, xrange, yrange, nbx=128, nby=128): Hist2DOfXY.__init__(self, xcoords, ycoords, xrange, yrange, nbx=nbx, nby=nby, xedges=None, yedges=None) self.lxcell = self.xedges[1] - self.xedges[0] self.lycell = self.yedges[1] - self.yedges[0] self.xleft = xcoords - 0.5 * self.lxcell self.xright = xcoords + 0.5 * self.lxcell self.ybottom = ycoords - 0.5 * self.lycell self.ytop = ycoords + 0.5 * self.lycell self.lxleft = np.floor(self.xleft / self.lxcell) + 1 - self.xleft / self.lxcell self.lxright = self.xright / self.lxcell - np.floor(self.xright / self.lxcell) self.lybottom = np.floor(self.ybottom / self.lycell) + 1 - self.ybottom / self.lycell self.lytop = self.ytop / self.lycell - np.floor(self.ytop / self.lycell)
[docs] def cic2d(self, wght): """Computes weighted CIC histogram. Parameters ---------- wght : Returns ------- """ hgb, edy, edx = np.histogram2d(self.ybottom, self.xleft, [self.yedges, self.xedges], weights=self.lxleft * self.lybottom * wght) hgh, edy, edx = np.histogram2d(self.ytop, self.xleft, [self.yedges, self.xedges], weights=self.lxleft * self.lytop * wght) hdb, edy, edx = np.histogram2d(self.ybottom, self.xright, [self.yedges, self.xedges], weights=self.lxright * self.lybottom * wght) hdh, edy, edx = np.histogram2d(self.ytop, self.xright, [self.yedges, self.xedges], weights=self.lxright * self.lytop * wght) return hgb + hgh + hdb + hdh, edy, edx
[docs] def hist2d(self, weights=None): """Returns histogram and middle points of y,x bins. Parameters ---------- weights """ if (weights is None): weights = np.ones_like(self.xleft) return self.cic2d(weights)
[docs] def binned_in_2d_array(self, tobin, weights=None): """Returns mass-weighted average of tobin array in bins and middle points of y,x bins. Parameters ---------- weights : ndarray (N,) masses. tobin : Returns ------- """ wght = weights * tobin h, edy, edx = self.cic2d(wght) hm, edy, edx = self.hist2d(weights=weights) indsnonzero = (hm != 0) h[indsnonzero] /= hm[indsnonzero] h[~indsnonzero] = np.nan return h, edy, edx
[docs]def xrange_from_xarr(xarr, xrange, funcofx): """Returns xrange if not (None, None) or else chooses reasonable range according to chosen function of x. Parameters ---------- xarr : ndarray (N,) array. xrange : (float, float) or (None, None) (xmin,xmax) returned as such if not (None, None). funcofx : ufunc Function such that the histogram will be of funcofx(xarr). Returns ------- (float, float) Rangeon which the histogram of funcofx(xarr) will be computed. """ xmin = xrange[0] xmax = xrange[1] if ((xmin is None) | (xmax is None)): if ((funcofx == np.log10) | (funcofx == np.log) | (funcofx == np.sqrt)): proper_indexes = xarr > 0 else: proper_indexes = np.fabs(funcofx(xarr)) != np.inf xmin = np.nanmin(xarr[proper_indexes]) xmax = np.nanmax(xarr[proper_indexes]) if (xmin == xmax): xmin = 0.9 * xmin xmax = 1.1 * xmax return (xmin, xmax)
[docs]def linearly_spaced_edges_in_function_from_nb(xrange, function, nb): """Returns edges linearly spaced in function, i.e. function(xmin) + np.arange(nb + 1) * (function(xmax) - function(xmin)) / (nb)" Parameters ---------- xrange : (float, float) (xmin, xmax) nb : int Number of bins. function : ufunc Function such that unction(xmin) + np.arange(nb + 1) * (function(xmax) - function(xmin)) / (nb) is returned. Returns ------- ndarray (nb+1,) edges linearly spaced in function. """ funcofx = function(xrange[0]) + np.arange(nb + 1) * (function(xrange[1]) - function(xrange[0])) / (nb) return funcofx
[docs]def linearly_spaced_edges_in_function_from_bin_size(xrange, function, bin_size): """Returns edges linearly spaced in function, i.e. function(xmin) + np.arange(nb + 1) * (function(xmax) - function(xmin)) / (nb)" Parameters ---------- xrange : (float, float) (xmin, xmax) bin_size : float Width of bins. function : ufunc Function such that unction(xmin) + np.arange(nb + 1) * (function(xmax) - function(xmin)) / (nb) is returned. Returns ------- ndarray (nb+1,) edges linearly spaced in function. """ nb = int((function(xrange[1]) - function(xrange[0])) / bin_size) + 1 # at worst, one useless bin in the end funcofx = function(xrange[0]) + np.arange(nb + 1) * bin_size return funcofx
[docs]def fill_range_around_edges(arr, func, edges): """Fills range of func(arr) with additional bins if edges do not cover the whole range. Parameters ---------- arr : ndarray (N,) array. func : ufunc Function such that bins in func(arr) are computed. edges : ndarray Edges. Returns ------- ndarray Edges covering the whole range of func(arr), linearly space in func(arr). """ xrange = xrange_from_xarr(arr, (None, None), func) xrange = func(xrange) bin_size = edges[1] - edges[0] if (xrange[0] < edges[0]): nb_left = int((edges[0] - xrange[0]) / bin_size) + 1 left_edges = edges[0] - bin_size - np.arange(nb_left) * bin_size left_edges = left_edges[::-1] edges = np.concatenate((left_edges, edges)) if (xrange[1] > edges[-1]): nb_right = int((xrange[1] - edges[-1]) / bin_size) + 1 right_edges = edges[-1] + bin_size + np.arange(nb_right) * bin_size edges = np.concatenate((edges, right_edges)) return edges