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

59 statements  

« prev     ^ index     » next       coverage.py v7.2.5, created at 2023-05-04 11:09 +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 

24from typing import TYPE_CHECKING, Any, Iterable, Mapping 

25 

26__all__ = ("RhoStatisticsPlotAction",) 

27 

28import numpy as np 

29from lsst.pex.config import ConfigDictField 

30 

31from ...interfaces import PlotAction, Vector 

32from .plotUtils import addPlotInfo 

33from .xyPlot import XYPlot 

34 

35if TYPE_CHECKING: 35 ↛ 36line 35 didn't jump to line 36, because the condition on line 35 was never true

36 from matplotlib.figure import Figure 

37 

38 from ...interfaces import KeyedData, KeyedDataSchema 

39 

40 

41class RhoStatisticsPlotAction(PlotAction): 

42 """Make multiple plots of rho statistics. 

43 

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

45 shape residual quantities. For exact definitions, see 

46 :ref:`here <rho_definitions>`. 

47 """ 

48 

49 rhoPlots = ConfigDictField( 

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

51 keytype=str, 

52 itemtype=XYPlot, 

53 default={}, 

54 ) 

55 

56 def setDefaults(self) -> None: 

57 super().setDefaults() 

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

59 

60 yLabels = { 

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

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

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

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

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

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

67 } 

68 

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

70 rhoPlot.xAxisLabel = "Separation [arcmin]" 

71 rhoPlot.yAxisLabel = yLabels[rhoId] 

72 rhoPlot.xScale = "log" 

73 rhoPlot.yScale = "symlog" 

74 rhoPlot.yLinThresh = 1e-6 

75 

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

77 

78 def getInputSchema(self) -> KeyedDataSchema: 

79 # Docstring inherited 

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

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

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

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

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

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

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

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

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

89 return base 

90 

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

92 # Docstring inherited 

93 return ("rho3alt", "rho1", "rho2", "rho3", "rho4", "rho5") 

94 

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

96 self._validateInput(data) 

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

98 

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

100 if not set(("rho3alt", "rho1", "rho2", "rho3", "rho4", "rho5")).issubset(data.keys()): 

101 raise ValueError("Input data must contain rho3alt, rho1, rho2, rho3, rho4, and rho5.") 

102 

103 def makePlot( 

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

105 ) -> Mapping[str, Figure]: 

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

107 

108 Parameters 

109 ---------- 

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

111 The catalog containing various rho statistics. 

112 plotInfo : `dict`, optional 

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

114 ``"run"`` 

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

116 ``"skymap"`` 

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

118 ``"filter"`` 

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

120 ``"tract"`` 

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

122 **kwargs 

123 Additional keyword arguments to pass to the plot 

124 

125 Returns 

126 ------- 

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

128 The resulting figures. 

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

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

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

132 

133 Examples 

134 -------- 

135 An example rho statistics plot may be seen below: 

136 

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

138 

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

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

141 """ 

142 # The prefix for the plot names must match the prefix in the pipeline. 

143 # This is therefore obtained from the plotInfo dict. 

144 default_prefix = "rhoStatisticsPlot" 

145 prefix = plotInfo.get("plotName", default_prefix) if plotInfo else default_prefix 

146 

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

148 for rho_name in ("rho1", "rho2", "rho3", "rho4", "rho5"): 

149 rho: XYPlot = self.rhoPlots[rho_name] 

150 rhoPlot_name = f"{prefix}_{rho_name}" 

151 

152 subdata = { 

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

154 "y": data[rho_name].xip, # type: ignore 

155 "yerr": np.sqrt(data[rho_name].varxip), # type: ignore 

156 "xerr": None, 

157 } 

158 fig = rho(subdata, **kwargs) 

159 if plotInfo is not None: 

160 fig_dict[rhoPlot_name] = addPlotInfo(fig, plotInfo) 

161 

162 # rho3alt is handled differently because its attributes differ. 

163 subdata = { 

164 "x": data["rho3alt"].meanr, # type: ignore 

165 "y": data["rho3alt"].xi, # type: ignore 

166 "yerr": np.sqrt(data["rho3alt"].varxi), # type: ignore 

167 "xerr": None, 

168 } 

169 fig = self.rhoPlots["rho3alt"](subdata, **kwargs) # type: ignore[misc] 

170 if plotInfo is not None: 

171 fig_dict["rhoStatisticsPlot_rho3alt"] = addPlotInfo(fig, plotInfo) 

172 

173 return fig_dict