Skip to content

DM-50425: Add completenessPlot publicationStyle #402

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1da239a
Fix RA diff plot using cos(RA) instead of cos(dec)
taranu Apr 26, 2025
133ab7c
Add SetSelector
taranu Apr 26, 2025
14f07c6
Set narrower default mag range for diff plots
taranu Apr 26, 2025
21387af
Add publicationStyle to CompletenessHist
taranu Apr 26, 2025
261b3f6
Add show_purity to CompletenessHist
taranu Apr 26, 2025
e4a5a41
Remove caps from completeness errorbars
taranu Apr 28, 2025
b3494e8
Make magnitude at completeness percentage labels smaller
taranu Apr 28, 2025
6c0fbde
Drop 10% and 20% completeness/purity from default percentiles
taranu Apr 28, 2025
a45e37c
Add x/yHistMaxLabels to scatterPlot
taranu Apr 30, 2025
db0d02c
Specify extent for scatterPlot hexbin
taranu Apr 30, 2025
dba3717
Set tighter margins for publicationStyle scatterPlots
taranu Apr 30, 2025
b9b27c5
Adjust scatterPlot histogram limits
taranu Apr 30, 2025
7c8bb4c
Add custom formatter to improve scatter hist tick labels
taranu May 2, 2025
01f05a8
Add objectId to columnMagnitudeScatterPlot test data
taranu May 2, 2025
f034005
Simplify CompletenessHist publication style
sr525 May 2, 2025
c6587d0
Add legendLocation config field to CompletenessHist
taranu May 2, 2025
b385cdb
Add label_shift config field to CompletenessHist
taranu May 2, 2025
6676658
Use make_figure for CompletenessHist
taranu May 2, 2025
d65e7c5
Adjust line colors for completeness plot
taranu May 15, 2025
c93153e
Add sortAllArrays to plotUtils.__all__
taranu May 15, 2025
8882777
Extend scatterPlot legend handlelength
taranu May 15, 2025
caf093e
Make scatterPlot hist Count label larger in publicationStyle
taranu May 17, 2025
56f0fe4
Set CompletenessHist legend to 2 columns
taranu Jun 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class MagnitudeCompletenessConfig(pexConfig.Config):
)
completeness_percentiles = pexConfig.ListField[float](
doc="The percentiles to find the magnitude at.",
default=[90.0, 80.0, 50.0, 20.0, 10.0],
default=[90.0, 80.0, 50.0],
itemCheck=isPercent,
)

Expand Down
118 changes: 86 additions & 32 deletions python/lsst/analysis/tools/actions/plot/completenessPlot.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@

from typing import Mapping

import matplotlib.pyplot as plt
import numpy as np
from lsst.pex.config import ChoiceField, Field
from lsst.pex.config.configurableActions import ConfigurableActionField
from lsst.utils.plotting import set_rubin_plotstyle
from lsst.utils.plotting import make_figure, set_rubin_plotstyle

# from matplotlib import gridspec
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment left in

from matplotlib.figure import Figure

from ...actions.keyedData import CalcCompletenessHistogramAction
Expand All @@ -39,11 +40,24 @@
class CompletenessHist(PlotAction):
"""Makes plots of completeness and purity."""

label_shift = Field[float](
doc="Fraction of plot width to shift completeness/purity labels by."
"Ignored if percentiles_style is not 'below_line'",
default=-0.1,
)
action = ConfigurableActionField[CalcCompletenessHistogramAction](
doc="Action to compute completeness/purity",
)
mag_ref_label = Field[str](doc="Label for the completeness x axis.", default="Reference magnitude")
mag_target_label = Field[str](doc="Label for the purity x axis.", default="Measured magnitude")
color_counts = Field[str](doc="Color for the line showing object counts", default="#029E73")
color_right = Field[str](
doc="Color for the line showing the correctly classified fraction", default="#949494"
)
color_wrong = Field[str](
doc="Color for the line showing the wrongly classified fraction", default="#DE8F05"
)
legendLocation = Field[str](doc="Legend position within main plot", default="lower left")
mag_ref_label = Field[str](doc="Label for the completeness x axis.", default="Reference Magnitude")
mag_target_label = Field[str](doc="Label for the purity x axis.", default="Measured Magnitude")
percentiles_style = ChoiceField[str](
doc="Style and locations for completeness threshold percentile labels",
allowed={
Expand All @@ -52,6 +66,8 @@ class CompletenessHist(PlotAction):
},
default="below_line",
)
publicationStyle = Field[bool](doc="Make a publication-style of plot", default=False)
show_purity = Field[bool](doc="Whether to include a purity plot below completness", default=True)

def getInputSchema(self) -> KeyedDataSchema:
yield from self.action.getOutputSchema()
Expand Down Expand Up @@ -132,10 +148,12 @@ def makePlot(self, data, plotInfo, **kwargs):

# Make plot showing the fraction recovered in magnitude bins
set_rubin_plotstyle()
fig, axes = plt.subplots(dpi=300, nrows=2, figsize=(8, 8))
color_counts = "purple"
color_wrong = "firebrick"
color_right = "teal"
n_sub = 1 + self.show_purity
fig = make_figure(dpi=300, figsize=(8, 4 * n_sub))
if self.show_purity:
axes = (fig.add_subplot(2, 1, 1), fig.add_subplot(2, 1, 2))
else:
axes = [fig.add_axes([0.1, 0.15, 0.8, 0.75])]
max_left = 1.05

band = kwargs.get("band")
Expand Down Expand Up @@ -167,28 +185,37 @@ def makePlot(self, data, plotInfo, **kwargs):

counts_all = data[names["count"]]

if self.publicationStyle:
lineTuples = (
(data[names["completeness"]], False, "k", "Completeness"),
(data[names["completeness_bad_match"]], False, self.color_wrong, "Incorrect Class"),
)
else:
lineTuples = (
(data[names["completeness"]], True, "k", "Completeness"),
(data[names["completeness_bad_match"]], False, self.color_wrong, "Incorrect class"),
(data[names["completeness_good_match"]], False, self.color_right, "Correct Class"),
)

plots = {
"Completeness": {
"count_type": "Reference",
"counts": data[names["count_ref"]],
"lines": (
(data[names["completeness"]], True, "k", "completeness"),
(data[names["completeness_bad_match"]], False, color_wrong, "wrong class"),
(data[names["completeness_good_match"]], False, color_right, "right class"),
),
"lines": lineTuples,
"xlabel": self.mag_ref_label,
},
"Purity": {
}
if self.show_purity:
plots["Purity"] = {
"count_type": "Object",
"counts": data[names["count_target"]],
"lines": (
(data[names["purity"]], True, "k", None),
(data[names["purity_bad_match"]], False, color_wrong, "wrong class"),
(data[names["purity_good_match"]], False, color_right, "right class"),
(data[names["purity"]], True, "k", "Purity"),
(data[names["purity_bad_match"]], False, self.color_wrong, "Incorrect class"),
(data[names["purity_good_match"]], False, self.color_right, "Correct class"),
),
"xlabel": self.mag_target_label,
},
}
}

# idx == 0 should be completeness; update this if that assumption
# is changed
Expand All @@ -203,9 +230,10 @@ def makePlot(self, data, plotInfo, **kwargs):
xticks=np.arange(round(xlim[0]), round(xlim[1])),
yticks=np.linspace(0, 1, 11),
)
axes_idx.grid(color="lightgrey", ls="-")
if not self.publicationStyle:
axes_idx.grid(color="lightgrey", ls="-")
ax_right = axes_idx.twinx()
ax_right.set_ylabel(f"{plot_data['count_type']} counts/mag")
ax_right.set_ylabel(f"{plot_data['count_type']} Counts/Magnitude", color="k")
ax_right.set_yscale("log")

for y, do_err, color, label in plot_data["lines"]:
Expand All @@ -214,26 +242,43 @@ def makePlot(self, data, plotInfo, **kwargs):
y=y,
xerr=x_err if do_err else None,
yerr=1.0 / np.sqrt(counts_all + 1) if do_err else None,
capsize=0,
color=color,
label=label,
)
y = plot_data["counts"] / interval
# It should be unusual for np.max(y) to be zero; nonetheless...
lines_left, labels_left = axes_idx.get_legend_handles_labels()
ax_right.step(
[x[0] - interval] + list(x) + [x[-1] + interval],
[0] + list(y) + [0],
where="mid",
color=color_counts,
label="counts",
color=self.color_counts,
label="Counts",
)

# Force the inputs counts histogram to the back
ax_right.zorder = 1
axes_idx.zorder = 2
axes_idx.patch.set_visible(False)

ax_right.set_ylim(0.999, 10 ** (max_left * np.log10(max(np.nanmax(y), 2))))
ax_right.tick_params(axis="y", labelcolor=color_counts)
lines_left, labels_left = axes_idx.get_legend_handles_labels()
ax_right.tick_params(axis="y", labelcolor=self.color_counts)
lines_right, labels_right = ax_right.get_legend_handles_labels()
axes_idx.legend(lines_left + lines_right, labels_left + labels_right, loc="lower left", ncol=2)

# Using fig for legend
(axes_idx if self.show_purity else fig).legend(
lines_left + lines_right,
labels_left + labels_right,
loc=self.legendLocation,
ncol=2,
)

if idx == 0:
percentiles = self.action.config_metrics.completeness_percentiles
if not self.publicationStyle:
percentiles = self.action.config_metrics.completeness_percentiles
else:
percentiles = [90.0, 50.0]
if percentiles:
above_plot = self.percentiles_style == "above_plot"
below_line = self.percentiles_style == "below_line"
Expand All @@ -242,7 +287,7 @@ def makePlot(self, data, plotInfo, **kwargs):
if above_plot:
texts = []
elif below_line:
offset = 0.1 * (xlims[1] - xlims[0])
offset = self.label_shift * (xlims[1] - xlims[0])
else:
raise RuntimeError(f"Unimplemented {self.percentiles_style=}")
for pct in percentiles:
Expand All @@ -260,13 +305,22 @@ def makePlot(self, data, plotInfo, **kwargs):
if above_plot:
texts.append(text)
elif below_line:
axes_idx.text(mag_completeness - offset, pct, text, ha="right", va="top")
axes_idx.text(
mag_completeness + offset,
pct - 0.02,
text,
ha="right",
va="top",
fontsize=12,
)
if above_plot:
texts = f"Thresholds: {'; '.join(texts)}"
axes_idx.text(xlims[0], max_left, texts, ha="left", va="bottom")

# Add useful information to the plot
addPlotInfo(fig, plotInfo)
fig.tight_layout()
fig.subplots_adjust(top=0.90)
if not self.publicationStyle:
addPlotInfo(fig, plotInfo)
if self.show_purity:
fig.tight_layout()
fig.subplots_adjust(top=0.90)
return fig
2 changes: 1 addition & 1 deletion python/lsst/analysis/tools/actions/plot/plotUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from __future__ import annotations

__all__ = ("PanelConfig",)
__all__ = ("PanelConfig", "sortAllArrays")

from typing import TYPE_CHECKING, Iterable, List, Mapping, Tuple

Expand Down
Loading
Loading