692 catalog_ref: pd.DataFrame | astropy.table.Table,
693 catalog_target: pd.DataFrame | astropy.table.Table,
694 catalog_match_ref: pd.DataFrame | astropy.table.Table,
695 catalog_match_target: pd.DataFrame | astropy.table.Table,
697 ) -> pipeBase.Struct:
698 """Load matched reference and target (measured) catalogs, measure summary statistics, and output
699 a combined matched catalog with columns from both inputs.
703 catalog_ref : `pandas.DataFrame` | `astropy.table.Table`
704 A reference catalog to diff objects/sources from.
705 catalog_target : `pandas.DataFrame` | `astropy.table.Table`
706 A target catalog to diff reference objects/sources to.
707 catalog_match_ref : `pandas.DataFrame` | `astropy.table.Table`
708 A catalog with match indices of target sources and selection flags
709 for each reference source.
710 catalog_match_target : `pandas.DataFrame` | `astropy.table.Table`
711 A catalog with selection flags for each target source.
712 wcs : `lsst.afw.image.SkyWcs`
713 A coordinate system to convert catalog positions to sky coordinates,
718 retStruct : `lsst.pipe.base.Struct`
719 A struct with output_ref and output_target attribute containing the
720 output matched catalogs.
723 config: DiffMatchedTractCatalogConfig = self.config
725 is_ref_pd = isinstance(catalog_ref, pd.DataFrame)
726 is_target_pd = isinstance(catalog_target, pd.DataFrame)
727 is_match_ref_pd = isinstance(catalog_match_ref, pd.DataFrame)
728 is_match_target_pd = isinstance(catalog_match_target, pd.DataFrame)
730 catalog_ref = astropy.table.Table.from_pandas(catalog_ref)
732 catalog_target = astropy.table.Table.from_pandas(catalog_target)
734 catalog_match_ref = astropy.table.Table.from_pandas(catalog_match_ref)
735 if is_match_target_pd:
736 catalog_match_target = astropy.table.Table.from_pandas(catalog_match_target)
738 if is_ref_pd
or is_target_pd
or is_match_ref_pd
or is_match_target_pd:
739 warnings.warn(
"pandas usage in MatchProbabilisticTask is deprecated; it will be removed "
740 " in favour of astropy.table after release 28.0.0", category=FutureWarning)
742 select_ref = catalog_match_ref[
'match_candidate']
745 select_target = (catalog_match_target[
'match_candidate']
746 if 'match_candidate' in catalog_match_target.columns
747 else np.ones(len(catalog_match_target), dtype=bool))
748 for column
in config.columns_target_select_true:
749 select_target &= catalog_target[column]
750 for column
in config.columns_target_select_false:
751 select_target &= ~catalog_target[column]
753 ref, target = config.coord_format.format_catalogs(
754 catalog_ref=catalog_ref, catalog_target=catalog_target,
755 select_ref=
None, select_target=select_target, wcs=wcs, radec_to_xy_func=radec_to_xy,
757 cat_ref = ref.catalog
758 cat_target = target.catalog
759 n_target = len(cat_target)
761 if config.include_unmatched:
762 for cat_add, cat_match
in ((cat_ref, catalog_match_ref), (cat_target, catalog_match_target)):
763 cat_add[
'match_candidate'] = cat_match[
'match_candidate']
765 match_row = catalog_match_ref[
'match_row']
766 matched_ref = match_row >= 0
767 matched_row = match_row[matched_ref]
768 matched_target = np.zeros(n_target, dtype=bool)
769 matched_target[matched_row] =
True
772 coord1_target_err, coord2_target_err = config.columns_target_coord_err
773 column_dist, column_dist_err =
'match_distance',
'match_distanceErr'
774 dist = np.full(n_target, np.nan)
776 target_match_c1, target_match_c2 = (coord[matched_row]
for coord
in (target.coord1, target.coord2))
777 target_ref_c1, target_ref_c2 = (coord[matched_ref]
for coord
in (ref.coord1, ref.coord2))
779 dist_err = np.full(n_target, np.nan)
780 dist[matched_row] = sphdist(
781 target_match_c1, target_match_c2, target_ref_c1, target_ref_c2
782 )
if config.coord_format.coords_spherical
else np.hypot(
783 target_match_c1 - target_ref_c1, target_match_c2 - target_ref_c2,
785 cat_target_matched = cat_target[matched_row]
789 np.ma.getdata(cat_target_matched[c_err])
for c_err
in (coord1_target_err, coord2_target_err)
792 dist_err[matched_row] = sphdist(
793 target_match_c1, target_match_c2, target_match_c1 + c1_err, target_match_c2 + c2_err
794 )
if config.coord_format.coords_spherical
else np.hypot(c1_err, c2_err)
795 cat_target[column_dist], cat_target[column_dist_err] = dist, dist_err
798 cat_left = cat_target[matched_row]
799 cat_right = cat_ref[matched_ref]
800 cat_right.rename_columns(
801 list(cat_right.columns),
802 new_names=[f
'{config.column_matched_prefix_ref}{col}' for col
in cat_right.columns],
804 cat_matched = astropy.table.hstack((cat_left, cat_right))
806 if config.include_unmatched:
810 cat_right = astropy.table.Table(
811 cat_ref[~matched_ref & select_ref]
813 cat_right.rename_columns(
815 [f
"{config.column_matched_prefix_ref}{col}" for col
in cat_right.colnames],
817 match_row_target = catalog_match_target[
'match_row']
818 cat_left = cat_target[~(match_row_target >= 0) & select_target]
822 cat_unmatched = astropy.table.vstack([cat_left, cat_right])
824 for columns_convert_base, prefix
in (
825 (config.columns_ref_mag_to_nJy, config.column_matched_prefix_ref),
826 (config.columns_target_mag_to_nJy,
""),
828 if columns_convert_base:
830 f
"{prefix}{k}": f
"{prefix}{v}" for k, v
in columns_convert_base.items()
831 }
if prefix
else columns_convert_base
832 to_convert = [cat_matched]
833 if config.include_unmatched:
834 to_convert.append(cat_unmatched)
835 for cat_convert
in to_convert:
836 cat_convert.rename_columns(
837 tuple(columns_convert.keys()),
838 tuple(columns_convert.values()),
840 for column_flux
in columns_convert.values():
841 cat_convert[column_flux] = u.ABmag.to(u.nJy, cat_convert[column_flux])
844 band_fluxes = [(band, config_flux)
for (band, config_flux)
in config.columns_flux.items()]
845 n_bands = len(band_fluxes)
848 do_stats = self.config.compute_stats
and (n_bands > 0)
851 column_dummy =
'dummy'
852 cat_ref[column_dummy] = np.zeros_like(ref.coord1)
856 extended_ref = cat_ref[config.column_ref_extended] == (
not config.column_ref_extended_inverted)
858 extended_target = cat_target[config.column_target_extended] >= config.extendedness_cut
861 suffixes = {MeasurementType.DIFF:
'diff', MeasurementType.CHI:
'chi'}
863 suffixes_flux = {MeasurementType.CHI: suffixes[MeasurementType.CHI]}
865 suffixes_mag = {MeasurementType.DIFF: suffixes[MeasurementType.DIFF]}
866 stats = {stat.name_short(): stat()
for stat
in (Median, SigmaIQR, SigmaMAD)}
868 for percentile
in self.config.percentiles:
869 stat =
Percentile(percentile=float(Decimal(percentile)))
870 stats[stat.name_short()] = stat
874 bands_columns=config.columns_flux,
876 suffixes_flux=suffixes_flux,
877 suffixes_mag=suffixes_mag,
880 column_dist=column_dist,
884 n_bins = config.mag_num_bins
885 data = np.zeros((n_bins,), dtype=[(key, value)
for key, value
in columns.items()])
886 data[
'bin'] = np.arange(n_bins)
889 bins_mag = np.linspace(start=config.mag_brightest_ref, stop=config.mag_faintest_ref,
891 data[
'mag_min'] = bins_mag[:-1]
892 data[
'mag_max'] = bins_mag[1:]
893 bins_mag = tuple((bins_mag[idx], bins_mag[idx + 1])
for idx
in range(n_bins))
896 column_mag_temp =
'mag_temp'
897 column_color_temp =
'color_temp'
898 column_color_err_temp =
'colorErr_temp'
899 flux_err_frac_prev = [
None]*n_models
900 mag_prev = [
None]*n_models
903 target.column_coord1: (
904 ref.column_coord1, target.column_coord1, coord1_target_err,
False,
906 target.column_coord2: (
907 ref.column_coord2, target.column_coord2, coord2_target_err,
False,
909 column_dist: (column_dummy, column_dist, column_dist_err,
False),
914 band_fluxes.append(band_fluxes[0])
915 flux_err_frac_first =
None
920 for idx_band, (band, config_flux)
in enumerate(band_fluxes):
921 if idx_band == n_bands:
923 mag_ref = mag_ref_first
924 flux_err_frac = flux_err_frac_first
925 mag_model = mag_first
927 mag_ref = -2.5*np.log10(cat_ref[config_flux.column_ref_flux]) + config.mag_zeropoint_ref
928 flux_err_frac = [
None]*n_models
929 mag_model = [
None]*n_models
932 cat_ref[column_color_temp] = cat_ref[column_mag_temp] - mag_ref
934 cat_ref[column_mag_temp] = mag_ref
936 select_ref_bins = [select_ref & (mag_ref > mag_lo) & (mag_ref < mag_hi)
937 for idx_bin, (mag_lo, mag_hi)
in enumerate(bins_mag)]
940 for idx_model
in range(n_models):
941 column_target_flux = config_flux.columns_target_flux[idx_model]
942 column_target_flux_err = config_flux.columns_target_flux_err[idx_model]
944 flux_target = cat_target[column_target_flux]
945 mag_target = -2.5*np.log10(flux_target) + config.mag_zeropoint_target
946 if config.mag_ceiling_target
is not None:
947 mag_target[mag_target > config.mag_ceiling_target] = config.mag_ceiling_target
948 mag_model[idx_model] = mag_target
951 flux_err_frac[idx_model] = cat_target[column_target_flux_err]/flux_target
956 column_mag_temp_model = f
'{column_mag_temp}{idx_model}'
957 cat_target[column_mag_temp_model] = mag_target
959 columns_target[f
'flux_{column_target_flux}'] = (
960 config_flux.column_ref_flux,
962 column_target_flux_err,
966 columns_target[f
'mag_{column_target_flux}'] = (
967 column_mag_temp, column_mag_temp_model,
None,
False,
972 skip_color = (idx_band == n_bands)
and (n_bands <= 2)
974 column_color_temp_model = f
'{column_color_temp}{idx_model}'
975 column_color_err_temp_model = f
'{column_color_err_temp}{idx_model}'
978 cat_target[column_color_temp_model] = mag_prev[idx_model] - mag_model[idx_model]
981 cat_target[column_color_err_temp_model] = 2.5/np.log(10)*np.hypot(
982 flux_err_frac[idx_model], flux_err_frac_prev[idx_model])
983 columns_target[f
'color_{band_prev}_m_{band}_{column_target_flux}'] = (
985 column_color_temp_model,
986 column_color_err_temp_model,
990 for idx_bin, (mag_lo, mag_hi)
in enumerate(bins_mag):
994 select_ref_bin = select_ref_bins[idx_bin]
995 select_target_bin = select_target & (mag_target > mag_lo) & (mag_target < mag_hi)
997 for sourcetype
in SourceType:
998 sourcetype_info = sourcetype.value
999 is_extended = sourcetype_info.is_extended
1001 select_ref_sub = select_ref_bin.copy()
1002 select_target_sub = select_target_bin.copy()
1003 if is_extended
is not None:
1004 is_extended_ref = (extended_ref == is_extended)
1005 select_ref_sub &= is_extended_ref
1007 n_ref_sub = np.count_nonzero(select_ref_sub)
1009 MatchType.ALL.value)] = n_ref_sub
1010 select_target_sub &= (extended_target == is_extended)
1011 n_target_sub = np.count_nonzero(select_target_sub)
1013 MatchType.ALL.value)] = n_target_sub
1016 match_row_bin = match_row.copy()
1017 match_row_bin[~select_ref_sub] = -1
1018 match_good = match_row_bin >= 0
1020 n_match = np.count_nonzero(match_good)
1025 rows_matched = match_row_bin[match_good]
1026 subset_target = cat_target[rows_matched]
1027 if (is_extended
is not None)
and (idx_model == 0):
1028 right_type = extended_target[rows_matched] == is_extended
1029 n_total = len(right_type)
1030 n_right = np.count_nonzero(right_type)
1032 MatchType.MATCH_RIGHT.value)] = n_right
1035 sourcetype_info.label,
1037 MatchType.MATCH_WRONG.value,
1038 )] = n_total - n_right
1041 for column, (column_ref, column_target, column_err_target, skip_diff) \
1042 in columns_target.items():
1043 values_ref = cat_ref[column_ref][match_good]
1045 subset_target[column_err_target]
1046 if column_err_target
is not None
1051 subset_target[column_target],
1056 prefix=f
'{band}_{sourcetype_info.label}_{column}',
1057 skip_diff=skip_diff,
1064 select_target_sub &= matched_target
1066 if is_extended
is not None and (np.count_nonzero(select_target_sub) > 0):
1067 n_total = np.count_nonzero(select_target_sub)
1068 right_type = np.zeros(n_target, dtype=bool)
1069 right_type[match_row[matched_ref & is_extended_ref]] =
True
1070 right_type &= select_target_sub
1071 n_right = np.count_nonzero(right_type)
1073 MatchType.MATCH_RIGHT.value)] = n_right
1075 MatchType.MATCH_WRONG.value)] = n_total - n_right
1078 for prefix
in (
'flux',
'mag'):
1079 del columns_target[f
'{prefix}_{column_target_flux}']
1081 del columns_target[f
'color_{band_prev}_m_{band}_{column_target_flux}']
1084 flux_err_frac_prev = flux_err_frac
1085 mag_prev = mag_model
1088 flux_err_frac_first = flux_err_frac
1089 mag_first = mag_model
1090 mag_ref_first = mag_ref
1092 if config.include_unmatched:
1094 cat_matched = astropy.table.vstack([cat_matched, cat_unmatched])
1096 retStruct = pipeBase.Struct(cat_matched=cat_matched)
1098 retStruct.diff_matched = astropy.table.Table(data)