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 plt.axhline(y=Nvis_median / 1000., linewidth=plotDict['reflinewidth'], color='b', 

73 alpha=.5, label=r'f$_0$ Median Nvisits=%.3g' % Nvis_median) 

74 plt.axvline(x=fOArea, linewidth=plotDict['reflinewidth'], color='r', 

75 alpha=.5, label='f$_0$ Area=%.3g' % fOArea) 

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

77 

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

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

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

81 

82 xMin = plotDict['xMin'] 

83 xMax = plotDict['xMax'] 

84 yMin = plotDict['yMin'] 

85 yMax = plotDict['yMax'] 

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

87 plt.xlim([xMin, xMax]) 

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

89 plt.ylim([yMin, yMax]) 

90 return fig.number 

91 

92 

93class SummaryHistogram(BasePlotter): 

94 """ 

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

96 such as if a histogram was calculated at each slicepoint 

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

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

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

100 """ 

101 

102 def __init__(self): 

103 self.plotType = 'SummaryHistogram' 

104 self.objectPlotter = True 

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

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

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

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

109 

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

111 """ 

112 Parameters 

113 ---------- 

114 metricValue : numpy.ma.MaskedArray 

115 Handles 'object' datatypes for the masked array. 

116 slicer : lsst.sims.maf.slicers 

117 Any MAF slicer. 

118 userPlotDict: dict 

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

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

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

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

123 or as a series of values (False) 

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

125 the bins used with the metric. 

126 fignum : int 

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

128 

129 Returns 

130 ------- 

131 int 

132 Matplotlib figure number used to create the plot. 

133 """ 

134 fig = plt.figure(fignum) 

135 plotDict = {} 

136 plotDict.update(self.defaultPlotDict) 

137 plotDict.update(userPlotDict) 

138 # Combine the metric values across all slicePoints. 

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

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

141 # Get the data type 

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

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

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

145 # Make an array to hold the combined result 

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

147 metric = plotDict['metricReduce'] 

148 metric.colname = 'metricValue' 

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

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

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

152 bins = plotDict['bins'] 

153 if plotDict['histStyle']: 

154 width = np.diff(bins) 

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

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

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

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

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

160 else: 

161 # Could use this to plot things like FFT 

162 x = bins[:-1] 

163 y = finalHist 

164 # Make the plot. 

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

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

167 # Add labels. 

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

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

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

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

172 # Set y and x limits, if provided. 

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

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

175 elif bins[0] == 0: 

176 plt.xlim(left=0) 

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

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

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

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

181 elif finalHist.min() == 0: 

182 plotDict['yMin'] = 0 

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

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

185 

186 return fig.number