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 

2from builtins import object 

3import os 

4from copy import deepcopy 

5import numpy as np 

6import numpy.ma as ma 

7import warnings 

8 

9import lsst.sims.maf.metrics as metrics 

10import lsst.sims.maf.slicers as slicers 

11import lsst.sims.maf.stackers as stackers 

12import lsst.sims.maf.maps as maps 

13import lsst.sims.maf.plots as plots 

14from lsst.sims.maf.stackers import ColInfo 

15import lsst.sims.maf.utils as utils 

16 

17__all__ = ['MetricBundle', 'createEmptyMetricBundle'] 

18 

19 

20def createEmptyMetricBundle(): 

21 """Create an empty metric bundle. 

22 

23 Returns 

24 ------- 

25 MetricBundle 

26 An empty metric bundle, configured with just the :class:`BaseMetric` and :class:`BaseSlicer`. 

27 """ 

28 return MetricBundle(metrics.BaseMetric(), slicers.BaseSlicer(), '') 

29 

30 

31class MetricBundle(object): 

32 """The MetricBundle is defined by a combination of a (single) metric, slicer and 

33 constraint - together these define a unique combination of an opsim benchmark. 

34 An example would be: a CountMetric, a HealpixSlicer, and a sqlconstraint 'filter="r"'. 

35 

36 After the metric is evaluated over the slicePoints of the slicer, the resulting 

37 metric values are saved in the MetricBundle. 

38 

39 The MetricBundle also saves the summary metrics to be used to generate summary 

40 statistics over those metric values, as well as the resulting summary statistic values. 

41 

42 Plotting parameters and display parameters (for showMaf) are saved in the MetricBundle, 

43 as well as additional metadata such as the opsim run name, and relevant stackers and maps 

44 to apply when calculating the metric values. 

45 """ 

46 colInfo = ColInfo() 

47 

48 def __init__(self, metric, slicer, constraint=None, sqlconstraint=None, 

49 stackerList=None, runName='opsim', metadata=None, 

50 plotDict=None, displayDict=None, 

51 summaryMetrics=None, mapsList=None, 

52 fileRoot=None, plotFuncs=None): 

53 # Set the metric. 

54 if not isinstance(metric, metrics.BaseMetric): 

55 raise ValueError('metric must be an lsst.sims.maf.metrics object') 

56 self.metric = metric 

57 # Set the slicer. 

58 if not isinstance(slicer, slicers.BaseSlicer): 

59 raise ValueError('slicer must be an lsst.sims.maf.slicers object') 

60 self.slicer = slicer 

61 # Set the constraint. 

62 self.constraint = constraint 

63 if self.constraint is None: 

64 # Provide backwards compatibility for now - phase out sqlconstraint eventually. 

65 if sqlconstraint is not None: 

66 warnings.warn('Future warning - "sqlconstraint" will be deprecated in favor of ' 

67 '"constraint" in a future release.') 

68 self.constraint = sqlconstraint 

69 if self.constraint is None: 

70 self.constraint = '' 

71 # Set the stackerlist if applicable. 

72 if stackerList is not None: 

73 if isinstance(stackerList, stackers.BaseStacker): 

74 self.stackerList = [stackerList, ] 

75 else: 

76 self.stackerList = [] 

77 for s in stackerList: 

78 if s is None: 

79 pass 

80 else: 

81 if not isinstance(s, stackers.BaseStacker): 

82 raise ValueError('stackerList must only contain lsst.sims.maf.stackers objs') 

83 self.stackerList.append(s) 

84 else: 

85 self.stackerList = [] 

86 # Set the 'maps' to apply to the slicer, if applicable. 

87 if mapsList is not None: 

88 if isinstance(mapsList, maps.BaseMap): 

89 self.mapsList = [mapsList, ] 

90 else: 

91 self.mapsList = [] 

92 for m in mapsList: 

93 if not isinstance(m, maps.BaseMap): 

94 raise ValueError('mapsList must only contain lsst.sims.maf.maps objects') 

95 self.mapsList.append(m) 

96 else: 

97 self.mapsList = [] 

98 # If the metric knows it needs a particular map, add it to the list. 

99 mapNames = [mapName.__class__.__name__ for mapName in self.mapsList] 

100 if hasattr(self.metric, 'maps'): 

101 for mapName in self.metric.maps: 

102 if mapName not in mapNames: 

103 tempMap = getattr(maps, mapName)() 

104 self.mapsList.append(tempMap) 

105 mapNames.append(mapName) 

106 

107 # Add the summary stats, if applicable. 

108 self.setSummaryMetrics(summaryMetrics) 

109 # Set the provenance/metadata. 

110 self._buildMetadata(metadata) 

111 # Set the run name and build the output filename base (fileRoot). 

112 self.setRunName(runName) 

113 # Reset fileRoot, if provided. 

114 if fileRoot is not None: 

115 self.fileRoot = fileRoot 

116 # Determine the columns needed from the database. 

117 self._findReqCols() 

118 # Set the plotting classes/functions. 

119 self.setPlotFuncs(plotFuncs) 

120 # Set the plotDict and displayDicts. 

121 self.plotDict = {} 

122 self.setPlotDict(plotDict) 

123 # Update/set displayDict. 

124 self.displayDict = {} 

125 self.setDisplayDict(displayDict) 

126 # This is where we store the metric values and summary stats. 

127 self.metricValues = None 

128 self.summaryValues = None 

129 

130 def _resetMetricBundle(self): 

131 """Reset all properties of MetricBundle. 

132 """ 

133 self.metric = None 

134 self.slicer = None 

135 self.constraint = None 

136 self.stackerList = [] 

137 self.summaryMetrics = [] 

138 self.plotFuncs = [] 

139 self.mapsList = None 

140 self.runName = 'opsim' 

141 self.metadata = '' 

142 self.dbCols = None 

143 self.fileRoot = None 

144 self.plotDict = {} 

145 self.displayDict = {} 

146 self.metricValues = None 

147 self.summaryValues = None 

148 

149 def _setupMetricValues(self): 

150 """Set up the numpy masked array to store the metric value data. 

151 """ 

152 dtype = self.metric.metricDtype 

153 # Can't store healpix slicer mask values in an int array. 

154 if dtype == 'int': 

155 dtype = 'float' 

156 if self.metric.shape == 1: 

157 shape = self.slicer.shape 

158 else: 

159 shape = (self.slicer.shape, self.metric.shape) 

160 self.metricValues = ma.MaskedArray(data=np.empty(shape, dtype), 

161 mask=np.zeros(shape, 'bool'), 

162 fill_value=self.slicer.badval) 

163 

164 def _buildMetadata(self, metadata): 

165 """If no metadata is provided, process the constraint 

166 (by removing extra spaces, quotes, the word 'filter' and equal signs) to make a metadata version. 

167 e.g. 'filter = "r"' becomes 'r' 

168 """ 

169 if metadata is None: 

170 self.metadata = self.constraint.replace('=', '').replace('filter', '').replace("'", '') 

171 self.metadata = self.metadata.replace('"', '').replace(' ', ' ') 

172 self.metadata.strip(' ') 

173 else: 

174 self.metadata = metadata 

175 

176 def _buildFileRoot(self): 

177 """ 

178 Build an auto-generated output filename root (i.e. minus the plot type or .npz ending). 

179 """ 

180 # Build basic version. 

181 self.fileRoot = '_'.join([self.runName, self.metric.name, self.metadata, 

182 self.slicer.slicerName[:4].upper()]) 

183 # Sanitize output name if needed. 

184 self.fileRoot = utils.nameSanitize(self.fileRoot) 

185 

186 def _findReqCols(self): 

187 """Find the columns needed by the metrics, slicers, and stackers. 

188 If there are any additional stackers required, instatiate them and add them to 

189 the self.stackers list. 

190 (default stackers have to be instantiated to determine what additional columns 

191 are needed from database). 

192 """ 

193 # Find all the columns needed by metric and slicer. 

194 knownCols = self.slicer.columnsNeeded + list(self.metric.colNameArr) 

195 # For the stackers already set up, find their required columns. 

196 for s in self.stackerList: 

197 knownCols += s.colsReq 

198 knownCols = set(knownCols) 

199 # Track sources of all of these columns. 

200 self.dbCols = set() 

201 newstackers = set() 

202 for col in knownCols: 

203 if self.colInfo.getDataSource(col) == self.colInfo.defaultDataSource: 

204 self.dbCols.add(col) 

205 else: 

206 # New default stackers could come from metric/slicer or stackers. 

207 newstackers.add(self.colInfo.getDataSource(col)) 

208 # Remove already-specified stackers from default list. 

209 for s in self.stackerList: 

210 if s.__class__ in newstackers: 

211 newstackers.remove(s.__class__) 

212 # Loop and check if stackers are introducing new columns or stackers. 

213 while len(newstackers) > 0: 

214 # Check for the sources of the columns in any of the new stackers. 

215 newCols = [] 

216 for s in newstackers: 

217 newstacker = s() 

218 newCols += newstacker.colsReq 

219 self.stackerList.append(newstacker) 

220 newCols = set(newCols) 

221 newstackers = set() 

222 for col in newCols: 

223 if self.colInfo.getDataSource(col) == self.colInfo.defaultDataSource: 

224 self.dbCols.add(col) 

225 else: 

226 newstackers.add(self.colInfo.getDataSource(col)) 

227 for s in self.stackerList: 

228 if s.__class__ in newstackers: 

229 newstackers.remove(s.__class__) 

230 # A Bit of cleanup. 

231 # Remove 'metricdata' from dbcols if it ended here by default. 

232 if 'metricdata' in self.dbCols: 

233 self.dbCols.remove('metricdata') 

234 if 'None' in self.dbCols: 

235 self.dbCols.remove('None') 

236 

237 def setSummaryMetrics(self, summaryMetrics): 

238 """Set (or reset) the summary metrics for the metricbundle. 

239 

240 Parameters 

241 ---------- 

242 summaryMetrics : List[BaseMetric] 

243 Instantiated summary metrics to use to calculate summary statistics for this metric. 

244 """ 

245 if summaryMetrics is not None: 

246 if isinstance(summaryMetrics, metrics.BaseMetric): 

247 self.summaryMetrics = [summaryMetrics] 

248 else: 

249 self.summaryMetrics = [] 

250 for s in summaryMetrics: 

251 if not isinstance(s, metrics.BaseMetric): 

252 raise ValueError('SummaryStats must only contain lsst.sims.maf.metrics objects') 

253 self.summaryMetrics.append(s) 

254 else: 

255 # Add identity metric to unislicer metric values (to get them into resultsDB). 

256 if self.slicer.slicerName == 'UniSlicer': 

257 self.summaryMetrics = [metrics.IdentityMetric()] 

258 else: 

259 self.summaryMetrics = [] 

260 

261 def setPlotFuncs(self, plotFuncs): 

262 """Set or reset the plotting functions. 

263 

264 The default is to use all the plotFuncs associated with the slicer, which 

265 is what happens in self.plot if setPlotFuncs is not used to override self.plotFuncs. 

266 

267 Parameters 

268 ---------- 

269 plotFuncs : List[BasePlotter] 

270 The plotter or plotters to use to generate visuals for this metric. 

271 """ 

272 if plotFuncs is not None: 

273 if plotFuncs is isinstance(plotFuncs, plots.BasePlotter): 

274 self.plotFuncs = [plotFuncs] 

275 else: 

276 self.plotFuncs = [] 

277 for pFunc in plotFuncs: 

278 if not isinstance(pFunc, plots.BasePlotter): 

279 raise ValueError('plotFuncs should contain instantiated ' + 

280 'lsst.sims.maf.plotter objects.') 

281 self.plotFuncs.append(pFunc) 

282 else: 

283 self.plotFuncs = [] 

284 for pFunc in self.slicer.plotFuncs: 

285 if isinstance(pFunc, plots.BasePlotter): 

286 self.plotFuncs.append(pFunc) 

287 else: 

288 self.plotFuncs.append(pFunc()) 

289 

290 def setPlotDict(self, plotDict): 

291 """Set or update any property of plotDict. 

292 

293 Parameters 

294 ---------- 

295 plotDict : dict 

296 A dictionary of plotting parameters. 

297 The usable keywords vary with each lsst.sims.maf.plots Plotter. 

298 """ 

299 # Don't auto-generate anything here - the plotHandler does it. 

300 if plotDict is not None: 

301 self.plotDict.update(plotDict) 

302 # Check for bad zp or normVal values. 

303 if 'zp' in self.plotDict: 

304 if self.plotDict['zp'] is not None: 

305 if not np.isfinite(self.plotDict['zp']): 

306 warnings.warn('Warning! Plot zp for %s was infinite: removing zp from plotDict' 

307 % (self.fileRoot)) 

308 del self.plotDict['zp'] 

309 if 'normVal' in self.plotDict: 

310 if self.plotDict['normVal'] == 0: 

311 warnings.warn('Warning! Plot normalization value for %s was 0: removing normVal from plotDict' 

312 % (self.fileRoot)) 

313 del self.plotDict['normVal'] 

314 

315 def setDisplayDict(self, displayDict=None, resultsDb=None): 

316 """Set or update any property of displayDict. 

317 

318 Parameters 

319 ---------- 

320 displayDict : Optional[dict] 

321 Dictionary of display parameters for showMaf. 

322 Expected keywords: 'group', 'subgroup', 'order', 'caption'. 

323 'group', 'subgroup', and 'order' control where the metric results are shown on the showMaf page. 

324 'caption' provides a caption to use with the metric results. 

325 These values are saved in the results database. 

326 resultsDb : Optional[ResultsDb] 

327 A MAF results database, used to save the display parameters. 

328 """ 

329 # Set up a temporary dictionary with the default values. 

330 tmpDisplayDict = {'group': None, 'subgroup': None, 'order': 0, 'caption': None} 

331 # Update from self.displayDict (to use existing values, if present). 

332 tmpDisplayDict.update(self.displayDict) 

333 # And then update from any values being passed now. 

334 if displayDict is not None: 

335 tmpDisplayDict.update(displayDict) 

336 # Reset self.displayDict to this updated dictionary. 

337 self.displayDict = tmpDisplayDict 

338 # If we still need to auto-generate a caption, do it. 

339 if self.displayDict['caption'] is None: 

340 if self.metric.comment is None: 

341 caption = self.metric.name + ' calculated on a %s basis' % (self.slicer.slicerName) 

342 if self.constraint!='' and self.constraint is not None: 

343 caption += ' using a subset of data selected via %s.' % (self.constraint) 

344 else: 

345 caption += '.' 

346 else: 

347 caption = self.metric.comment 

348 if 'zp' in self.plotDict: 

349 caption += ' Values plotted with a zeropoint of %.2f.' % (self.plotDict['zp']) 

350 if 'normVal' in self.plotDict: 

351 caption += ' Values plotted with a normalization value of %.2f.' % (self.plotDict['normVal']) 

352 self.displayDict['caption'] = caption 

353 if resultsDb: 

354 # Update the display values in the resultsDb. 

355 metricId = resultsDb.updateMetric(self.metric.name, self.slicer.slicerName, 

356 self.runName, self.constraint, 

357 self.metadata, None) 

358 resultsDb.updateDisplay(metricId, self.displayDict) 

359 

360 def setRunName(self, runName, updateFileRoot=True): 

361 """Set (or reset) the runName. FileRoot will be updated accordingly if desired. 

362 

363 Parameters 

364 ---------- 

365 runName: str 

366 Run Name, which will become part of the fileRoot. 

367 fileRoot: bool, opt 

368 Flag to update the fileRoot with the runName. Default True. 

369 """ 

370 self.runName = runName 

371 if updateFileRoot: 

372 self._buildFileRoot() 

373 

374 def writeDb(self, resultsDb=None, outfileSuffix=None): 

375 """Write the metricValues to the database 

376 """ 

377 if outfileSuffix is not None: 

378 outfile = self.fileRoot + '_' + outfileSuffix + '.npz' 

379 else: 

380 outfile = self.fileRoot + '.npz' 

381 if resultsDb is not None: 

382 metricId = resultsDb.updateMetric(self.metric.name, self.slicer.slicerName, 

383 self.runName, self.constraint, 

384 self.metadata, outfile) 

385 resultsDb.updateDisplay(metricId, self.displayDict) 

386 

387 def write(self, comment='', outDir='.', outfileSuffix=None, resultsDb=None): 

388 """Write metricValues (and associated metadata) to disk. 

389 

390 Parameters 

391 ---------- 

392 comment : Optional[str] 

393 Any additional comments to add to the output file 

394 outDir : Optional[str] 

395 The output directory 

396 outfileSuffix : Optional[str] 

397 Additional suffix to add to the output files (typically a numerical suffix for movies) 

398 resultsD : Optional[ResultsDb] 

399 Results database to store information on the file output 

400 """ 

401 if outfileSuffix is not None: 

402 outfile = self.fileRoot + '_' + outfileSuffix + '.npz' 

403 else: 

404 outfile = self.fileRoot + '.npz' 

405 self.slicer.writeData(os.path.join(outDir, outfile), 

406 self.metricValues, 

407 metricName=self.metric.name, 

408 simDataName=self.runName, 

409 constraint=self.constraint, 

410 metadata=self.metadata + comment, 

411 displayDict=self.displayDict, 

412 plotDict=self.plotDict) 

413 if resultsDb is not None: 

414 self.writeDb(resultsDb=resultsDb) 

415 

416 def outputJSON(self): 

417 """Set up and call the baseSlicer outputJSON method, to output to IO string. 

418 

419 Returns 

420 ------- 

421 io 

422 IO object containing JSON data representing the metric bundle data. 

423 """ 

424 io = self.slicer.outputJSON(self.metricValues, 

425 metricName=self.metric.name, 

426 simDataName=self.runName, 

427 metadata=self.metadata, 

428 plotDict=self.plotDict) 

429 return io 

430 

431 def read(self, filename): 

432 """Read metricValues and associated metadata from disk. 

433 Overwrites any data currently in metricbundle. 

434 

435 Parameters 

436 ---------- 

437 filename : str 

438 The file from which to read the metric bundle data. 

439 """ 

440 if not os.path.isfile(filename): 

441 raise IOError('%s not found' % filename) 

442 

443 self._resetMetricBundle() 

444 # Set up a base slicer to read data (we don't know type yet). 

445 baseslicer = slicers.BaseSlicer() 

446 # Use baseslicer to read file. 

447 metricValues, slicer, header = baseslicer.readData(filename) 

448 self.slicer = slicer 

449 self.metricValues = metricValues 

450 self.metricValues.fill_value = slicer.badval 

451 # It's difficult to reinstantiate the metric object, as we don't 

452 # know what it is necessarily -- the metricName can be changed. 

453 self.metric = metrics.BaseMetric() 

454 # But, for plot label building, we do need to try to recreate the 

455 # metric name and units. 

456 self.metric.units = '' 

457 if header is not None: 

458 self.metric.name = header['metricName'] 

459 if 'plotDict' in header: 

460 if 'units' in header['plotDict']: 

461 self.metric.units = header['plotDict']['units'] 

462 self.runName = header['simDataName'] 

463 try: 

464 self.constraint = header['constraint'] 

465 except KeyError: 

466 self.constraint = header['sqlconstraint'] 

467 self.metadata = header['metadata'] 

468 if 'plotDict' in header: 

469 self.setPlotDict(header['plotDict']) 

470 if 'displayDict' in header: 

471 self.setDisplayDict(header['displayDict']) 

472 if self.metadata is None: 

473 self._buildMetadata() 

474 path, head = os.path.split(filename) 

475 self.fileRoot = head.replace('.npz', '') 

476 self.setPlotFuncs(None) 

477 

478 def computeSummaryStats(self, resultsDb=None): 

479 """Compute summary statistics on metricValues, using summaryMetrics (metricbundle list). 

480 

481 Parameters 

482 ---------- 

483 resultsDb : Optional[ResultsDb] 

484 ResultsDb object to use to store the summary statistic values on disk. 

485 """ 

486 if self.summaryValues is None: 

487 self.summaryValues = {} 

488 if self.summaryMetrics is not None: 

489 # Build array of metric values, to use for (most) summary statistics. 

490 rarr_std = np.array(list(zip(self.metricValues.compressed())), 

491 dtype=[('metricdata', self.metricValues.dtype)]) 

492 for m in self.summaryMetrics: 

493 # The summary metric colname should already be set to 'metricdata', but in case it's not: 

494 m.colname = 'metricdata' 

495 summaryName = m.name.replace(' metricdata', '').replace(' None', '') 

496 if hasattr(m, 'maskVal'): 

497 # summary metric requests to use the mask value, as specified by itself, 

498 # rather than skipping masked vals. 

499 rarr = np.array(list(zip(self.metricValues.filled(m.maskVal))), 

500 dtype=[('metricdata', self.metricValues.dtype)]) 

501 else: 

502 rarr = rarr_std 

503 if np.size(rarr) == 0: 

504 summaryVal = self.slicer.badval 

505 else: 

506 summaryVal = m.run(rarr) 

507 self.summaryValues[summaryName] = summaryVal 

508 # Add summary metric info to results database, if applicable. 

509 if resultsDb: 

510 metricId = resultsDb.updateMetric(self.metric.name, self.slicer.slicerName, 

511 self.runName, self.constraint, self.metadata, None) 

512 resultsDb.updateSummaryStat(metricId, summaryName=summaryName, summaryValue=summaryVal) 

513 

514 def reduceMetric(self, reduceFunc, reducePlotDict=None, reduceDisplayDict=None): 

515 """Run 'reduceFunc' (any function that operates on self.metricValues). 

516 Typically reduceFunc will be the metric reduce functions, as they are tailored to expect the 

517 metricValues format. 

518 reduceDisplayDict and reducePlotDicts are displayDicts and plotDicts to be 

519 applied to the new metricBundle. 

520 

521 Parameters 

522 ---------- 

523 reduceFunc : Func 

524 Any function that will operate on self.metricValues (typically metric.reduce* function). 

525 reducePlotDict : Optional[dict] 

526 Plot dictionary for the results of the reduce function. 

527 reduceDisplayDict : Optional[dict] 

528 Display dictionary for the results of the reduce function. 

529 

530 Returns 

531 ------- 

532 MetricBundle 

533 New metric bundle, inheriting metadata from this metric bundle, but containing the new 

534 metric values calculated with the 'reduceFunc'. 

535 """ 

536 # Generate a name for the metric values processed by the reduceFunc. 

537 rName = reduceFunc.__name__.replace('reduce', '') 

538 reduceName = self.metric.name + '_' + rName 

539 # Set up metricBundle to store new metric values, and add plotDict/displayDict. 

540 newmetric = deepcopy(self.metric) 

541 newmetric.name = reduceName 

542 newmetric.metricDtype = 'float' 

543 if reducePlotDict is not None: 

544 if 'units' in reducePlotDict: 

545 newmetric.units = reducePlotDict['units'] 

546 newmetricBundle = MetricBundle(metric=newmetric, slicer=self.slicer, 

547 stackerList=self.stackerList, 

548 constraint=self.constraint, 

549 metadata=self.metadata, 

550 runName=self.runName, 

551 plotDict=None, plotFuncs=self.plotFuncs, 

552 displayDict=None, 

553 summaryMetrics=self.summaryMetrics, 

554 mapsList=self.mapsList, fileRoot='') 

555 # Build a new output file root name. 

556 newmetricBundle._buildFileRoot() 

557 # Add existing plotDict (except for title/xlabels etc) into new plotDict. 

558 for k, v in self.plotDict.items(): 

559 if k not in newmetricBundle.plotDict: 

560 newmetricBundle.plotDict[k] = v 

561 # Update newmetricBundle's plot dictionary with any set explicitly by reducePlotDict. 

562 newmetricBundle.setPlotDict(reducePlotDict) 

563 # Copy the parent metric's display dict into the reduce display dict. 

564 newmetricBundle.setDisplayDict(self.displayDict) 

565 # Set the reduce function display 'order' (this is set in the BaseMetric 

566 # by default, but can be overriden in a metric). 

567 order = newmetric.reduceOrder[rName] 

568 newmetricBundle.displayDict['order'] = order 

569 # And then update the newmetricBundle's display dictionary with any set 

570 # explicitly by reduceDisplayDict. 

571 newmetricBundle.setDisplayDict(reduceDisplayDict) 

572 # Set up new metricBundle's metricValues masked arrays, copying metricValue's mask. 

573 newmetricBundle.metricValues = ma.MaskedArray(data=np.empty(len(self.slicer), 'float'), 

574 mask=self.metricValues.mask, 

575 fill_value=self.slicer.badval) 

576 # Fill the reduced metric data using the reduce function. 

577 for i, (mVal, mMask) in enumerate(zip(self.metricValues.data, self.metricValues.mask)): 

578 if not mMask: 

579 newmetricBundle.metricValues.data[i] = reduceFunc(mVal) 

580 return newmetricBundle 

581 

582 def plot(self, plotHandler=None, plotFunc=None, outfileSuffix=None, savefig=False): 

583 """ 

584 Create all plots available from the slicer. plotHandler holds the output directory info, etc. 

585 

586 Parameters 

587 ---------- 

588 plotHandler : Optional[PlotHandler] 

589 The plotHandler saves the output location and resultsDb connection for a set of plots. 

590 plotFunc : Optional[BasePlotter] 

591 Any plotter function. If not specified, the plotters in self.plotFuncs will be used. 

592 outfileSuffix : Optional[str] 

593 Optional string to append to the end of the plot output files. 

594 Useful when creating sequences of images for movies. 

595 savefig : Optional[bool] 

596 Flag indicating whether or not to save the figure to disk. Default is False. 

597 

598 Returns 

599 ------- 

600 dict 

601 Dictionary of plotType:figure number key/value pairs, indicating what plots were created 

602 and what matplotlib figure numbers were used. 

603 """ 

604 # Generate a plotHandler if none was set. 

605 if plotHandler is None: 

606 plotHandler = plots.PlotHandler(savefig=savefig) 

607 # Make plots. 

608 if plotFunc is not None: 

609 if isinstance(plotFunc, plots.BasePlotter): 

610 plotFunc = plotFunc 

611 else: 

612 plotFunc = plotFunc() 

613 

614 plotHandler.setMetricBundles([self]) 

615 plotHandler.setPlotDicts(plotDicts=[self.plotDict], reset=True) 

616 madePlots = {} 

617 if plotFunc is not None: 

618 fignum = plotHandler.plot(plotFunc, outfileSuffix=outfileSuffix) 

619 madePlots[plotFunc.plotType] = fignum 

620 else: 

621 for plotFunc in self.plotFuncs: 

622 fignum = plotHandler.plot(plotFunc, outfileSuffix=outfileSuffix) 

623 madePlots[plotFunc.plotType] = fignum 

624 return madePlots