diff --git a/.github/workflows/build_check.yml b/.github/workflows/build_check.yml index 256e3b42..a7ca2821 100644 --- a/.github/workflows/build_check.yml +++ b/.github/workflows/build_check.yml @@ -43,16 +43,16 @@ jobs: git clone https://github.com/LSSTDESC/healsparse cd healsparse pip install . - - name: Analysing the code with pylint - run: | - pip install pylint - pylint clevar --ignored-classes=astropy.units - name: Run the unit tests run: | pip install pytest pytest-cov pytest tests/ --cov=clevar/ env: DISPLAY: test + - name: Analysing the code with pylint + run: | + pip install pylint + pylint clevar --ignored-classes=astropy.units - name: Install Sphinx prereq run: | conda install -c conda-forge sphinx sphinx_rtd_theme nbconvert>=7 pandoc ipython ipython_genutils diff --git a/clevar/match_metrics/plot_helper.py b/clevar/match_metrics/plot_helper.py index 804c18e1..7d854a35 100644 --- a/clevar/match_metrics/plot_helper.py +++ b/clevar/match_metrics/plot_helper.py @@ -140,7 +140,9 @@ def get_bin_label(edge_lower, edge_higher, format_func=lambda v: v, prefix=""): return f"${prefix}[{format_func(edge_lower)}$ : ${format_func(edge_higher)}]$" -def add_panel_bin_label(axes, edges_lower, edges_higher, format_func=lambda v: v, prefix=""): +def add_panel_bin_label( + axes, edges_lower, edges_higher, format_func=lambda v: v, prefix="", position="top" +): """ Adds label with bin range on the top of panel @@ -156,11 +158,25 @@ def add_panel_bin_label(axes, edges_lower, edges_higher, format_func=lambda v: v Function to format the values of the bins prefix: str Prefix to add to labels + position: str + Position od the panel, must be in: top, bottom, left, right """ - for ax, val_lower, val_higher in zip(axes.flatten(), edges_lower, edges_higher): - topax = ax.twiny() - topax.set_xticks([]) - topax.set_xlabel(get_bin_label(val_lower, val_higher, format_func, prefix)) + for ax, vb, vt in zip(axes.flatten(), edges_lower, edges_higher): + if position == "top": + use_ax = ax.twiny() + use_ax.set_xticks([]) + set_label = use_ax.set_xlabel + elif position == "bottom": + set_label = ax.set_xlabel + elif position == "left": + set_label = ax.set_ylabel + elif position == "right": + use_ax = ax.twinx() + use_ax.set_yticks([]) + set_label = use_ax.set_ylabel + else: + raise ValueError(f"position (={position}) must be in: top, bottom, left, right") + set_label(get_bin_label(vb, vt, format_func, prefix)) def get_density_colors( diff --git a/clevar/match_metrics/recovery/__init__.py b/clevar/match_metrics/recovery/__init__.py index 5f50c0b7..674e423d 100644 --- a/clevar/match_metrics/recovery/__init__.py +++ b/clevar/match_metrics/recovery/__init__.py @@ -4,4 +4,4 @@ from . import array_funcs as ArrayFuncs from . import catalog_funcs as ClCatalogFuncs -from .funcs import plot, plot_panel, plot2D, skyplot +from .funcs import plot, plot_panel, plot2D, skyplot, plot_fscore diff --git a/clevar/match_metrics/recovery/array_funcs.py b/clevar/match_metrics/recovery/array_funcs.py index e6df7c77..f952fb3e 100644 --- a/clevar/match_metrics/recovery/array_funcs.py +++ b/clevar/match_metrics/recovery/array_funcs.py @@ -5,7 +5,7 @@ import numpy as np import healpy as hp -from ...utils import none_val, updated_dict +from ...utils import none_val, updated_dict, index_list from .. import plot_helper as ph from ..plot_helper import plt @@ -403,3 +403,252 @@ def skyplot( info = {"fig": fig, "nc_pix": all_pix, "nc_mt_pix": mt_pix} return info + + +def get_fscore( + cat1_values1, + cat1_values2, + cat1_bins1, + cat1_bins2, + cat1_is_matched, + cat2_values1, + cat2_values2, + cat2_bins1, + cat2_bins2, + cat2_is_matched, + beta=1, + pref="cat1", +): + """ + Computes fscore + + Parameters + ---------- + cat1_values1, cat2_values2: array + Component 1 and 2 of catalog 1. + cat1_bins1, cat1_bins2: array, int + Bins for components 1 and 2 of catalog 1. + cat1_is_matched: array (boolean) + Boolean array indicating matching status of catalog 1. + cat2_values1, cat2_values2: array + Component 1 and 2 of catalog 2. + cat2_bins1, cat2_bins2: array, int + Bins for components 1 and 2 of catalog 2. + cat2_is_matched: array (boolean) + Boolean array indicating matching status of catalog 2. + beta: float + Additional recall weight in f-score + pref: str + Peference to which recovery rate beta is applied, must be cat1 or cat2. + + + Returns + ------- + dict + Binned fscore and recovery rate. Its sections are: + + * `fscore`: F-n score binned with (cat2_bins1, cat2_bins2, cat1_bins1, cat1_bins2). + * `cat1`: Dictionary with recovery rate of catalog 1, see get_recovery_rate for info. + * `cat2`: Dictionary with recovery rate of catalog 2, see get_recovery_rate for info. + """ + rec1 = get_recovery_rate(cat1_values1, cat1_values2, cat1_bins1, cat1_bins2, cat1_is_matched) + rec2 = get_recovery_rate(cat2_values1, cat2_values2, cat2_bins1, cat2_bins2, cat2_is_matched) + + r1_grid = np.outer(np.ones(rec2["recovery"].size), rec1["recovery"].flatten()) + r2_grid = np.outer(rec2["recovery"].flatten(), np.ones(rec1["recovery"].size)) + + beta2 = beta**2 + fscore = (1 + beta2) * r1_grid * r2_grid + if pref == "cat1": + fscore /= beta2 * r1_grid + r2_grid + elif pref == "cat2": + fscore /= r1_grid + beta2 * r2_grid + else: + raise ValueError(f"pref (={pref}) must be cat1 or cat2") + return { + "fscore": fscore.reshape(*rec2["recovery"].shape, *rec1["recovery"].shape), + "cat1": rec1, + "cat2": rec2, + } + + +def plot_fscore( + cat1_val1, + cat1_val2, + cat1_bins1, + cat1_bins2, + cat1_is_matched, + cat2_val1, + cat2_val2, + cat2_bins1, + cat2_bins2, + cat2_is_matched, + beta=1, + pref="cat1", + par_order=(0, 1, 2, 3), + shape="steps", + plt_kwargs=None, + lines_kwargs_list=None, + fig_kwargs=None, + legend_kwargs=None, + cat1_val1_label=None, + cat1_val2_label=None, + cat2_val1_label=None, + cat2_val2_label=None, + cat1_datalabel1_format=lambda v: v, + cat1_datalabel2_format=lambda v: v, + cat2_datalabel1_format=lambda v: v, + cat2_datalabel2_format=lambda v: v, +): + """ + Plot recovery rate as lines in panels, with each line binned by bins1 + and each panel is based on the data inside a bins2 bin. + + Parameters + ---------- + cat1_values1, cat2_values2: array + Component 1 and 2 of catalog 1. + cat1_bins1, cat1_bins2: array, int + Bins for components 1 and 2 of catalog 1. + cat1_is_matched: array (boolean) + Boolean array indicating matching status of catalog 1. + cat2_values1, cat2_values2: array + Component 1 and 2 of catalog 2. + cat2_bins1, cat2_bins2: array, int + Bins for components 1 and 2 of catalog 2. + cat2_is_matched: array (boolean) + Boolean array indicating matching status of catalog 2. + beta: float + Additional recall weight in f-score + pref: str + Peference to which recovery rate beta is applied, must be cat1 or cat2. + par_order: list, bool + It transposes quantities used, must be a percolation of (0, 1, 2, 3). + shape: str + Shape of the lines. Can be steps or line. + plt_kwargs: dict + Additional arguments for pylab.plot + lines_kwargs_list: list, None + List of additional arguments for plotting each line (using pylab.plot). + Must have same size as len(bins2)-1 + fig_kwargs: dict + Additional arguments for plt.subplots + legend_kwargs: dict + Additional arguments for pylab.legend + cat1_val1_label, cat1_val2_label: str + Labels for quantities 1 and 2 of catalog 1. + cat2_val1_label, cat2_val2_label: str + Labels for quantities 1 and 2 of catalog 2. + cat1_datalabel1_format, cat1_datalabel2_format: function + Function to format the values of labels for catalog 1. + cat2_datalabel1_format, cat2_datalabel2_format: function + Function to format the values of labels for catalog 2. + + + Returns + ------- + info: dict + Information of data in the plots, it contains the sections: + + * `fig`: `matplotlib.figure.Figure` object. + * `axes`: `matplotlib.axes` used in the plot. + * `data`: Binned data used in the plot. It has the sections: + + * `fscore`: F-n score binned with (cat2_bins1, cat2_bins2, cat1_bins1, cat1_bins2). + * `cat1`: Dictionary with recovery rate of catalog 1, see get_recovery_rate. + * `cat2`: Dictionary with recovery rate of catalog 2, see get_recovery_rate. + """ + # pylint: disable=too-many-locals + info = { + "data": get_fscore( + cat1_val1, + cat1_val2, + cat1_bins1, + cat1_bins2, + cat1_is_matched, + cat2_val1, + cat2_val2, + cat2_bins1, + cat2_bins2, + cat2_is_matched, + beta=beta, + pref=pref, + ) + } + + # Order of parameters + edges = index_list( + [ + info["data"]["cat1"]["edges1"], + info["data"]["cat1"]["edges2"], + info["data"]["cat2"]["edges1"], + info["data"]["cat2"]["edges2"], + ], + par_order, + ) + labels = index_list( + [cat1_val1_label, cat1_val2_label, cat2_val1_label, cat2_val2_label], par_order + ) + label_formats = index_list( + [ + cat1_datalabel1_format, + cat1_datalabel2_format, + cat2_datalabel1_format, + cat2_datalabel2_format, + ], + par_order, + ) + tr_order = index_list([2, 3, 0, 1], index_list(par_order, [2, 3, 0, 1])) + + ni = edges[2].size - 1 + nj = edges[3].size - 1 + info.update( + dict( + zip( + ("fig", "axes"), + plt.subplots( + ni, + nj, + **updated_dict({"sharex": True, "sharey": True, "figsize": (8, 6)}, fig_kwargs), + ), + ) + ) + ) + add_legend = True + for axl, fscl in zip(info["axes"], info["data"]["fscore"].transpose(*tr_order)): + for ax, fsc in zip(axl, fscl): + ph.add_grid(ax) + ph.plot_histograms( + fsc.T, + edges[0], + edges[1], + ax=ax, + shape=shape, + plt_kwargs=plt_kwargs, + lines_kwargs_list=lines_kwargs_list, + add_legend=add_legend, + legend_format=label_formats[1], + legend_kwargs=legend_kwargs, + ) + add_legend = False + for ax in info["axes"][:, 0]: + ax.set_ylabel(f"$F_{{{beta}}}$ score") + for ax in info["axes"][-1, :]: + ax.set_xlabel(f"${labels[0]}$") + ph.add_panel_bin_label( + info["axes"], + edges[3][:-1], + edges[3][1:], + prefix="" if labels[3] is None else f"{labels[3]}: ", + format_func=label_formats[3], + ) + ph.add_panel_bin_label( + info["axes"][:, -1], + edges[2][:-1], + edges[2][1:], + prefix="" if labels[2] is None else f"{labels[2]}: ", + format_func=label_formats[2], + position="right", + ) + + return info diff --git a/clevar/match_metrics/recovery/catalog_funcs.py b/clevar/match_metrics/recovery/catalog_funcs.py index 7afa31f3..e2784876 100644 --- a/clevar/match_metrics/recovery/catalog_funcs.py +++ b/clevar/match_metrics/recovery/catalog_funcs.py @@ -3,7 +3,7 @@ Main recovery functions using catalogs, wrapper of array_funcs functions """ import numpy as np -from ...utils import none_val +from ...utils import none_val, updated_dict, subdict from .. import plot_helper as ph from . import array_funcs @@ -417,3 +417,227 @@ def skyplot( figsize=figsize, **kwargs, ) + + +def _plot_fscore_base( + pltfunc, + cat1, + cat1_col1, + cat1_col2, + cat1_bins1, + cat1_bins2, + cat2, + cat2_col1, + cat2_col2, + cat2_bins1, + cat2_bins2, + matching_type, + cat1_mask=None, + cat1_mask_unmatched=None, + cat2_mask=None, + cat2_mask_unmatched=None, + **kwargs, +): + """ + Adapts local function to use a ArrayFuncs function. + + Parameters + ---------- + pltfunc: function + ArrayFuncs function + cat: clevar.ClCatalog + ClCatalog with matching information + col1, col2: str + Names of columns 1 and 2. + bins1, bins2: array, int + Bins for components 1 and 2. + matching_type: str + Type of matching to be considered. Must be in: + 'cross', 'cat1', 'cat2', 'multi_cat1', 'multi_cat2', 'multi_join' + cat1_mask: array + Mask of unwanted clusters of catalog 1. + cat1_mask_unmatched: array + Mask of unwanted unmatched clusters (ex: out of footprint) of catalog 1. + cat2_mask: array + Mask of unwanted clusters of catalog 2. + cat2_mask_unmatched: array + Mask of unwanted unmatched clusters (ex: out of footprint) of catalog 2. + **kwargs: + Additional arguments to be passed to pltfunc + + Returns + ------- + Same as pltfunc + """ + # pylint: disable=too-many-locals + c1_mask, c1_is_matched = _rec_masks(cat1, matching_type, cat1_mask, cat1_mask_unmatched) + c2_mask, c2_is_matched = _rec_masks(cat2, matching_type, cat2_mask, cat2_mask_unmatched) + # make sure bins stay consistent regardless of mask + c1_edges1, c1_edges2 = np.histogram2d( + cat1[cat1_col1], cat1[cat1_col2], bins=(cat1_bins1, cat1_bins2) + )[1:] + c2_edges1, c2_edges2 = np.histogram2d( + cat2[cat2_col1], cat2[cat2_col2], bins=(cat2_bins1, cat2_bins2) + )[1:] + return pltfunc( + cat1[cat1_col1][c1_mask], + cat1[cat1_col2][c1_mask], + c1_edges1, + c1_edges2, + c1_is_matched[c1_mask], + cat2[cat2_col1][c2_mask], + cat2[cat2_col2][c2_mask], + c2_edges1, + c2_edges2, + c2_is_matched[c2_mask], + **kwargs, + ) + + +def plot_fscore( + cat1, + cat1_col1, + cat1_col2, + cat1_bins1, + cat1_bins2, + cat2, + cat2_col1, + cat2_col2, + cat2_bins1, + cat2_bins2, + matching_type, + beta=1, + pref="cat1", + par_order=(0, 1, 2, 3), + cat1_mask=None, + cat1_mask_unmatched=None, + cat2_mask=None, + cat2_mask_unmatched=None, + xlabel=None, + ylabel=None, + xscale="linear", + **kwargs, +): + """ + Plot recovery rate as lines, with each line binned by bins1 inside a bin of bins2. + + Parameters + ---------- + cat1: clevar.ClCatalog + ClCatalog with matching information + cat1_col1, cat1_col2: str + Names of columns 1 and 2 of catalog 1. + cat1_bins1, cat1_bins2: array, int + Bins for components 1 and 2 of catalog 1. + cat2: clevar.ClCatalog + ClCatalog with matching information + cat2_col1, cat2_col2: str + Names of columns 1 and 2 of catalog 2. + cat2_bins1, cat2_bins2: array, int + Bins for components 1 and 2 of catalog 2. + matching_type: str + Type of matching to be considered. Must be in: + 'cross', 'cat1', 'cat2', 'multi_cat1', 'multi_cat2', 'multi_join' + beta: float + Additional recall weight in f-score + pref: str + Peference to which recovery rate beta is applied, must be cat1 or cat2. + par_order: list, bool + It transposes quantities used, must be a percolation of (0, 1, 2, 3). + cat1_mask: array + Mask of unwanted clusters of catalog 1. + cat1_mask_unmatched: array + Mask of unwanted unmatched clusters (ex: out of footprint) of catalog 1. + cat2_mask: array + Mask of unwanted clusters of catalog 2. + cat2_mask_unmatched: array + Mask of unwanted unmatched clusters (ex: out of footprint) of catalog 2. + + Other parameters + ---------------- + shape: str + Shape of the lines. Can be steps or line. + xlabel: str + Label of component 1. Default is col1. + ylabel: str + Label of recovery rate. + xscale: str + Scale of x axis. + plt_kwargs: dict + Additional arguments for pylab.plot + fig_kwargs: dict + Additional arguments for plt.subplots + lines_kwargs_list: list, None + List of additional arguments for plotting each line (using pylab.plot). + Must have same size as len(bins2)-1 + legend_kwargs: dict + Additional arguments for pylab.legend + cat1_val1_label, cat1_val2_label: str + Labels for quantities 1 and 2 of catalog 1. + cat2_val1_label, cat2_val2_label: str + Labels for quantities 1 and 2 of catalog 2. + cat1_datalabel1_format, cat1_datalabel2_format: function + Function to format the values of labels for catalog 1. + cat2_datalabel1_format, cat2_datalabel2_format: function + Function to format the values of labels for catalog 2. + + Returns + ------- + info: dict + Information of data in the plots, it contains the sections: + + * `fig`: `matplotlib.figure.Figure` object. + * `axes`: `matplotlib.axes` used in the plot. + * `data`: Binned data used in the plot. It has the sections: + + * `fscore`: F-n score binned with (cat2_bins1, cat2_bins2, cat1_bins1, cat1_bins2). + * `cat1`: Dictionary with recovery rate of catalog 1, see get_recovery_rate. + * `cat2`: Dictionary with recovery rate of catalog 2, see get_recovery_rate. + """ + # pylint: disable=too-many-locals + # pylint: disable=unused-argument + info = _plot_fscore_base( + array_funcs.plot_fscore, + **subdict( + locals(), + ( + "cat1", + "cat1_col1", + "cat1_col2", + "cat1_bins1", + "cat1_bins2", + "cat2", + "cat2_col1", + "cat2_col2", + "cat2_bins1", + "cat2_bins2", + "matching_type", + "beta", + "pref", + "par_order", + ), + ), + **updated_dict( + { + "cat1_val1_label": cat1.labels[cat1_col1], + "cat1_val2_label": cat1.labels[cat1_col2], + "cat2_val1_label": cat2.labels[cat2_col1], + "cat2_val2_label": cat2.labels[cat2_col2], + "cat1_mask": cat1_mask, + "cat1_mask_unmatched": cat1_mask_unmatched, + "cat2_mask": cat2_mask, + "cat2_mask_unmatched": cat2_mask_unmatched, + }, + kwargs, + ), + ) + if xlabel: + for ax in info["axes"][-1]: + ax.set_xlabel(xlabel) + if ylabel: + for ax in info["axes"][:, 0]: + ax.set_ylabel(ylabel) + for ax in info["axes"].flatten(): + ax.set_xscale(xscale) + ax.set_ylim(-0.01, 1.05) + return info diff --git a/clevar/match_metrics/recovery/funcs.py b/clevar/match_metrics/recovery/funcs.py index ecee37df..d2a5d4bb 100644 --- a/clevar/match_metrics/recovery/funcs.py +++ b/clevar/match_metrics/recovery/funcs.py @@ -554,3 +554,161 @@ def skyplot( figsize=figsize, **kwargs, ) + + +def plot_fscore( + cat1, + cat1_redshift_bins, + cat1_mass_bins, + cat2, + cat2_redshift_bins, + cat2_mass_bins, + matching_type, + beta=1, + pref="cat1", + par_order=(0, 1, 2, 3), + cat1_mask=None, + cat1_mask_unmatched=None, + cat2_mask=None, + cat2_mask_unmatched=None, + log_mass=True, + fscore_label=None, + **kwargs, +): + """ + Plot recovery rate as lines in panels, with each line binned by redshift + and each panel is based on the data inside a mass bin. + + Parameters + ---------- + cat1: clevar.ClCatalog + ClCatalog with matching information + cat1_redshift_bins: array, int + Bins for redshift of catalog 1. + cat1_mass_bins: array, int + Bins for mass of catalog 1. + cat2: clevar.ClCatalog + ClCatalog with matching information + cat2_redshift_bins: array, int + Bins for redshift of catalog 2. + cat2_mass_bins: array, int + Bins for mass of catalog 2. + matching_type: str + Type of matching to be considered. Must be in: + 'cross', 'cat1', 'cat2', 'multi_cat1', 'multi_cat2', 'multi_join' + beta: float + Additional recall weight in f-score + pref: str + Peference to which recovery rate beta is applied, must be cat1 or cat2. + par_order: list, bool + It transposes quantities used, must be a percolation of (0, 1, 2, 3). + log_mass: bool + Plot mass in log scale + cat1_mask: array + Mask of unwanted clusters of catalog 1. + cat1_mask_unmatched: array + Mask of unwanted unmatched clusters (ex: out of footprint) of catalog 1. + cat2_mask: array + Mask of unwanted clusters of catalog 2. + cat2_mask_unmatched: array + Mask of unwanted unmatched clusters (ex: out of footprint) of catalog 2. + + Other parameters + ---------------- + shape: str + Shape of the lines. Can be steps or line. + cat1_redshift_label, cat1_mass_label: str + Label for redshift and mass of catalog 1. + cat2_redshift_label, cat2_mass_label: str + Label for redshift and mass of catalog 2. + fscore_label: str + Label for fscore. + plt_kwargs: dict + Additional arguments for pylab.plot + fig_kwargs: dict + Additional arguments for plt.subplots + lines_kwargs_list: list, None + List of additional arguments for plotting each line (using pylab.plot). + Must have same size as len(bins2)-1 + legend_kwargs: dict + Additional arguments for pylab.legend + cat1_val1_label, cat1_val2_label: str + Labels for quantities 1 and 2 of catalog 1. + cat2_val1_label, cat2_val2_label: str + Labels for quantities 1 and 2 of catalog 2. + cat1_redshift_format, cat1_mass_format: function + Function to format the values of labels for catalog 1. + cat2_redshift_format, cat2_mass_format: function + Function to format the values of labels for catalog 2. + cat1_redshift_fmt, cat1_mass_fmt: str + Format the values of labels for catalog 1 (ex: '.2f'). + cat2_redshift_fmt, cat2_mass_fmt: str + Format the values of labels for catalog 2 (ex: '.2f'). + + Returns + ------- + info: dict + Information of data in the plots, it contains the sections: + + * `fig`: `matplotlib.figure.Figure` object. + * `axes`: `matplotlib.axes` used in the plot. + * `data`: Binned data used in the plot. It has the sections: + + * `fscore`: F-n score binned with (cat2_redshift_bins, cat2_mass_bins, \ + cat1_redshift_bins, cat1_mass_bins). + * `cat1`: Dictionary with recovery rate of catalog 1, see get_recovery_rate. + * `cat2`: Dictionary with recovery rate of catalog 2, see get_recovery_rate. + """ + kwargs_ = {} + kwargs_.update(kwargs) + # labels + kwargs_["cat1_val1_label"] = kwargs_.pop("cat1_redshift_label", cat1.labels["z"]) + kwargs_["cat1_val2_label"] = kwargs_.pop("cat1_mass_label", cat1.labels["mass"]) + kwargs_["cat2_val1_label"] = kwargs_.pop("cat2_redshift_label", cat2.labels["z"]) + kwargs_["cat2_val2_label"] = kwargs_.pop("cat2_mass_label", cat2.labels["mass"]) + # label formats + # pylint: disable=protected-access + for i in "12": + ph._set_label_format( + kwargs_, + f"cat{i}_redshiftlabel_format", + f"cat{i}_redshiftlabel_fmt", + log=False, + default_fmt=".2f", + ) + ph._set_label_format( + kwargs_, + f"cat{i}_masslabel_format", + f"cat{i}_masslabel_fmt", + log=log_mass, + default_fmt=".1f", + ) + kwargs_["cat1_datalabel1_format"] = kwargs_.pop("cat1_redshiftlabel_format") + kwargs_["cat1_datalabel2_format"] = kwargs_.pop("cat1_masslabel_format") + kwargs_["cat2_datalabel1_format"] = kwargs_.pop("cat2_redshiftlabel_format") + kwargs_["cat2_datalabel2_format"] = kwargs_.pop("cat2_masslabel_format") + + info = catalog_funcs.plot_fscore( + cat1, + "z", + "mass", + cat1_redshift_bins, + cat1_mass_bins, + cat2, + "z", + "mass", + cat2_redshift_bins, + cat2_mass_bins, + matching_type, + beta=beta, + pref=pref, + par_order=par_order, + cat1_mask=cat1_mask, + cat1_mask_unmatched=cat1_mask_unmatched, + cat2_mask=cat2_mask, + cat2_mask_unmatched=cat2_mask_unmatched, + xscale="log" if par_order[0] in (1, 3) else "linear", + ylabel=fscore_label, + **kwargs_, + ) + return info diff --git a/clevar/utils.py b/clevar/utils.py index 34cd21a5..1a2e5732 100644 --- a/clevar/utils.py +++ b/clevar/utils.py @@ -202,6 +202,24 @@ def _convert_keys(self): veclen = np.vectorize(len) +def index_list(input_list, indexes): + """Get list sorted by index list + + Parameters + ---------- + input_list: list + Input list + indexes: list + List of indexes + + Returns + ------- + list + input_list sorted by indexes. + """ + return [input_list[i] for i in indexes] + + def none_val(value, none_value): """ Set default value to be returned if input is None diff --git a/tests/test_metrics_plot_helper.py b/tests/test_metrics_plot_helper.py new file mode 100644 index 00000000..0dced890 --- /dev/null +++ b/tests/test_metrics_plot_helper.py @@ -0,0 +1,15 @@ +"""Tests for clevar/match_metrics/plot_helper""" +import pylab as plt +from numpy.testing import assert_raises +from clevar.match_metrics import plot_helper as ph + + +def test_add_panel_bin_label(): + fig, axes = plt.subplots(2, 2) + edges = range(5) + edges_lower, edges_higher = edges[:-1], edges[1:] + for position in ("top", "bottom", "left", "right"): + ph.add_panel_bin_label(axes, edges_lower, edges_higher, position=position) + assert_raises( + ValueError, ph.add_panel_bin_label, axes, edges_lower, edges_higher, position="unknown" + ) diff --git a/tests/test_metrics_recovery.py b/tests/test_metrics_recovery.py index 095411b1..4acdee7b 100644 --- a/tests/test_metrics_recovery.py +++ b/tests/test_metrics_recovery.py @@ -129,3 +129,44 @@ def test_skyplot(): matching_type, recovery_label=None, ) + + +def test_fscore(): + matching_type = "cat1" + redshift_bins = [0, 0.5, 1] + mass_bins = [1e13, 1e14, 1e15, 1e16] + args = ( + _test_data.cat1, + redshift_bins, + mass_bins, + _test_data.cat2, + redshift_bins, + mass_bins, + matching_type, + ) + rc.plot_fscore( + *args, + beta=1, + pref="cat1", + par_order=(0, 1, 2, 3), + cat1_mask=None, + cat1_mask_unmatched=None, + cat2_mask=None, + cat2_mask_unmatched=None, + log_mass=True, + fscore_label="fscore", + xlabel="x", + ) + rc.plot_fscore( + *args, + beta=1, + pref="cat2", + par_order=(0, 1, 2, 3), + cat1_mask=None, + cat1_mask_unmatched=None, + cat2_mask=None, + cat2_mask_unmatched=None, + log_mass=True, + fscore_label=None, + ) + assert_raises(ValueError, rc.plot_fscore, *args, pref="unknown") diff --git a/tests/test_utils.py b/tests/test_utils.py index 627c508c..44d33e4f 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -21,4 +21,10 @@ def test_timer_class(): LPtime = utils.Timer("test_name") LPtime.title(" (some subtitle)") LPtime.time() - LPtime.end() # close object + LPtime.end() + + +def test_other(): + assert utils.none_val("y", "x") == "y" + assert utils.none_val(None, "x") == "x" + assert utils.index_list(["A", "B"], [1, 0]) == ["B", "A"]