Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1from builtins import zip 

2import numpy as np 

3import healpy as hp 

4import matplotlib.pyplot as plt 

5 

6import lsst.sims.maf.metrics as metrics 

7 

8from .plotHandler import BasePlotter 

9 

10__all__ = ['FOPlot', 'SummaryHistogram'] 

11 

12class FOPlot(BasePlotter): 

13 """ 

14 Special plotter to generate and label fO plots. 

15 """ 

16 def __init__(self): 

17 self.plotType = 'FO' 

18 self.objectPlotter = False 

19 self.defaultPlotDict = {'title': None, 'xlabel': 'Number of visits', 

20 'ylabel': 'Area (1000s of square degrees)', 

21 'scale': None, 'Asky': 18000., 'Nvisits': 825, 

22 'xMin': 0, 'xMax': None, 'yMin': 0, 'yMax': None, 

23 'linewidth': 2, 'reflinewidth': 2} 

24 

25 def __call__(self, metricValue, slicer, userPlotDict, fignum=None): 

26 """ 

27 Parameters 

28 ---------- 

29 metricValue : numpy.ma.MaskedArray 

30 The metric values calculated with the 'Count' metric and a healpix slicer. 

31 slicer : lsst.sims.maf.slicers.HealpixSlicer 

32 userPlotDict: dict 

33 Dictionary of plot parameters set by user (overrides default values). 

34 Note that Asky and Nvisits values set here and in the slicer should be consistent, 

35 for plot labels and summary statistic values to be consistent. 

36 fignum : int 

37 Matplotlib figure number to use (default = None, starts new figure). 

38 

39 Returns 

40 ------- 

41 int 

42 Matplotlib figure number used to create the plot. 

43 """ 

44 if not hasattr(slicer, 'nside'): 

45 raise ValueError('FOPlot to be used with healpix or healpix derived slicers.') 

46 fig = plt.figure(fignum) 

47 plotDict = {} 

48 plotDict.update(self.defaultPlotDict) 

49 plotDict.update(userPlotDict) 

50 

51 if plotDict['scale'] is None: 

52 plotDict['scale'] = (hp.nside2pixarea(slicer.nside, degrees=True) / 1000.0) 

53 

54 # Expect metricValue to be something like number of visits 

55 cumulativeArea = np.arange(1, metricValue.compressed().size + 1)[::-1] * plotDict['scale'] 

56 plt.plot(np.sort(metricValue.compressed()), cumulativeArea, 'k-', 

57 linewidth=plotDict['linewidth'], zorder=0) 

58 # This is breaking the rules and calculating the summary stats in two places. 

59 # Could just calculate summary stats and pass in labels. 

60 rarr = np.array(list(zip(metricValue.compressed())), 

61 dtype=[('fO', metricValue.dtype)]) 

62 fOArea = metrics.fOArea(col='fO', Asky=plotDict['Asky'], norm=False, 

63 nside=slicer.nside).run(rarr) 

64 fONv = metrics.fONv(col='fO', Nvisit=plotDict['Nvisits'], norm=False, 

65 nside=slicer.nside).run(rarr) 

66 

67 plt.axvline(x=plotDict['Nvisits'], linewidth=plotDict['reflinewidth'], color='b') 

68 plt.axhline(y=plotDict['Asky'] / 1000., linewidth=plotDict['reflinewidth'], color='r') 

69 # Add lines for Nvis_median and fOArea: note if these are -666 (badval), 

70 # the default xMin/yMin values will just leave them off the edges of the plot. 

71 Nvis_median = fONv['value'][np.where(fONv['name'] == 'MedianNvis')] 

72 # Note that Nvis is the number of visits (it's not an area) - so goes on number axis 

73 plt.axvline(x=Nvis_median, linewidth=plotDict['reflinewidth'], color='b', 

74 alpha=.5, linestyle=':', label=r'f$_0$ Median Nvisits=%.0f' % Nvis_median) 

75 plt.axhline(y=fOArea / 1000., linewidth=plotDict['reflinewidth'], color='r', 

76 alpha=.5, linestyle=':', label='f$_0$ Area=%.0f' % fOArea) 

77 plt.legend(loc='lower left', fontsize='small', numpoints=1) 

78 

79 plt.xlabel(plotDict['xlabel']) 

80 plt.ylabel(plotDict['ylabel']) 

81 plt.title(plotDict['title']) 

82 

83 xMin = plotDict['xMin'] 

84 xMax = plotDict['xMax'] 

85 yMin = plotDict['yMin'] 

86 yMax = plotDict['yMax'] 

87 if (xMin is not None) or (xMax is not None): 

88 plt.xlim([xMin, xMax]) 

89 if (yMin is not None) or (yMax is not None): 

90 plt.ylim([yMin, yMax]) 

91 return fig.number 

92 

93 

94class SummaryHistogram(BasePlotter): 

95 """ 

96 Special plotter to summarize metrics which return a set of values at each slicepoint, 

97 such as if a histogram was calculated at each slicepoint 

98 (e.g. with the lsst.sims.maf.metrics.TgapsMetric). 

99 Effectively marginalizes the calculated values over the sky, and plots the a summarized 

100 version (reduced to a single according to the plotDict['metricReduce'] metric). 

101 """ 

102 

103 def __init__(self): 

104 self.plotType = 'SummaryHistogram' 

105 self.objectPlotter = True 

106 self.defaultPlotDict = {'title': None, 'xlabel': None, 'ylabel': 'Count', 'label': None, 

107 'cumulative': False, 'xMin': None, 'xMax': None, 'yMin': None, 'yMax': None, 

108 'color': 'b', 'linestyle': '-', 'histStyle': True, 'grid': True, 

109 'metricReduce': metrics.SumMetric(), 'bins': None} 

110 

111 def __call__(self, metricValue, slicer, userPlotDict, fignum=None): 

112 """ 

113 Parameters 

114 ---------- 

115 metricValue : numpy.ma.MaskedArray 

116 Handles 'object' datatypes for the masked array. 

117 slicer : lsst.sims.maf.slicers 

118 Any MAF slicer. 

119 userPlotDict: dict 

120 Dictionary of plot parameters set by user (overrides default values). 

121 'metricReduce' (an lsst.sims.maf.metric) indicates how to marginalize the metric values 

122 calculated at each point to a single series of values over the sky. 

123 'histStyle' (True/False) indicates whether to plot the results as a step histogram (True) 

124 or as a series of values (False) 

125 'bins' (np.ndarray) sets the x values for the resulting plot and should generally match 

126 the bins used with the metric. 

127 fignum : int 

128 Matplotlib figure number to use (default = None, starts new figure). 

129 

130 Returns 

131 ------- 

132 int 

133 Matplotlib figure number used to create the plot. 

134 """ 

135 fig = plt.figure(fignum) 

136 plotDict = {} 

137 plotDict.update(self.defaultPlotDict) 

138 plotDict.update(userPlotDict) 

139 # Combine the metric values across all slicePoints. 

140 if not isinstance(plotDict['metricReduce'], metrics.BaseMetric): 

141 raise ValueError('Expected plotDict[metricReduce] to be a MAF metric object.') 

142 # Get the data type 

143 dt = metricValue.compressed()[0].dtype 

144 # Change an array of arrays (dtype=object) to a 2-d array of correct dtype 

145 mV = np.array(metricValue.compressed().tolist(), dtype=[('metricValue', dt)]) 

146 # Make an array to hold the combined result 

147 finalHist = np.zeros(mV.shape[1], dtype=float) 

148 metric = plotDict['metricReduce'] 

149 metric.colname = 'metricValue' 

150 # Loop over each bin and use the selected metric to combine the results 

151 for i in np.arange(finalHist.size): 

152 finalHist[i] = metric.run(mV[:, i]) 

153 bins = plotDict['bins'] 

154 if plotDict['histStyle']: 

155 width = np.diff(bins) 

156 leftedge = bins[:-1] - width/2.0 

157 rightedge = bins[:-1] + width/2.0 

158 #x = np.ravel(list(zip(bins[:-1], bins[1:]))) 

159 x = np.ravel(list(zip(leftedge, rightedge))) 

160 y = np.ravel(list(zip(finalHist, finalHist))) 

161 else: 

162 # Could use this to plot things like FFT 

163 x = bins[:-1] 

164 y = finalHist 

165 # Make the plot. 

166 plt.plot(x, y, linestyle=plotDict['linestyle'], 

167 label=plotDict['label'], color=plotDict['color']) 

168 # Add labels. 

169 plt.xlabel(plotDict['xlabel']) 

170 plt.ylabel(plotDict['ylabel']) 

171 plt.title(plotDict['title']) 

172 plt.grid(plotDict['grid'], alpha=0.3) 

173 # Set y and x limits, if provided. 

174 if plotDict['xMin'] is not None: 

175 plt.xlim(left=plotDict['xMin']) 

176 elif bins[0] == 0: 

177 plt.xlim(left=0) 

178 if plotDict['xMax'] is not None: 

179 plt.xlim(right=plotDict['xMax']) 

180 if plotDict['yMin'] is not None: 

181 plt.ylim(bottom=plotDict['yMin']) 

182 elif finalHist.min() == 0: 

183 plotDict['yMin'] = 0 

184 if plotDict['yMax'] is not None: 

185 plt.ylim(top=plotDict['yMax']) 

186 

187 return fig.number