def _set_percentile_splitting(self, split=0.5, **kwargs): """ Method interprets the arguments passed to the constructor and sets up the interpolation scheme for how halos will be divided into two types as a function of the primary halo property. """ if 'splitting_model' in kwargs: self.splitting_model = kwargs['splitting_model'] func = getattr(self.splitting_model, kwargs['splitting_method_name']) if isinstance(func, collections.Callable): self._input_split_func = func else: raise HalotoolsError("Input ``splitting_model`` must have a callable function " "named ``%s``" % kwargs['splitting_method_name']) elif 'split_abscissa' in list(kwargs.keys()): if custom_len(kwargs['split_abscissa']) != custom_len(split): raise HalotoolsError("``split`` and ``split_abscissa`` must have the same length") self._split_abscissa = kwargs['split_abscissa'] self._split_ordinates = split else: try: self._split_abscissa = [2] self._split_ordinates = [split] except KeyError: msg = ("The _set_percentile_splitting method must at least be called with a ``split``" "keyword argument, or alternatively ``split`` and ``split_abscissa`` arguments.") raise HalotoolsError(msg)
def compute_conditional_shuffled_ranks(indices_of_prim_haloprop_bin, sec_haloprop, correlation_coeff, **kwargs): ''' TODO Docs ''' if sec_haloprop is None: msg = ( "\n``sec_haloprop`` must be passed into compute_conditional_shuffled_ranks, or a table" "with ``sec_haloprop_key`` as a column.\n") raise HalotoolsError(msg) try: assert np.all( np.logical_and(-1 <= correlation_coeff, correlation_coeff <= 1)) except AssertionError: msg = ( "\n``correlation_coeff`` must be passed into compute_conditional_percentiles," "and must be between -1 and 1\n") raise HalotoolsError(msg) num_in_bin = len(indices_of_prim_haloprop_bin) original_ranks = rankdata(sec_haloprop[indices_of_prim_haloprop_bin], 'ordinal') - 0.5 original_ranks /= num_in_bin return noisy_percentile(original_ranks, correlation_coeff=correlation_coeff)
def _process_args(sample1, sample2, period, Nsub): """ utility function to process common function arguments """ # check to make sure Nsub is reasonable Nsub = np.atleast_1d(Nsub) if len(Nsub) == 1: Nsub = np.array([Nsub[0]] * 3) try: assert np.all(Nsub < np.inf) assert np.all(Nsub > 0) except AssertionError: msg = ("`Nsub` must be a bounded positive number in all dimensions.") raise HalotoolsError(msg) # check to see if we are using periodic boundary conditions if period is None: PBCs = False else: PBCs = True # determine minimum box size the data occupies. if PBCs is False: sample1, sample2, randoms, Lbox = _enclose_in_box(sample1, sample2) else: Lbox = period j_index_1, N_sub_vol = cuboid_subvolume_labels(sample1, Nsub, Lbox) j_index_2, N_sub_vol = cuboid_subvolume_labels(sample2, Nsub, Lbox) return sample1, sample2, j_index_1, j_index_2, Nsub, N_sub_vol, Lbox, PBCs
def _initialize_assembias_param_dict(self, assembias_strength=0.5, assembias_slope=1.0, **kwargs): r""" For full documentation, see the Heaviside Assembias Declaration in Halotools. This function calls the superclass's version. Then, it adds the parameters from disp_func to the dict as well. Parameters ---------- assembias_strength : float, optional Strength of assembias. Default is 1.0 for maximum strength assembias_slope : float, optional Effective slopes of disp_func. Can be an iterator of float. Uses the same abscissa as assembias_strength """ super(ContinuousAssembias, self)._initialize_assembias_param_dict( assembias_strength=assembias_strength, **kwargs) # Accept float or iterable slope = assembias_slope try: iterator = iter(slope) slope = list(slope) except TypeError: slope = [slope] # assert it has the proper length if custom_len(self._assembias_strength_abscissa) != custom_len(slope): raise HalotoolsError("``assembias_strength`` and ``assembias_slope`` " "must have the same length") for ipar, val in enumerate(slope): self.param_dict[self._get_continuous_assembias_param_dict_key(ipar)] = val
def _initialize_assembias_param_dict(self, assembias_strength=0.5, **kwargs): """ """ if not hasattr(self, 'param_dict'): self.param_dict = {} # Make sure the code behaves properly whether or not we were passed an iterable strength = assembias_strength try: iterator = iter(strength) strength = list(strength) except TypeError: strength = [strength] if 'assembias_strength_abscissa' in kwargs: abscissa = kwargs['assembias_strength_abscissa'] try: iterator = iter(abscissa) abscissa = list(abscissa) except TypeError: abscissa = [abscissa] else: abscissa = [2] if custom_len(abscissa) != custom_len(strength): raise HalotoolsError("``assembias_strength`` and ``assembias_strength_abscissa`` " "must have the same length") self._assembias_strength_abscissa = abscissa for ipar, val in enumerate(strength): self.param_dict[self._get_assembias_param_dict_key(ipar)] = val
def compute_prim_haloprop_bins(dlog10_prim_haloprop=0.05, **kwargs): """ Parameters ---------- prim_haloprop : array Array storing the value of the primary halo property column of the ``table`` passed to ``compute_conditional_percentiles``. prim_haloprop_bin_boundaries : array, optional Array defining the boundaries by which we will bin the input ``table``. Default is None, in which case the binning will be automatically determined using the ``dlog10_prim_haloprop`` keyword. dlog10_prim_haloprop : float, optional Logarithmic spacing of bins of the mass-like variable within which we will assign secondary property percentiles. Default is 0.05. Returns -------- output : array Numpy array of integers storing the bin index of the prim_haloprop bin to which each halo in the input table was assigned. """ try: prim_haloprop = kwargs['prim_haloprop'] except KeyError: msg = ("The ``compute_prim_haloprop_bins`` method " "requires the ``prim_haloprop`` keyword argument") raise HalotoolsError(msg) try: prim_haloprop_bin_boundaries = kwargs['prim_haloprop_bin_boundaries'] except KeyError: lg10_min_prim_haloprop = np.log10(np.min(prim_haloprop)) - 0.001 lg10_max_prim_haloprop = np.log10(np.max(prim_haloprop)) + 0.001 num_prim_haloprop_bins = ( lg10_max_prim_haloprop - lg10_min_prim_haloprop) / dlog10_prim_haloprop prim_haloprop_bin_boundaries = np.logspace( lg10_min_prim_haloprop, lg10_max_prim_haloprop, num=ceil(num_prim_haloprop_bins)) # digitize the masses so that we can access them bin-wise #print "PHP",np.max(prim_haloprop), prim_haloprop_bin_boundaries[-1] output = np.digitize(prim_haloprop, prim_haloprop_bin_boundaries) # Use the largest bin for any points larger than the largest bin boundary, # and raise a warning if such points are found Nbins = len(prim_haloprop_bin_boundaries) if Nbins in output: msg = ( "\n\nThe ``compute_prim_haloprop_bins`` function detected points in the \n" "input array of primary halo property that were larger than the largest value\n" "of the input ``prim_haloprop_bin_boundaries``. All such points will be assigned\n" "to the largest bin.\nBe sure that this is the behavior you expect for your application.\n\n" ) warn(msg) output = np.where(output == Nbins, Nbins - 1, output) return output
def _set_percentile_splitting(self, split=0.5, **kwargs): """ Interpret constructor arguments and set up interpolation scheme In this subclass, add "split" as a free parameter, similar to assembias. """ if not hasattr(self, 'param_dict'): self.param_dict = {} if 'splitting_model' in kwargs: #self.splitting_model = kwargs['splitting_model'] #func = getattr(self.splitting_model, kwargs['splitting_method_name']) #if isinstance(func, collections.Callable): # self._input_split_func = func #else: raise HalotoolsError( "Input ``splitting_model`` has not yet been implemented for the " "FreeSplitAssembias subclass.") # Make sure the code behaves properly whether or not we were passed an iterable try: iterator = iter(split) split = list(split) except TypeError: split = [split] if 'assembias_strength_abscissa' in kwargs: abscissa = kwargs['assembias_strength_abscissa'] try: iterator = iter(abscissa) abscissa = list(abscissa) except TypeError: abscissa = [abscissa] else: abscissa = [2] if custom_len(abscissa) != custom_len(split): raise HalotoolsError( "``assembias_strength`` and ``assembias_strength_abscissa`` " "must have the same length") self._split_abscissa = abscissa for ipar, val in enumerate(split): self.param_dict[self._get_free_split_assembias_param_dict_key( ipar)] = val
def _interpret_constructor_inputs(self, loginterp=True, sec_haloprop_key=model_defaults.sec_haloprop_key, **kwargs): """ """ self._loginterp = loginterp self.sec_haloprop_key = sec_haloprop_key required_attr_list = ['prim_haloprop_key', 'gal_type'] for attr in required_attr_list: if not hasattr(self, attr): msg = ("In order to use the HeavisideAssembias class " "to decorate your model component with assembly bias, \n" "the component instance must have a %s attribute") raise HalotoolsError(msg % attr) try: self._method_name_to_decorate = kwargs['method_name_to_decorate'] except KeyError: msg = ("The constructor to the HeavisideAssembiasComponent class " "must be called with the following keyword arguments:\n" "``%s``") raise HalotoolsError(msg % ('_method_name_to_decorate')) try: lower_bound = float(kwargs['lower_assembias_bound']) lower_bound_key = 'lower_bound_' + self._method_name_to_decorate + '_' + self.gal_type setattr(self, lower_bound_key, lower_bound) upper_bound = float(kwargs['upper_assembias_bound']) upper_bound_key = 'upper_bound_' + self._method_name_to_decorate + '_' + self.gal_type setattr(self, upper_bound_key, upper_bound) except KeyError: msg = ("The constructor to the HeavisideAssembiasComponent class " "must be called with the following keyword arguments:\n" "``%s``, ``%s``") raise HalotoolsError(msg % ('lower_assembias_bound', 'upper_assembias_bound')) self._set_percentile_splitting(**kwargs) self._initialize_assembias_param_dict(**kwargs) if 'halo_type_tuple' in kwargs: self.halo_type_tuple = kwargs['halo_type_tuple']
def percentile_splitting_function(self, prim_haloprop): """ Method returns the fraction of halos that are ``type-2`` as a function of the input primary halo property. Parameters ----------- prim_haloprop : array_like Array storing the primary halo property. Returns ------- split : float Fraction of ``type2`` halos at the input primary halo property. """ if hasattr(self, '_input_split_func'): result = self._input_split_func(prim_haloprop=prim_haloprop) if np.any(result < 0): msg = ("The input split_func passed to the HeavisideAssembias class" "must not return negative values") raise HalotoolsError(msg) if np.any(result > 1): msg = ("The input split_func passed to the HeavisideAssembias class" "must not return values exceeding unity") raise HalotoolsError(msg) return result elif self._loginterp is True: spline_function = model_helpers.custom_spline( np.log10(self._split_abscissa), self._split_ordinates, k=3) result = spline_function(np.log10(prim_haloprop)) else: spline_function = model_helpers.custom_spline( self._split_abscissa, self._split_ordinates, k=3) result = spline_function(prim_haloprop) return result
def _galprop_perturbation(self, **kwargs): """ Method determines how much to boost the baseline function according to the strength of assembly bias and the min/max boost allowable by the requirement that the all-halo baseline function be preserved. The returned perturbation applies to type-1 halos. """ lower_bound_key = 'lower_bound_' + self._method_name_to_decorate + '_' + self.gal_type baseline_lower_bound = getattr(self, lower_bound_key) upper_bound_key = 'upper_bound_' + self._method_name_to_decorate + '_' + self.gal_type baseline_upper_bound = getattr(self, upper_bound_key) try: baseline_result = kwargs['baseline_result'] prim_haloprop = kwargs['prim_haloprop'] splitting_result = kwargs['splitting_result'] except KeyError: msg = ("Must call _galprop_perturbation method of the" "HeavisideAssembias class with the following keyword arguments:\n" "``baseline_result``, ``splitting_result`` and ``prim_haloprop``") raise HalotoolsError(msg) result = np.zeros(len(prim_haloprop)) strength = self.assembias_strength(prim_haloprop) positive_strength_idx = strength > 0 negative_strength_idx = strength < 0 if len(baseline_result[positive_strength_idx]) > 0: base_pos = baseline_result[positive_strength_idx] split_pos = splitting_result[positive_strength_idx] type1_frac_pos = 1 - split_pos strength_pos = strength[positive_strength_idx] upper_bound1 = baseline_upper_bound - base_pos upper_bound2 = ((1 - type1_frac_pos)/type1_frac_pos)*(base_pos - baseline_lower_bound) upper_bound = np.minimum(upper_bound1, upper_bound2) result[positive_strength_idx] = strength_pos*upper_bound if len(baseline_result[negative_strength_idx]) > 0: base_neg = baseline_result[negative_strength_idx] split_neg = splitting_result[negative_strength_idx] type1_frac_neg = 1 - split_neg strength_neg = strength[negative_strength_idx] lower_bound1 = baseline_lower_bound - base_neg lower_bound2 = (1 - type1_frac_neg)/type1_frac_neg*(base_neg - baseline_upper_bound) lower_bound = np.maximum(lower_bound1, lower_bound2) result[negative_strength_idx] = np.abs(strength_neg)*lower_bound return result
def _decorate_baseline_method(self): """ """ try: baseline_method = getattr(self, self._method_name_to_decorate) setattr(self, 'baseline_'+self._method_name_to_decorate, baseline_method) decorated_method = self.assembias_decorator(baseline_method) setattr(self, self._method_name_to_decorate, decorated_method) except AttributeError: msg = ("The baseline model constructor must be called before " "calling the HeavisideAssembias constructor, \n" "and the baseline model must have a method named ``%s``") raise HalotoolsError(msg % self._method_name_to_decorate)
def assembias_mc_occupation(self, seed=None, **kwargs): first_occupation_moment_orig = self.mean_occupation_orig(**kwargs) first_occupation_moment = self.mean_occupation(**kwargs) if self._upper_occupation_bound == 1: with NumpyRNGContext(seed): score = np.random.rand( custom_len(first_occupation_moment_orig)) total = np.count_nonzero(first_occupation_moment_orig > score) result = np.where(first_occupation_moment > score, 1, 0) diff = result.sum() - total if diff < 0: x = (first_occupation_moment / score) result.fill(0) result[x.argsort()[-total:]] = 1 elif diff > 0: x = (1.0 - first_occupation_moment) / (1.0 - score) result.fill(0) result[x.argsort()[:total]] = 1 elif self._upper_occupation_bound == float("inf"): total = self._poisson_distribution( first_occupation_moment_orig.sum(), seed=seed) if seed is not None: seed += 1 with NumpyRNGContext(seed): score = np.random.rand(total) score.sort() x = first_occupation_moment.cumsum(dtype=np.float64) x /= x[-1] result = np.ediff1d(np.insert(np.searchsorted(score, x), 0, 0)) else: msg = ( "\nYou have chosen to set ``_upper_occupation_bound`` to some value \n" "besides 1 or infinity. In such cases, you must also \n" "write your own ``mc_occupation`` method that overrides the method in the \n" "OccupationComponent super-class\n") raise HalotoolsError(msg) if 'table' in kwargs: kwargs['table']['halo_num_' + self.gal_type] = result return result
def wrapper(*args, **kwargs): ################################################################################# # Retrieve the arrays storing prim_haloprop and sec_haloprop # The control flow below is what permits accepting an input # table or a directly inputting prim_haloprop and sec_haloprop arrays _HAS_table = False if 'table' in kwargs: try: table = kwargs['table'] prim_haloprop = table[self.prim_haloprop_key] sec_haloprop = table[self.sec_haloprop_key] _HAS_table = True except KeyError: msg = ("When passing an input ``table`` to the " " ``assembias_decorator`` method,\n" "the input table must have a column with name ``%s``" "and a column with name ``%s``.\n") raise HalotoolsError(msg % (self.prim_haloprop_key), self.sec_haloprop_key) else: try: prim_haloprop = np.atleast_1d(kwargs['prim_haloprop']) except KeyError: msg = ("\nIf not passing an input ``table`` to the " "``assembias_decorator`` method,\n" "you must pass ``prim_haloprop`` argument.\n") raise HalotoolsError(msg) try: sec_haloprop = np.atleast_1d(kwargs['sec_haloprop']) except KeyError: msg = ("\nIf not passing an input ``table`` to the " "``assembias_decorator`` method,\n" "you must pass ``sec_haloprop`` argument") raise HalotoolsError(msg) ################################################################################# # Compute the percentile to split on as a function of the input prim_haloprop split = self.percentile_splitting_function(prim_haloprop) # Compute the baseline, undecorated result result = func(*args, **kwargs) # We will only decorate values that are not edge cases, # so first compute the mask for non-edge cases no_edge_mask = ( (split > 0) & (split < 1) & (result > baseline_lower_bound) & (result < baseline_upper_bound) ) # Now create convenient references to the non-edge-case sub-arrays no_edge_result = result[no_edge_mask] no_edge_split = split[no_edge_mask] # Retrieve percentile values (medians) if they've been precomputed. Else, compute them. if _HAS_table is True: if self.sec_haloprop_key + '_percentile_values' in table.keys(): no_edge_percentile_values = table[self.sec_haloprop_key + '_percentile_value'][no_edge_mask] else: # the value of sec_haloprop_percentile will be computed from scratch no_edge_percentile_values = compute_conditional_percentile_values( p=no_edge_split, prim_haloprop=prim_haloprop[no_edge_mask], sec_haloprop=sec_haloprop[no_edge_mask] ) else: try: percentiles = kwargs['sec_haloprop_percentile_values'] if custom_len(percentiles) == 1: percentiles = np.zeros(custom_len(prim_haloprop)) + percentiles no_edge_percentile_values = percentiles[no_edge_mask] except KeyError: no_edge_percentile_values = compute_conditional_percentile_values(p=no_edge_split, prim_haloprop=prim_haloprop[no_edge_mask], sec_haloprop=sec_haloprop[no_edge_mask] ) # NOTE I've removed the type 1 mask as it is not well-defined in this implementation # this has all been rolled into the galprop_perturbation function # normalize by max value away from percentile # This ensures that the "slope" definition and boundaries are universal pv_sub_sec_haloprop = sec_haloprop[no_edge_mask] - no_edge_percentile_values if prim_haloprop[no_edge_mask].shape[0] == 0: perturbation = np.zeros_like(no_edge_result) else: perturbation = self._galprop_perturbation( prim_haloprop=prim_haloprop[no_edge_mask], sec_haloprop=pv_sub_sec_haloprop/np.max(np.abs(pv_sub_sec_haloprop)), #sec_haloprop = compute_conditional_percentiles(prim_haloprop = prim_haloprop[no_edge_mask], sec_haloprop=sec_haloprop[no_edge_mask])-no_edge_split, baseline_result=no_edge_result) no_edge_result += perturbation result[no_edge_mask] = no_edge_result return result
def __call__(self, *args, **kwargs): if 'table' in kwargs: table = kwargs['table'] try: prim_haloprop_key = kwargs['prim_haloprop_key'] prim_haloprop = table[prim_haloprop_key] except KeyError: msg = ( "\nWhen passing an input ``table`` to a ``compute_conditional_*`` method,\n" "you must also pass ``prim_haloprop_key`` keyword arguments\n" "whose values are column keys of the input ``table``\n") raise HalotoolsError(msg) # Note sec_haloprop is not necessary for all methods. try: sec_haloprop_key = kwargs['sec_haloprop_key'] sec_haloprop = table[sec_haloprop_key] except KeyError: sec_haloprop = None else: try: prim_haloprop = kwargs['prim_haloprop'] except KeyError: msg = ( "\nIf not passing an input ``table`` to a ``compute_conditional_*`` method,\n" "you must pass a ``prim_haloprop`` arguments\n") raise HalotoolsError(msg) try: sec_haloprop = kwargs['sec_haloprop'] except KeyError: sec_haloprop = None compute_prim_haloprop_bins_dict = {} compute_prim_haloprop_bins_dict['prim_haloprop'] = prim_haloprop try: compute_prim_haloprop_bins_dict['prim_haloprop_bin_boundaries'] = ( kwargs['prim_haloprop_bin_boundaries']) except KeyError: pass try: compute_prim_haloprop_bins_dict['dlog10_prim_haloprop'] = kwargs[ 'dlog10_prim_haloprop'] except KeyError: pass # Check if we need to recompute the mass bins, or if it's been memoized same_dict = False if compute_prim_haloprop_bins_dict.keys( ) == self.last_compute_prim_haloprop_bins_dict.keys( ): # same as we were last asked for, don't recompute for key, val in compute_prim_haloprop_bins_dict.iteritems(): last_val = self.last_compute_prim_haloprop_bins_dict[key] if hasattr(val, 'shape'): if val.shape != last_val.shape: break # We now know they have the same shape if not np.all(np.equal(val, last_val)): break elif val != last_val: break else: same_dict = True if same_dict: prim_haloprop_bins = self.last_prim_haloprop_bins else: prim_haloprop_bins = compute_prim_haloprop_bins( **compute_prim_haloprop_bins_dict) #update cache self.last_compute_prim_haloprop_bins_dict = compute_prim_haloprop_bins_dict self.last_prim_haloprop_bins = prim_haloprop_bins output = np.zeros_like(prim_haloprop) # toss these in here so we don't have to repeat the above! fkwargs = kwargs.copy() if 'prim_haloprop' not in fkwargs: fkwargs['prim_haloprop'] = prim_haloprop if 'sec_haloprop' not in fkwargs: fkwargs['sec_haloprop'] = sec_haloprop # sort on secondary property only with each mass bin bins_in_halocat = set(prim_haloprop_bins) #idx is usully the same; however, some edge cases make this work better for idx, ibin in enumerate(bins_in_halocat): indices_of_prim_haloprop_bin = np.where( prim_haloprop_bins == ibin)[0] output[indices_of_prim_haloprop_bin] = self.func( idx=idx, ibin=ibin, indices_of_prim_haloprop_bin=indices_of_prim_haloprop_bin, *args, **fkwargs) return output
def compute_conditional_percentiles(indices_of_prim_haloprop_bin, sec_haloprop, **kwargs): r""" In bins of the ``prim_haloprop``, compute the rank-order percentile of the input ``table`` based on the value of ``sec_haloprop``. Note indices_of_prim_haloprop_bin is passed in from the decorator and does not need to be specified. Parameters ---------- table : astropy table, optional a keyword argument that stores halo catalog being used to make mock galaxy population If a `table` is passed, the `prim_haloprop_key` and `sec_haloprop_key` keys must also be passed. If not passing a `table`, you must directly pass the `prim_haloprop` and `sec_haloprop` keyword arguments. prim_haloprop_key : string, optional Name of the column of the input ``table`` that will be used to access the primary halo property. `compute_conditional_percentiles` bins the ``table`` by ``prim_haloprop_key`` when computing the result. sec_haloprop_key : string, optional Name of the column of the input ``table`` that will be used to access the secondary halo property. `compute_conditional_percentiles` bins the ``table`` by ``prim_haloprop_key``, and in each bin uses the value stored in ``sec_haloprop_key`` to compute the ``prim_haloprop``-conditioned rank-order percentile. prim_haloprop : array_like, optional Array storing the primary halo property used to bin the input points. If a `prim_haloprop` is passed, you must also pass a `sec_haloprop`. sec_haloprop : array_like, optional Array storing the secondary halo property used to define the conditional percentiles in each bin of `prim_haloprop`. prim_haloprop_bin_boundaries : array, optional Array defining the boundaries by which we will bin the input ``table``. Default is None, in which case the binning will be automatically determined using the ``dlog10_prim_haloprop`` keyword. dlog10_prim_haloprop : float, optional Logarithmic spacing of bins of the mass-like variable within which we will assign secondary property percentiles. Default is 0.2. Examples -------- >>> from halotools.sim_manager import FakeSim >>> fakesim = FakeSim() >>> result = compute_conditional_percentiles(table = fakesim.halo_table, prim_haloprop_key = 'halo_mvir', sec_haloprop_key = 'halo_vmax') Notes ----- The sign of the result is such that in bins of the primary property, *smaller* values of the secondary property receive *smaller* values of the returned percentile. """ if sec_haloprop is None: msg = ( "\n``sec_haloprop`` must be passed into compute_conditional_percentiles, or a table" "with ``sec_haloprop_key`` as a column.\n") raise HalotoolsError(msg) num_in_bin = len(sec_haloprop[indices_of_prim_haloprop_bin]) # Find the indices that sort by the secondary property ind_sorted = np.argsort(sec_haloprop[indices_of_prim_haloprop_bin]) percentiles = np.zeros(num_in_bin) percentiles[ind_sorted] = (np.arange(num_in_bin) + 1.0) / float(num_in_bin) return percentiles
def compute_conditional_percentile_values(idx, indices_of_prim_haloprop_bin, sec_haloprop, p=0.5, **kwargs): """ In bins of the ``prim_haloprop``, compute the percentile given by p of the input ``table`` based on the value of ``sec_haloprop``. Note indices_of_prim_haloprop_bin is passed in from the decorator and does not need to be specified. Parameters ---------- p: float or array Percentile to find. Float or array of floats between 0 and 1. Default is 0.5, the median. table : astropy table, optional a keyword argument that stores halo catalog being used to make mock galaxy population If a `table` is passed, the `prim_haloprop_key` and `sec_haloprop_key` keys must also be passed. If not passing a `table`, you must directly pass the `prim_haloprop` and `sec_haloprop` keyword arguments. prim_haloprop_key : string, optional Name of the column of the input ``table`` that will be used to access the primary halo property. `compute_conditional_percentile_values` bins the ``table`` by ``prim_haloprop_key`` when computing the result. sec_haloprop_key : string, optional Name of the column of the input ``table`` that will be used to access the secondary halo property. `compute_conditional_percentile_values` bins the ``table`` by ``prim_haloprop_key``, and in each bin uses the value stored in ``sec_haloprop_key`` to compute the ``prim_haloprop``-conditioned rank-order percentile. prim_haloprop : array_like, optional Array storing the primary halo property used to bin the input points. If a `prim_haloprop` is passed, you must also pass a `sec_haloprop`. sec_haloprop : array_like, optional Array storing the secondary halo property used to define the conditional percentiles in each bin of `prim_haloprop`. prim_haloprop_bin_boundaries : array, optional Array defining the boundaries by which we will bin the input ``table``. Default is None, in which case the binning will be automatically determined using the ``dlog10_prim_haloprop`` keyword. dlog10_prim_haloprop : float, optional Logarithmic spacing of bins of the mass-like variable within which we will assign secondary property percentiles. Default is 0.2. Examples -------- >>> from halotools.sim_manager import FakeSim >>> fakesim = FakeSim() >>> result = compute_conditional_percentile_values(table = fakesim.halo_table, prim_haloprop_key = 'halo_mvir', sec_haloprop_key = 'halo_vmax') Notes ----- The sign of the result is such that in bins of the primary property, *smaller* values of the secondary property receive *smaller* values of the returned percentile. """ pp = p if type(p) is float else p[idx - 1] try: assert 0 <= pp <= 1 except AssertionError: raise HalotoolsError( 'The value of `p=%0.2f` in ``compute_conditional_percentile_values`` must be between 0 and 1' % pp) return np.percentile(sec_haloprop[indices_of_prim_haloprop_bin], pp * 100)
def _galprop_perturbation(self, **kwargs): r""" Method determines hwo much to boost the baseline function according to the strength of assembly bias and the min/max boost allowable by the requirement that the all-halo baseline function be preserved. The returned perturbation applies to all halos. Required kwargs are: ``baseline_result``, ``prim_haloprop``, ``sec_haloprop``. Note that this is defined where sec_haloprop has had the p-th percentile subtracted and is normalized by the maximum value. Returns ndarray with dimensions of prim_haloprop detailing the perturbation. """ lower_bound_key = 'lower_bound_' + self._method_name_to_decorate + '_' + self.gal_type baseline_lower_bound = getattr(self, lower_bound_key) upper_bound_key = 'upper_bound_' + self._method_name_to_decorate + '_' + self.gal_type baseline_upper_bound = getattr(self, upper_bound_key) try: baseline_result = kwargs['baseline_result'] prim_haloprop = kwargs['prim_haloprop'] sec_haloprop = kwargs['sec_haloprop'] except KeyError: msg = ("Must call _galprop_perturbation method of the" "HeavisideAssembias class with the following keyword arguments:\n" "``baseline_result``, ``splitting_result`` and ``prim_haloprop``") raise HalotoolsError(msg) # evaluate my continuous modification strength = self.assembias_strength(prim_haloprop) slope = self.assembias_slope(prim_haloprop) # the average displacement acts as a normalization we need. max_displacement = self._disp_func(sec_haloprop=sec_haloprop, slope=slope) disp_average = compute_conditional_averages(vals=max_displacement,prim_haloprop=prim_haloprop) #disp_average = np.ones((prim_haloprop.shape[0], ))*0.5 result = np.zeros(len(prim_haloprop)) greater_than_half_avg_idx = disp_average > 0.5 less_than_half_avg_idx = disp_average <= 0.5 #print max_displacement #print strength, slope if len(max_displacement[greater_than_half_avg_idx]) > 0: base_pos = baseline_result[greater_than_half_avg_idx] strength_pos = strength[greater_than_half_avg_idx] avg_pos = disp_average[greater_than_half_avg_idx] upper_bound1 = (base_pos - baseline_lower_bound)/avg_pos upper_bound2 = (baseline_upper_bound - base_pos)/(1-avg_pos) upper_bound = np.minimum(upper_bound1, upper_bound2) result[greater_than_half_avg_idx] = strength_pos*upper_bound*(max_displacement[greater_than_half_avg_idx]-avg_pos) if len(max_displacement[less_than_half_avg_idx]) > 0: base_neg = baseline_result[less_than_half_avg_idx] strength_neg = strength[less_than_half_avg_idx] avg_neg = disp_average[less_than_half_avg_idx] lower_bound1 = (base_neg-baseline_lower_bound)/avg_neg#(1- avg_neg) lower_bound2 = (baseline_upper_bound - base_neg)/(1-avg_neg)#avg_neg lower_bound = np.minimum(lower_bound1, lower_bound2) result[less_than_half_avg_idx] = strength_neg*lower_bound*(max_displacement[less_than_half_avg_idx]-avg_neg) return result
def compute_conditional_percentile(p=0.5, **kwargs): """ In bins of the ``prim_haloprop``, compute the percentile given by p of the input ``table`` based on the value of ``sec_haloprop``. Parameters ---------- p: float or array Percentile to find. Float or array of floats between 0 and 1. Default is 0.5, the median. table : astropy table, optional a keyword argument that stores halo catalog being used to make mock galaxy population If a `table` is passed, the `prim_haloprop_key` and `sec_haloprop_key` keys must also be passed. If not passing a `table`, you must directly pass the `prim_haloprop` and `sec_haloprop` keyword arguments. prim_haloprop_key : string, optional Name of the column of the input ``table`` that will be used to access the primary halo property. `compute_conditional_percentiles` bins the ``table`` by ``prim_haloprop_key`` when computing the result. sec_haloprop_key : string, optional Name of the column of the input ``table`` that will be used to access the secondary halo property. `compute_conditional_percentiles` bins the ``table`` by ``prim_haloprop_key``, and in each bin uses the value stored in ``sec_haloprop_key`` to compute the ``prim_haloprop``-conditioned rank-order percentile. prim_haloprop : array_like, optional Array storing the primary halo property used to bin the input points. If a `prim_haloprop` is passed, you must also pass a `sec_haloprop`. sec_haloprop : array_like, optional Array storing the secondary halo property used to define the conditional percentiles in each bin of `prim_haloprop`. prim_haloprop_bin_boundaries : array, optional Array defining the boundaries by which we will bin the input ``table``. Default is None, in which case the binning will be automatically determined using the ``dlog10_prim_haloprop`` keyword. dlog10_prim_haloprop : float, optional Logarithmic spacing of bins of the mass-like variable within which we will assign secondary property percentiles. Default is 0.2. Examples -------- >>> from halotools.sim_manager import FakeSim >>> fakesim = FakeSim() >>> result = compute_conditional_percentiles(table = fakesim.halo_table, prim_haloprop_key = 'halo_mvir', sec_haloprop_key = 'halo_vmax') Notes ----- The sign of the result is such that in bins of the primary property, *smaller* values of the secondary property receive *smaller* values of the returned percentile. """ try: assert np.all(0 < p) and np.all(1 > p) except AssertionError: raise HalotoolsError("p must be a floating number between 0 and 1. ") if 'table' in kwargs: table = kwargs['table'] try: prim_haloprop_key = kwargs['prim_haloprop_key'] prim_haloprop = table[prim_haloprop_key] sec_haloprop_key = kwargs['sec_haloprop_key'] sec_haloprop = table[sec_haloprop_key] except KeyError: msg = ( "\nWhen passing an input ``table`` to the ``compute_conditional_percentiles`` method,\n" "you must also pass ``prim_haloprop_key`` and ``sec_haloprop_key`` keyword arguments\n" "whose values are column keys of the input ``table``\n") raise HalotoolsError(msg) else: try: prim_haloprop = kwargs['prim_haloprop'] sec_haloprop = kwargs['sec_haloprop'] except KeyError: msg = ( "\nIf not passing an input ``table`` to the ``compute_conditional_percentiles`` method,\n" "you must pass a ``prim_haloprop`` and ``sec_haloprop`` arguments\n" ) raise HalotoolsError(msg) compute_prim_haloprop_bins_dict = {} compute_prim_haloprop_bins_dict['prim_haloprop'] = prim_haloprop try: compute_prim_haloprop_bins_dict['prim_haloprop_bin_boundaries'] = ( kwargs['prim_haloprop_bin_boundaries']) except KeyError: pass try: compute_prim_haloprop_bins_dict['dlog10_prim_haloprop'] = kwargs[ 'dlog10_prim_haloprop'] except KeyError: pass prim_haloprop_bins = compute_prim_haloprop_bins( **compute_prim_haloprop_bins_dict) output = np.zeros_like(prim_haloprop) if type(p) is float: p = np.array([p for i in xrange(len(prim_haloprop))]) # sort on secondary property only with each mass bin bins_in_halocat = set(prim_haloprop_bins) for ibin, pp in izip(bins_in_halocat, p): indices_of_prim_haloprop_bin = np.where(prim_haloprop_bins == ibin)[0] num_in_bin = len(sec_haloprop[indices_of_prim_haloprop_bin]) # Find the indices that sort by the secondary property perc = np.percentile(sec_haloprop[indices_of_prim_haloprop_bin], pp * 100) # place the percentiles into the catalog output[indices_of_prim_haloprop_bin] = perc return output
# First check to see if the log has any matching entries before # requesting the download # This is technically redundant with the functionality in the downloading methods, # but this makes it easier to issue the right error message if args.overwrite == False: if download_halos == True: gen = downman.halo_table_cache.matching_log_entry_generator matching_halocats = list( gen(simname = simname, halo_finder = halo_finder, version_name = version_name, redshift = redshift, dz_tol = 0.1)) if len(matching_halocats) > 0: matching_fname = matching_halocats[0].fname raise HalotoolsError(existing_fname_error_msg % matching_fname) if download_ptcls == True: gen2 = downman.ptcl_table_cache.matching_log_entry_generator matching_ptcl_cats = list( gen2(simname = simname, version_name = ptcl_version_name, redshift = redshift, dz_tol = 0.1)) if len(matching_ptcl_cats) > 0: matching_fname = matching_ptcl_cats[0].fname raise HalotoolsError(existing_fname_error_msg % matching_fname) ################################################################## ##################################################################
def wrapper(*args, **kwargs): ################################################################################# # Retrieve the arrays storing prim_haloprop and sec_haloprop # The control flow below is what permits accepting an input # table or a directly inputting prim_haloprop and sec_haloprop arrays _HAS_table = False if 'table' in kwargs: try: table = kwargs['table'] prim_haloprop = table[self.prim_haloprop_key] sec_haloprop = table[self.sec_haloprop_key] _HAS_table = True except KeyError: msg = ("When passing an input ``table`` to the " " ``assembias_decorator`` method,\n" "the input table must have a column with name ``%s``" "and a column with name ``%s``.\n") raise HalotoolsError(msg % (self.prim_haloprop_key), self.sec_haloprop_key) else: try: prim_haloprop = np.atleast_1d(kwargs['prim_haloprop']) except KeyError: msg = ("\nIf not passing an input ``table`` to the " "``assembias_decorator`` method,\n" "you must pass ``prim_haloprop`` argument.\n") raise HalotoolsError(msg) try: sec_haloprop = np.atleast_1d(kwargs['sec_haloprop']) except KeyError: if 'sec_haloprop_percentile' not in kwargs: msg = ("\nIf not passing an input ``table`` to the " "``assembias_decorator`` method,\n" "you must pass either a ``sec_haloprop`` or " "``sec_haloprop_percentile`` argument.\n") raise HalotoolsError(msg) ################################################################################# # Compute the fraction of type-2 halos as a function of the input prim_haloprop split = self.percentile_splitting_function(prim_haloprop) # Compute the baseline, undecorated result result = func(*args, **kwargs) # We will only decorate values that are not edge cases, # so first compute the mask for non-edge cases no_edge_mask = ( (split > 0) & (split < 1) & (result > baseline_lower_bound) & (result < baseline_upper_bound) ) # Now create convenient references to the non-edge-case sub-arrays no_edge_result = result[no_edge_mask] no_edge_split = split[no_edge_mask] ################################################################################# # Compute the array type1_mask # This array will serve as a boolean mask that divides the halo sample into two subsamples # There are several possible ways that the type1_mask can be computed, depending on # what the decorator was passed as input if _HAS_table is True: # we were passed halo_type_tuple: if hasattr(self, 'halo_type_tuple'): halo_type_key = self.halo_type_tuple[0] halo_type1_val = self.halo_type_tuple[1] type1_mask = table[halo_type_key][no_edge_mask] == halo_type1_val # the value of sec_haloprop_percentile is already stored as a column of the table elif self.sec_haloprop_key + '_percentile' in list(table.keys()): no_edge_percentiles = table[self.sec_haloprop_key + '_percentile'][no_edge_mask] type1_mask = no_edge_percentiles > no_edge_split else: # the value of sec_haloprop_percentile will be computed from scratch percentiles = compute_conditional_percentiles( prim_haloprop=prim_haloprop, sec_haloprop=sec_haloprop ) no_edge_percentiles = percentiles[no_edge_mask] type1_mask = no_edge_percentiles > no_edge_split else: try: percentiles = kwargs['sec_haloprop_percentile'] if custom_len(percentiles) == 1: percentiles = np.zeros(custom_len(prim_haloprop)) + percentiles except KeyError: percentiles = compute_conditional_percentiles( prim_haloprop=prim_haloprop, sec_haloprop=sec_haloprop ) no_edge_percentiles = percentiles[no_edge_mask] type1_mask = no_edge_percentiles > no_edge_split # type1_mask has now been computed for all possible branchings ################################################################################# perturbation = self._galprop_perturbation( prim_haloprop=prim_haloprop[no_edge_mask], baseline_result=no_edge_result, splitting_result=no_edge_split) frac_type1 = 1 - no_edge_split frac_type2 = 1 - frac_type1 perturbation[~type1_mask] *= (-frac_type1[~type1_mask] / (frac_type2[~type1_mask])) no_edge_result += perturbation result[no_edge_mask] = no_edge_result return result
def _galprop_perturbation(self, **kwargs): """ Method determines hwo much to boost the baseline function according to the strength of assembly bias and the min/max boost allowable by the requirement that the all-halo baseline function be preserved. The returned perturbation applies to type-1 halos. Uses the disp_func passed in duing perturbation :param kwargs: Required kwargs are: baseline_result prim_haloprop sec_haloprop splitting_result :return: result, np.arry with dimensions of prim_haloprop detailing the perturbation. """ #lower_bound_key = 'lower_bound_' + self._method_name_to_decorate + '_' + self.gal_type #baseline_lower_bound = getattr(self, lower_bound_key) upper_bound_key = 'upper_bound_' + self._method_name_to_decorate + '_' + self.gal_type baseline_upper_bound = getattr(self, upper_bound_key) try: baseline_result = kwargs['baseline_result'] prim_haloprop = kwargs['prim_haloprop'] sec_haloprop = kwargs['sec_haloprop'] splitting_result = kwargs['splitting_result'] except KeyError: msg = ( "Must call _galprop_perturbation method of the" "HeavisideAssembias class with the following keyword arguments:\n" "``baseline_result``, ``splitting_result`` and ``prim_haloprop``" ) raise HalotoolsError(msg) #evaluate my continuous modification strength = self.assembias_strength(prim_haloprop) #get the kwargs for disp_func from the param dict disp_func_kwargs = {} for key, val in self.param_dict.iteritems(): if key[:10] == 'disp_func_' and self.gal_type in key: split_key = key.split('_') disp_func_kwargs[split_key[-2]] = val #subtract the central value to make sure the right value is in the center of the function #t0 = time() central_val = compute_conditional_percentile( splitting_result, prim_haloprop=prim_haloprop, sec_haloprop=sec_haloprop) #the average displacement acts as a normalization we need. #t1 = time() disp_average = compute_conditional_averages( self.disp_func, disp_func_kwargs, prim_haloprop=prim_haloprop, sec_haloprop=sec_haloprop - central_val) #t2 = time() #x = compute_conditional_percentiles(prim_haloprop=prim_haloprop, sec_haloprop=sec_haloprop-central_val) #t3 = time() #print type(self) #print t1-t0, t2-t1, t3-t2 bound1 = baseline_result / disp_average bound2 = (baseline_upper_bound - baseline_result) / (baseline_upper_bound - disp_average) #stop NaN broadcasting bound1[np.isnan(bound1)] = np.inf bound2[np.isnan(bound2)] = np.inf bound = np.minimum(bound1, bound2) result = strength * bound * (self.disp_func( sec_haloprop - central_val, **disp_func_kwargs) - disp_average) #print 'Perturbations' #print result.mean(),result.std(), result.max(), result.min() return result
#!/usr/bin/env python """Command-line script to rebuild the particle table cache log""" import argparse, os, fnmatch from astropy.table import Table import numpy as np from time import time try: import h5py except ImportError: msg = ( "\nMust have h5py installed to use the rebuild_ptcl_table_cache_log script.\n" ) raise HalotoolsError(msg) from halotools.custom_exceptions import HalotoolsError from halotools.sim_manager import PtclTableCache from halotools.sim_manager.ptcl_table_cache_log_entry import PtclTableCacheLogEntry old_cache = PtclTableCache() old_cache_log_exists = os.path.isfile(old_cache.cache_log_fname) cache_log_dirname = os.path.dirname(old_cache.cache_log_fname) corrupted_cache_log_basename = 'corrupted_ptcl_table_cache_log.txt' corrupted_cache_log_fname = os.path.join(cache_log_dirname, corrupted_cache_log_basename) rejected_filename_log_fname = 'rejected_ptcl_table_filenames.txt' rejected_filename_log_fname = os.path.join(cache_log_dirname, rejected_filename_log_fname)
def wrapper(*args, **kwargs): ################################################################################# # Retrieve the arrays storing prim_haloprop and sec_haloprop # The control flow below is what permits accepting an input # table or a directly inputting prim_haloprop and sec_haloprop arrays _HAS_table = False if 'table' in kwargs: try: table = kwargs['table'] prim_haloprop = table[self.prim_haloprop_key] sec_haloprop = table[self.sec_haloprop_key] _HAS_table = True except KeyError: msg = ( "When passing an input ``table`` to the " " ``assembias_decorator`` method,\n" "the input table must have a column with name ``%s``" "and a column with name ``%s``.\n") raise HalotoolsError(msg % (self.prim_haloprop_key), self.sec_haloprop_key) else: try: prim_haloprop = np.atleast_1d(kwargs['prim_haloprop']) except KeyError: msg = ("\nIf not passing an input ``table`` to the " "``assembias_decorator`` method,\n" "you must pass ``prim_haloprop`` argument.\n") raise HalotoolsError(msg) try: sec_haloprop = np.atleast_1d(kwargs['sec_haloprop']) except KeyError: if 'sec_haloprop_percentile' not in kwargs: msg = ("\nIf not passing an input ``table`` to the " "``assembias_decorator`` method,\n" "you must pass either a ``sec_haloprop`` or " "``sec_haloprop_percentile`` argument.\n") raise HalotoolsError(msg) ################################################################################# # Compute the fraction of type-2 halos as a function of the input prim_haloprop split = self.percentile_splitting_function(prim_haloprop) # Compute the baseline, undecorated result #t0 = time() result = func(*args, **kwargs) #t1 = time() #print 'Baseline time:',t1 - t0 # We will only decorate values that are not edge cases, # so first compute the mask for non-edge cases no_edge_mask = ((split > 0) & (split < 1) & (result > baseline_lower_bound) & (result < baseline_upper_bound)) # Now create convenient references to the non-edge-case sub-arrays no_edge_result = result[no_edge_mask] no_edge_split = split[no_edge_mask] #NOTE I've removed the type 1 mask as it is not necessary # this has all been rolled into the galprop_perturbation function if prim_haloprop[no_edge_mask].shape[0] == 0: perturbation = np.zeros_like(no_edge_result) else: perturbation = self._galprop_perturbation( prim_haloprop=prim_haloprop[no_edge_mask], sec_haloprop=sec_haloprop[no_edge_mask], baseline_result=no_edge_result, splitting_result=no_edge_split) no_edge_result += perturbation #print result.mean(), result.std(), result.max(), result.min() result[no_edge_mask] = no_edge_result #print 'End Wrapper' #print result.mean(), result.std(), result.max(), result.min() return result
def wrapper(*args, **kwargs): ################################################################################# # Retrieve the arrays storing prim_haloprop and sec_haloprop # The control flow below is what permits accepting an input # table or a directly inputting prim_haloprop and sec_haloprop arrays _HAS_table = False if 'table' in kwargs: try: table = kwargs['table'] prim_haloprop = table[self.prim_haloprop_key] sec_haloprop = table[self.sec_haloprop_key] _HAS_table = True except KeyError: msg = ( "When passing an input ``table`` to the " " ``assembias_decorator`` method,\n" "the input table must have a column with name ``%s``" "and a column with name ``%s``.\n") raise HalotoolsError(msg % (self.prim_haloprop_key), self.sec_haloprop_key) else: try: prim_haloprop = np.atleast_1d(kwargs['prim_haloprop']) except KeyError: msg = ("\nIf not passing an input ``table`` to the " "``assembias_decorator`` method,\n" "you must pass ``prim_haloprop`` argument.\n") raise HalotoolsError(msg) try: sec_haloprop = np.atleast_1d(kwargs['sec_haloprop']) except KeyError: msg = ("\nIf not passing an input ``table`` to the " "``assembias_decorator`` method,\n" "you must pass ``sec_haloprop`` argument") raise HalotoolsError(msg) ################################################################################# # Compute the percentile to split on as a function of the input prim_haloprop split = self.percentile_splitting_function(prim_haloprop) # Compute the baseline, undecorated result result = func(*args, **kwargs) # We will only decorate values that are not edge cases, # so first compute the mask for non-edge cases no_edge_mask = ((split > 0) & (split < 1) & (result > baseline_lower_bound) & (result < baseline_upper_bound)) # Now create convenient references to the non-edge-case sub-arrays no_edge_result = result[no_edge_mask] no_edge_split = split[no_edge_mask] # TODO i can maybe figure out how to cache the percentiles in the table, but for hte time being i'll just to retrieve them everytime if _HAS_table is True: if self.sec_haloprop_key + '_percentile_values' in table.keys( ): # no_edge_percentile_values = table[self.sec_haloprop_key + '_percentile_value'][no_edge_mask] pass else: # the value of sec_haloprop_percentile will be computed from scratch #no_edge_percentile_values = compute_conditional_percentile_values( p=no_edge_split, # prim_haloprop=prim_haloprop[no_edge_mask], # sec_haloprop=sec_haloprop[no_edge_mask] #) pass else: pass ''' try: percentiles = kwargs['sec_haloprop_percentile_values'] if custom_len(percentiles) == 1: percentiles = np.zeros(custom_len(prim_haloprop)) + percentiles no_edge_percentile_values = percentiles[no_edge_mask] except KeyError: no_edge_percentile_values = compute_conditional_percentile_values(p=no_edge_split, prim_haloprop=prim_haloprop[no_edge_mask], sec_haloprop=sec_haloprop[no_edge_mask] ) ''' # NOTE I've removed the type 1 mask as it is not well-defined in this implementation strength = self.assembias_strength(prim_haloprop[no_edge_mask]) shuffled_ranks = compute_conditional_shuffled_ranks( prim_haloprop=prim_haloprop[no_edge_mask], sec_haloprop=sec_haloprop[no_edge_mask], correlation_coeff=strength) dist = bernoulli if baseline_upper_bound == 1 else poisson return dist.isf(1 - shuffled_ranks, mu=result)
def compute_conditional_averages(disp_func=lambda x: x, disp_func_kwargs={}, **kwargs): """ In bins of the ``prim_haloprop``, compute the average value of disp_func given the input ``table`` based on the value of ``sec_haloprop``. Parameters ---------- disp_func: function, optional A kwarg that is the function to calculate the conditional average of. Default is 'lambda x: x' which will compute the average value of sec_haloprop in bins of prim_haloprop disp_func_kwargs: dictionary, optional kwargs for the disp_func. Default is an empty dictionary table : astropy table, optional a keyword argument that stores halo catalog being used to make mock galaxy population If a `table` is passed, the `prim_haloprop_key` and `sec_haloprop_key` keys must also be passed. If not passing a `table`, you must directly pass the `prim_haloprop` and `sec_haloprop` keyword arguments. prim_haloprop_key : string, optional Name of the column of the input ``table`` that will be used to access the primary halo property. `compute_conditional_percentiles` bins the ``table`` by ``prim_haloprop_key`` when computing the result. sec_haloprop_key : string, optional Name of the column of the input ``table`` that will be used to access the secondary halo property. `compute_conditional_percentiles` bins the ``table`` by ``prim_haloprop_key``, and in each bin uses the value stored in ``sec_haloprop_key`` to compute the ``prim_haloprop``-conditioned rank-order percentile. prim_haloprop : array_like, optional Array storing the primary halo property used to bin the input points. If a `prim_haloprop` is passed, you must also pass a `sec_haloprop`. sec_haloprop : array_like, optional Array storing the secondary halo property used to define the conditional percentiles in each bin of `prim_haloprop`. prim_haloprop_bin_boundaries : array, optional Array defining the boundaries by which we will bin the input ``table``. Default is None, in which case the binning will be automatically determined using the ``dlog10_prim_haloprop`` keyword. dlog10_prim_haloprop : float, optional Logarithmic spacing of bins of the mass-like variable within which we will assign secondary property percentiles. Default is 0.2. Examples -------- >>> from halotools.sim_manager import FakeSim >>> fakesim = FakeSim() >>> result = compute_conditional_percentiles(table = fakesim.halo_table, prim_haloprop_key = 'halo_mvir', sec_haloprop_key = 'halo_vmax') Notes ----- The sign of the result is such that in bins of the primary property, *smaller* values of the secondary property receive *smaller* values of the returned percentile. """ if 'table' in kwargs: table = kwargs['table'] try: prim_haloprop_key = kwargs['prim_haloprop_key'] prim_haloprop = table[prim_haloprop_key] sec_haloprop_key = kwargs['sec_haloprop_key'] sec_haloprop = table[sec_haloprop_key] except KeyError: msg = ( "\nWhen passing an input ``table`` to the ``compute_conditional_percentiles`` method,\n" "you must also pass ``prim_haloprop_key`` and ``sec_haloprop_key`` keyword arguments\n" "whose values are column keys of the input ``table``\n") raise HalotoolsError(msg) else: try: prim_haloprop = kwargs['prim_haloprop'] sec_haloprop = kwargs['sec_haloprop'] except KeyError: msg = ( "\nIf not passing an input ``table`` to the ``compute_conditional_percentiles`` method,\n" "you must pass a ``prim_haloprop`` and ``sec_haloprop`` arguments\n" ) raise HalotoolsError(msg) compute_prim_haloprop_bins_dict = {} compute_prim_haloprop_bins_dict['prim_haloprop'] = prim_haloprop try: compute_prim_haloprop_bins_dict['prim_haloprop_bin_boundaries'] = ( kwargs['prim_haloprop_bin_boundaries']) except KeyError: pass try: compute_prim_haloprop_bins_dict['dlog10_prim_haloprop'] = kwargs[ 'dlog10_prim_haloprop'] except KeyError: pass prim_haloprop_bins = compute_prim_haloprop_bins( **compute_prim_haloprop_bins_dict) output = np.zeros_like(prim_haloprop) # sort on secondary property only with each mass bin bins_in_halocat = set(prim_haloprop_bins) for ibin in bins_in_halocat: indices_of_prim_haloprop_bin = np.where(prim_haloprop_bins == ibin)[0] # place the percentiles into the catalog output[indices_of_prim_haloprop_bin] = np.mean( disp_func(sec_haloprop[indices_of_prim_haloprop_bin], **disp_func_kwargs)) #TODO i'm not sure if this should have dimensions of prim_haloprop or the binning... return output