Source code for slsim.Util.catalog_util

import numpy as np

# This file contains functions commonly used by the CosmosWebCatalog and HSTCosmosCatalog
# classes in Sources/SourceCatalogues/


[docs] def match_source( angular_size, physical_size, axis_ratio, n_sersic, processed_catalog, max_scale=1, match_n_sersic=False, ): """This function matches the parameters in source_dict to find a corresponding source in a source catalog. The parameters being matched are: 1. physical size 2. axis ratio 3. n_sersic only if match_n_sersic is True Each parameter being matched is normalized so that the max is 1 and min is 0. Matching is then performed by selecting the nearest point in 2D space (3D if match_n_sersic is True). :param angular_size: desired angular size of the source [arcsec] :param physical_size: desired physical size of the source [kpc] :param axis_ratio: desired axis ratio :param sersic_angle: desired sersic angle :param processed_catalog: the returned object from calling process_catalog() See e.g. slsim/Sources/SourceCatalogues HSTCosmosCatalog or CosmosWebCatalog :param max_scale: The matched image will be scaled to have the desired angular size. Scaling up results in a more pixelated image. This input determines what the maximum up-scale factor is. :type max_scale: int or float :param match_n_sersic: determines whether to match based off of the sersic index as well. Since n_sersic is usually undefined and set to 1 in SLSim, this is set to False by default. :type match_n_sersic: bool :return: row of astropy table corresponding to the matched source in the catalog """ # Later, the matched image will be scaled to match angular size # We want to avoid upscaling to prevent pixelization of the image processed_catalog = processed_catalog[ angular_size <= processed_catalog["angular_size"].data * max_scale ] if len(processed_catalog) == 0: return None physical_sizes = np.append(processed_catalog["physical_size"].data, physical_size) physical_sizes = normalize_features(physical_sizes, norm_type="minmax") axis_ratios = np.append(processed_catalog["axis_ratio"].data, axis_ratio) axis_ratios = normalize_features(axis_ratios, norm_type="minmax") distances = (physical_sizes[:-1] - physical_sizes[-1]) ** 2 + ( axis_ratios[:-1] - axis_ratios[-1] ) ** 2 if match_n_sersic: n_sersics = np.append(processed_catalog["sersic_index"].data, n_sersic) n_sersics = normalize_features(n_sersics, norm_type="minmax") distances += (n_sersics[:-1] - n_sersics[-1]) ** 2 matched_source = processed_catalog[np.argmin(distances)] return matched_source
[docs] def normalize_features(data, norm_type="zscore", data_min=None, data_max=None): """Normalizes a 1D array of data. :param data: 1d array of data :param norm_type: string indicating the type of normalization to apply :param data_min: minimum value of the data (can be used to override the default scaling) :param data_max: maximum value of the data (can be used to override the default scaling) :return: normalized 1d array of data """ data = np.array(data, dtype=float) if norm_type == "minmax": d_min = data_min if data_min is not None else np.nanmin(data) d_max = data_max if data_max is not None else np.nanmax(data) if d_max == d_min: # Prevent division by zero return np.zeros_like(data) return (data - d_min) / (d_max - d_min) elif norm_type == "zscore": mean = np.nanmean(data) std = np.nanstd(data) if std == 0: return np.zeros_like(data) return (data - mean) / std else: raise ValueError("Unsupported normalization type. Use 'minmax' or 'zscore'.")
[docs] def safe_value(val): """This function ensures that a value that we put into a pandas DataFrame is safe, i.e doesn't have mismatched datatypes. :param val: value to store in df :type val: string or float or list or array :return: safe value """ if isinstance(val, np.ndarray): # Ensure native byte order if hasattr(val, "dtype") and val.dtype.byteorder not in ("=", "|"): val = val.astype(val.dtype.newbyteorder("=")) # If array has one element, convert to float if val.size == 1: return float(val) elif isinstance(val, np.generic): return float(val) return val