Source code for drtsans.determine_bins

import numpy as np
from collections import namedtuple


# Define structure (namedtuple) for binning parameters: min, max, number of bins
# bins shall be integer as number of bins
BinningParams = namedtuple("BinningParams", "min max bins")
# Define structure (namedtuple) for bins: bin edges and bin boundaries
# Both bin edge and bin boundaries shall be 1-dimensional 1D array and 'edges' size is 1 larger than centers
Bins = namedtuple("Bins", "edges centers")


[docs] def determine_1d_linear_bins(x_min, x_max, bins): """Determine linear bin edges and centers Parameters ---------- x_min : float Q min of bin edge x_max : float or None Q max of bin edge bins : integer number of bins Returns ------- ~drtsans.iq.Bins Bins including bin centers and bin edges """ # Check input x min and x max if x_min is None or x_max is None or x_min >= x_max: raise RuntimeError( "x min {} and x max {} must not be None and x min shall be less than x max" "".format(x_min, x_max) ) # force the number of bins to be an integer and error check it bins = int(bins) if bins <= 0: raise ValueError("Encountered illegal number of bins: {}".format(bins)) # Calculate Q step size delta_x = float((x_max - x_min) / bins) # Determine bin edges bin_edges = np.arange(bins + 1).astype("float") * delta_x + x_min # Determine bin centers from edges bin_centers = (bin_edges[1:] + bin_edges[:-1]) * 0.5 # Construct Bins instance linear_bins = Bins(bin_edges, bin_centers) return linear_bins
[docs] def determine_1d_log_bins(x_min, x_max, decade_on_center, n_bins_per_decade=None, n_bins=None): """ Parameters ---------- x_min: float minimum value of X in the bins x_max: float maximum value of X in the bins decade_on_center: bool flag that data must be centered on decades n_bins_per_decade: int, None density of points (number of data points per decade) n_bins: int, None total number of points in the output Returns ------- """ # Check inputs if n_bins_per_decade is None and n_bins is None: raise RuntimeError( "Density of points (n_bins_per_decade) and total number of bins (n_bins) cannot be " "None simultaneously. One and only one of them must be specified." ) elif n_bins_per_decade is not None and n_bins is not None: raise RuntimeError( "Density of points (n_bins_per_decade) and total number of bins (n_bins) cannot be " "specified simultaneously. One and only one of them must be specified." ) # only allow either n_bins or n_bins_per_decade # Calculate Q min, number of total bins and number of steps if n_bins_per_decade is not None: # user specifies number of bins per decade # determine X min if decade_on_center: x_ref = _calculate_x_ref(x_min, n_bins_per_decade) else: x_ref = x_min # calculate step size n_step = 10 ** (1 / n_bins_per_decade) # calculate number of bins n_bins = _calculate_n_bins(x_ref, x_max, n_step) else: # user specifies number of total bins # case that is not supported if decade_on_center: assert n_bins_per_decade is not None, ( "For option decade_on_center, number of bins per decade " "is required" ) x_ref = x_min # calculate bin step size # Equation 11.33 n_step = 10 ** ((np.log10(x_max / x_ref)) / (n_bins - 1)) # Calculate kay kay = (n_step - 1) / (n_step + 1) # Calculate bin centers # init an array ranging from 0 to (n_bins - 1) bin_centers = np.arange(n_bins).astype("float64") # Equation 11.34: Q_k = Q_ref * 10^(k * delta L) bin_centers = x_ref * n_step**bin_centers # Calculate bin edges (aka boundaries) # Equation 11.35 bin_edges = np.ndarray(shape=(n_bins + 1,), dtype="float64") # calculate left edges (i.e., right edges except last one), i.e., Q_{k-1, max} = Q_{k, min} bin_edges[:-1] = bin_centers[:] - kay * bin_centers[:] # calculate last right edge bin_edges[-1] = bin_centers[-1] + kay * bin_centers[-1] # Form output as Bins object log_bins = Bins(bin_edges, bin_centers) return log_bins
def _calculate_x_ref(x_min, n_bins_per_decade): """Calculate reference X (minimum X) by Equation in Chapter 11 x_ref = 10^((1/N_bins_decade)*(round(N_bins_decade*log10(Q_min)))) Parameters ---------- x_min: float minimum x value n_bins_per_decade: int point density Returns ------- """ ref_x = 10 ** ((1.0 / n_bins_per_decade) * (np.round(n_bins_per_decade * np.log10(x_min)))) return ref_x def _calculate_n_bins(x_min, x_max, n_step): """Calculate number of total bins by implementing Equation 11.32 N_bins = floor(ceil((log10(Q_max/Calc_Q_min) + log10((N_step+1)/2.0))/log10(N_step))) Parameters ---------- x_min: float minimum value of x to be included in the bin x_max: float maximum value of x to be included in the bin n_step: float step size Returns ------- """ n_bins = np.floor(np.ceil((np.log10(x_max / x_min) + np.log10((n_step + 1) * 0.5)) / np.log10(n_step))) # to avoid round off error such that n_bins = |n_bins| + epsilon, where epsilon is an infinitesimally # small value n_bins = int(n_bins + 1e-5) return n_bins