Coverage for python / lsst / analysis / tools / actions / plot / rhoStatisticsPlot.py: 26%

58 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-23 09:00 +0000

1# This file is part of analysis_tools. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <https://www.gnu.org/licenses/>. 

21 

22from __future__ import annotations 

23 

24__all__ = ("RhoStatisticsPlot",) 

25 

26from collections.abc import Iterable, Mapping 

27from typing import Any 

28 

29import numpy as np 

30from matplotlib.figure import Figure 

31 

32from lsst.pex.config import ConfigDictField 

33 

34from ...interfaces import KeyedData, KeyedDataSchema, PlotAction, Vector 

35from .plotUtils import addPlotInfo 

36from .xyPlot import XYPlot 

37 

38 

39class RhoStatisticsPlot(PlotAction): 

40 """Make multiple plots of rho statistics. 

41 

42 Rho statistics capture the spatial correlation amongst various PSF size and 

43 shape residual quantities. For exact definitions, see 

44 :ref:`here <rho_definitions>`. 

45 """ 

46 

47 rhoPlots = ConfigDictField[str, XYPlot]( 

48 doc="A configurable dict describing the rho statistics to plot.", 

49 default={}, 

50 ) 

51 

52 def setDefaults(self) -> None: 

53 super().setDefaults() 

54 self.rhoPlots = {rhoName: XYPlot() for rhoName in ("rho3alt", "rho1", "rho2", "rho3", "rho4", "rho5")} 

55 

56 yLabels = { 

57 "rho3alt": r"$\rho'_{3}(\theta) = \langle \frac{\delta T}{T}, \frac{\delta T}{T}\rangle$", 

58 "rho1": r"$\rho_{1}(\theta) = \langle \delta e, \delta e \rangle$", 

59 "rho2": r"$\rho_{2}(\theta) = \langle e, \delta e \rangle$", 

60 "rho3": r"$\rho_{3}(\theta) = \langle e\frac{\delta T}{T} , e\frac{\delta T}{T} \rangle$", 

61 "rho4": r"$\rho_{4}(\theta) = \langle \delta e, e\frac{\delta T}{T} \rangle$", 

62 "rho5": r"$\rho_{5}(\theta) = \langle e, e\frac{\delta T}{T} \rangle$", 

63 } 

64 

65 for rhoId, rhoPlot in self.rhoPlots.items(): 

66 rhoPlot.xAxisLabel = "Separation [arcmin]" 

67 rhoPlot.yAxisLabel = yLabels[rhoId] 

68 rhoPlot.xScale = "log" 

69 rhoPlot.yScale = "symlog" 

70 rhoPlot.yLinThresh = 1e-6 

71 rhoPlot.yLine = 0.0 

72 

73 self.rhoPlots["rho3alt"].yScale = "linear" # type: ignore 

74 

75 def getInputSchema(self) -> KeyedDataSchema: 

76 # Docstring inherited 

77 base: list[tuple[str, type[Vector]]] = [] 

78 base.append(("coord_ra", Vector)) 

79 base.append(("coord_dec", Vector)) 

80 base.append(("{{band}}_ixx", Vector)) 

81 base.append(("{{band}}_iyy", Vector)) 

82 base.append(("{{band}}_ixy", Vector)) 

83 base.append(("{{band}}_ixxPSF", Vector)) 

84 base.append(("{{band}}_iyyPSF", Vector)) 

85 base.append(("{{band}}_ixyPSF", Vector)) 

86 return base 

87 

88 def getOutputNames(self) -> Iterable[str]: 

89 # Docstring inherited 

90 yield from self.rhoPlots.keys() 

91 

92 def __call__(self, data: KeyedData, **kwargs) -> Mapping[str, Figure]: 

93 self._validateInput(data) 

94 return self.makePlot(data, **kwargs) 

95 

96 def _validateInput(self, data: KeyedData) -> None: 

97 required = set(self.rhoPlots.keys()) 

98 if not required.issubset(data.keys()): 

99 raise ValueError(f"Input data must contain {', '.join(self.rhoPlots.keys())}") 

100 

101 def makePlot( 

102 self, data: KeyedData, plotInfo: Mapping[str, str] | None = None, **kwargs: Any 

103 ) -> Mapping[str, Figure]: 

104 r"""Make the plot(s). 

105 

106 Parameters 

107 ---------- 

108 data : `~pandas.core.frame.DataFrame` 

109 The catalog containing various rho statistics. 

110 plotInfo : `dict`, optional 

111 A dictionary of information about the data being plotted with keys: 

112 ``"run"`` 

113 The output run for the plots (`str`). 

114 ``"skymap"`` 

115 The type of skymap used for the data (`str`). 

116 ``"filter"`` 

117 The filter used for this data (`str`). 

118 ``"tract"`` 

119 The tract that the data comes from (`str`). 

120 **kwargs 

121 Additional keyword arguments to pass to the plot 

122 

123 Returns 

124 ------- 

125 fig_dict : `dict` [`~matplotlib.figure.Figure`] 

126 The resulting figures. 

127 The figure corresponding :math:`\rho_1(\theta)` can be accessed 

128 with the key `rho1` and similarly for the other rho statistics. 

129 :math:`\rho_3'` is accessed with the key `rho3alt`. 

130 

131 Examples 

132 -------- 

133 An example rho statistics plot may be seen below: 

134 

135 .. image:: /_static/analysis_tools/rhoPlotExample.png 

136 

137 For further details on how to generate a plot, please refer to the 

138 :ref:`getting started guide<analysis-tools-getting-started>`. 

139 """ 

140 fig_dict: dict[str, Figure] = {} 

141 for rho_name in self.rhoPlots.keys(): 

142 rho: XYPlot = self.rhoPlots[rho_name] 

143 subdata = { 

144 "x": data[rho_name].meanr, # type: ignore 

145 "xerr": None, 

146 } 

147 if rho_name == "rho3alt": 

148 subdata["y"] = data[rho_name].xi # type: ignore 

149 subdata["yerr"] = np.sqrt(data[rho_name].varxi) 

150 else: 

151 subdata["y"] = data[rho_name].xip # type: ignore 

152 subdata["yerr"] = np.sqrt(data[rho_name].varxip) # type: ignore 

153 fig = rho(subdata, **kwargs) 

154 if plotInfo is not None: 

155 fig_dict[rho_name] = addPlotInfo(fig, plotInfo) 

156 

157 return fig_dict