Coverage for larch/plot/bokeh_xafsplots.py: 0%

667 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2024-10-16 21:04 +0000

1#!/usr/bin/env python 

2""" 

3Plotting macros for XAFS data sets and fits 

4 

5 Function Description of what is plotted 

6 ---------------- ----------------------------------------------------- 

7 plot_mu() mu(E) for XAFS data group in various forms 

8 plot_bkg() mu(E) and background mu0(E) for XAFS data group 

9 plot_chik() chi(k) for XAFS data group 

10 plot_chie() chi(E) for XAFS data group 

11 plot_chir() chi(R) for XAFS data group 

12 plot_chifit() chi(k) and chi(R) for fit to feffit dataset 

13 plot_path_k() chi(k) for a single path of a feffit dataset 

14 plot_path_r() chi(R) for a single path of a feffit dataset 

15 plot_paths_k() chi(k) for model and all paths of a feffit dataset 

16 plot_paths_r() chi(R) for model and all paths of a feffit dataset 

17 plot_diffkk() plots from DIFFKK 

18 ---------------- ----------------------------------------------------- 

19""" 

20 

21import os 

22import numpy as np 

23import time 

24import logging 

25from copy import deepcopy 

26 

27from larch import Group 

28from larch.math import index_of 

29from larch.xafs import cauchy_wavelet, etok 

30 

31def nullfunc(*args, **kws): 

32 pass 

33 

34get_display = _plot = _oplot = _newplot = _fitplot = _plot_text = nullfunc 

35 

36HAS_BOKEH = True 

37try: 

38 import bokeh 

39except ImportError: 

40 HAS_BOKEH = False 

41 

42if HAS_BOKEH: 

43 from bokeh import plotting as bplot 

44 from bokeh.plotting import figure, show 

45 from bokeh.io import output_notebook, curdoc 

46 from bokeh.io import show as bokeh_show 

47 

48 from bokeh.core.properties import field 

49 from bokeh.models import ColumnDataSource, LinearAxis, Grid, VSpan, Range1d 

50 

51 

52 

53LineColors = ('#1f77b4', '#d62728', '#2ca02c', '#ff7f0e', '#9467bd', 

54 '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf') 

55LineStyles = ('solid', 'dashed', 'dotted') 

56NCOLORS = len(LineColors) 

57NSTYLES = len(LineStyles) 

58 

59FIGSTYLE = dict(width=800, height=500, 

60 toolbar_location='above', 

61 tools="pan,box_zoom,save,reset", 

62 

63 # showlegend=True, hovermode='closest', 

64 # legend=dict(borderwidth=0.5, bgcolor='#F2F2F2'), 

65 # # orientation='v') #, x=0.1, y=1.15)# , yanchor='top'), 

66 #plot_bgcolor='#FDFDFF', 

67 #xaxis=dict(showgrid=True, gridcolor='#D8D8D8', 

68 # color='#004', zerolinecolor='#DDD'), 

69 # 

70 # yaxis=dict(showgrid=True, gridcolor='#D8D8D8', 

71 # color='#004', zerolinecolor='#DDD') 

72 ) 

73 

74def set_label_weight(label, w): 

75 return label.replace('_w_', '{0:g}'.format(w)) 

76 

77# common XAFS plot labels 

78def chirlab(kweight, show_mag=True, show_real=False, show_imag=False): 

79 """generate chi(R) label for a kweight 

80 

81 Arguments 

82 ---------- 

83 kweight k-weight to use (required) 

84 show_mag bool whether to plot |chi(R)| [True] 

85 show_real bool whether to plot Re[chi(R)] [False] 

86 show_imag bool whether to plot Im[chi(R)] [False] 

87 """ 

88 ylab = [] 

89 if show_mag: ylab.append(plotlabels.chirmag) 

90 if show_real: ylab.append(plotlabels.chirre) 

91 if show_imag: ylab.append(plotlabels.chirim) 

92 if len(ylab) > 1: ylab = [plotlabels.chir] 

93 return set_label_weight(ylab[0], kweight+1) 

94#enddef 

95 

96# note: 

97# to make life easier for MathJax/Plotly/Bokeh/IPython 

98# we have just replaced "\AA" with "\unicode{x212B}" 

99 

100plotlabels = Group(k = r'$$k \rm\,(\unicode{x212B}^{-1})$$', 

101 r = r'$$R \rm\,(\unicode{x212B})$$', 

102 energy = r'$$E\rm\,(eV)$$', 

103 ewithk = r'$$E\rm\,(eV)$$' + '\n' + r'$$[k \rm\,(\unicode{x212B}^{-1})]$$', 

104 mu = r'$$\mu(E)$$', 

105 norm = r'normalized $$\mu(E)$$', 

106 flat = r'flattened $$\mu(E)$$', 

107 deconv = r'deconvolved $$\mu(E)$$', 

108 dmude = r'$$d\mu_{\mathrm norm}(E)/dE$$', 

109 d2mude = r'$$d^2\mu_{\rm norm}(E)/dE^2$$', 

110 chie = r'$$\chi(E)$$', 

111 chie0 = r'$$\chi(E)$$', 

112 chie1 = r'$$E\chi(E) \rm\, (eV)$$', 

113 chiew = r'$$E^{{_w_}\chi(E) \rm\,(eV^{_w_})$$', 

114 chikw = r'$$k^{{_w_}}\chi(k) \rm\,(\unicode{x212B}^{{-_w_}})$$', 

115 chi0 = r'$$\chi(k)$$', 

116 chi1 = r'$$k\chi(k) \rm\,(\unicode{x212B}^{-1})$$', 

117 chi2 = r'$$k^2\chi(k) \rm\,(\unicode{x212B}^{-2})$$', 

118 chi3 = r'$$k^3\chi(k) \rm\,(\unicode{x212B}^{-3})$$', 

119 chir = r'$$\chi(R) \rm\,(\unicode{x212B}^{{-_w_}})$$', 

120 chirmag = r'$$|\chi(R)| \rm\,(\unicode{x212B}^{{-_w_}})$$', 

121 chirre = r'$${{\rm Re}}[\chi(R)] \rm\,(\unicode{x212B}^{{-_w_}})$$', 

122 chirim = r'$${{\rm Im}}[\chi(R)] \rm\,(\unicode{x212B}^{{-_w_}})$$', 

123 chirpha = r'$${{\rm Phase}}[\chi(R)] \rm\,(\unicode{x212B}^{{-_w_}})$$', 

124 e0color = '#B2B282', 

125 chirlab = chirlab) 

126 

127 

128def safetitle(t): 

129 if "'" in t: 

130 t = t.replace("'", "\\'") 

131 return t 

132 

133def _get_title(dgroup, title=None): 

134 """get best title for group""" 

135 if title is not None: 

136 return safetitle(title) 

137 data_group = getattr(dgroup, 'data', None) 

138 

139 for attr in ('title', 'plot_title', 'filename', 'name', '__name__'): 

140 t = getattr(dgroup, attr, None) 

141 if t is not None: 

142 if attr == 'filename': 

143 folder, file = os.path.split(t) 

144 if folder == '': 

145 t = file 

146 else: 

147 top, folder = os.path.split(folder) 

148 t = '/'.join((folder, file)) 

149 return safetitle(t) 

150 if data_group is not None: 

151 t = getattr(data_group, attr, None) 

152 if t is not None: 

153 return t 

154 return safetitle(repr(dgroup)) 

155 

156 

157def _get_kweight(dgroup, kweight=None): 

158 if kweight is not None: 

159 return kweight 

160 callargs = getattr(dgroup, 'callargs', None) 

161 ftargs = getattr(callargs, 'xftf', {'kweight':0}) 

162 return ftargs['kweight'] 

163 

164def _get_erange(dgroup, emin=None, emax=None): 

165 """get absolute emin/emax for data range, allowing using 

166 values relative to e0. 

167 """ 

168 dat_emin, dat_emax = min(dgroup.energy)-100, max(dgroup.energy)+100 

169 e0 = getattr(dgroup, 'e0', 0.0) 

170 if emin is not None: 

171 if not (emin > dat_emin and emin < dat_emax): 

172 if emin+e0 > dat_emin and emin+e0 < dat_emax: 

173 emin += e0 

174 else: 

175 emin = dat_emin 

176 if emax is not None: 

177 if not (emax > dat_emin and emax < dat_emax): 

178 if emax+e0 > dat_emin and emax+e0 < dat_emax: 

179 emax += e0 

180 else: 

181 emax = dat_emax 

182 return emin, emax 

183 

184def extend_plotrange(x, y, xmin=None, xmax=None, extend=0.10): 

185 """return plot limits to extend a plot range for x, y pairs""" 

186 xeps = min(np.diff(x)) / 5. 

187 if xmin is None: 

188 xmin = min(x) 

189 if xmax is None: 

190 xmax = max(x) 

191 

192 xmin = max(min(x), xmin-5) 

193 xmax = min(max(x), xmax+5) 

194 

195 i0 = index_of(x, xmin + xeps) 

196 i1 = index_of(x, xmax + xeps) + 1 

197 

198 xspan = x[i0:i1] 

199 xrange = max(xspan) - min(xspan) 

200 yspan = y[i0:i1] 

201 yrange = max(yspan) - min(yspan) 

202 

203 return (min(xspan) - extend * xrange, 

204 max(xspan) + extend * xrange, 

205 min(yspan) - extend * yrange, 

206 max(yspan) + extend * yrange) 

207 

208 

209def redraw(win=1, xmin=None, xmax=None, ymin=None, ymax=None, 

210 dymin=None, dymax=None, 

211 show_legend=True, stacked=False): 

212 pass 

213 

214 

215class BokehFigure: 

216 """wrapping of Bokeh Figure 

217 """ 

218 def __init__(self, style=None, **kws): 

219 

220 try: 

221 self.in_ipython = __IPYTHON__ 

222 except NameError: 

223 self.in_ipython = False 

224 if self.in_ipython: 

225 output_notebook() 

226 style = self.style = deepcopy(FIGSTYLE) 

227 if style is not None: 

228 self.style.update(style) 

229 

230 

231 self.fig = bplot.figure(width=style['width'], height=style['height'], 

232 toolbar_location=style['toolbar_location'], 

233 tools=style['tools']) 

234 

235 self.fig.xgrid.grid_line_color = '#D8D8D8' 

236 self.fig.ygrid.grid_line_color = '#D8D8D8' 

237 self.fig.legend.click_policy = 'hide' 

238 self.fig 

239 self.clear() 

240 

241 

242 def clear(self): 

243 self.y2_axes = None 

244 self.traces = [] 

245 

246 def add_plot(self, x, y, label=None, color=None, linewidth=3, 

247 style='solid', marker=None, y2label=None, side='left'): 

248 itrace = len(self.traces) 

249 

250 if label is None: 

251 label = "trace %d" % (1+itrace) 

252 if color is None: 

253 color = LineColors[itrace % NCOLORS] 

254 if style is None: 

255 style = LineStyles[ int(itrace*1.0 / NCOLORS) % NSTYLES] 

256 

257 opts = {'line_color': color, 'line_width': linewidth, 

258 'legend_label': label} 

259 

260 if side == 'right': 

261 if y2label is None: 

262 y2label = label 

263 

264 ymin, ymax = min(y), max(y) 

265 yr = abs(ymax-ymin) 

266 try: 

267 if yr/(ymin+ymax) > 1.e-18: 

268 ymin, ymax = ymin-0.02*yr, ymax+0.02*yr 

269 except: 

270 pass 

271 self.fig.extra_y_ranges['y2'] = Range1d(ymin, ymax) 

272 self.y2_axes = LinearAxis(axis_label=y2label, y_range_name='y2') 

273 

274 self.fig.add_layout(self.y2_axes, 'left') 

275 opts['y_range_name'] = 'y2' 

276 

277 trace = self.fig.line(x, y, **opts) 

278 self.traces.append((trace, x, y, opts)) 

279 self.fig.legend.click_policy='hide' 

280 

281 def add_vline(self, x=None, line_width=1, line_color='#666666', **kws): 

282 if x is None: 

283 return 

284 

285 source = ColumnDataSource({'x': [x], 'y': [line_width]}) 

286 glyph = VSpan(x=x, line_width=line_width, line_color=line_color) 

287 self.fig.add_glyph(source, glyph) 

288 

289 def set_xrange(self, xmin, xmax): 

290 

291 if xmin is not None: 

292 self.fig.x_range.start = xmin 

293 if xmax is not None: 

294 self.fig.x_range.end = xmax 

295 

296 

297 def set_yrange(self, ymin, ymax): 

298 if ymin is not None: 

299 self.fig.y_range.start = ymin 

300 if ymax is not None: 

301 self.fig.y_range.end = ymax 

302 

303 

304 def set_ylog(self, ylog=True): 

305 ytype = 'log' if ylog else 'linear' 

306 self.fig.y_axis_type = ytype 

307 

308 

309 def set_style(self, title=None, xlabel=None, ylabel=None, y2label=None): 

310 if title is not None: 

311 self.fig.title.text = title 

312 if xlabel is not None: 

313 self.fig.xaxis.axis_label = xlabel 

314 if ylabel is not None: 

315 self.fig.yaxis.axis_label = ylabel 

316 if y2label is not None and self.y2_axes is not None: 

317 self.y2_axes.axis_label = y2label 

318 

319 

320 def show(self, title=None, xlabel=None, ylabel=None, y2label=None, 

321 xmin=None, xmax=None, ymin=None, ymax=None, show=True): 

322 self.set_style(title=title, xlabel=xlabel, ylabel=ylabel, y2label=y2label) 

323 self.set_xrange(xmin, xmax) 

324 self.set_yrange(ymin, ymax) 

325 

326 if show: 

327 curdoc().add_root(self.fig) 

328 bokeh_show(self.fig) 

329 return self 

330 

331def plot(xdata, ydata, dy=None, fig=None, label=None, xlabel=None, 

332 ylabel=None, y2label=None, title=None, side='left', ylog_scale=None, 

333 xlog_scale=None, grid=None, xmin=None, xmax=None, ymin=None, 

334 ymax=None, color=None, style='solid', alpha=None, fill=False, 

335 drawstyle=None, linewidth=2, marker=None, markersize=None, 

336 show_legend=None, bgcolor=None, framecolor=None, gridcolor=None, 

337 textcolor=None, labelfontsize=None, titlefontsize=None, 

338 legendfontsize=None, fullbox=None, axes_style=None, zorder=None, show=True): 

339 """emulate wxmplot plot() function, probably incompletely""" 

340 

341 if fig is None: 

342 fig = BokehFigure() 

343 

344 if xmin is None: 

345 xmin = min(xdata) 

346 if xmax is None: 

347 xmax = max(xdata) 

348 if ymin is None: 

349 ymin = min(ydata) 

350 if ymax is None: 

351 ymax = max(ydata) 

352 

353 if xmin is not None and xmax is not None: 

354 xr = abs(xmax-xmin) 

355 try: 

356 if xr/(xmin+xmax) > 1.e-18: 

357 xmin, xmax = xmin-0.02*xr, xmax+0.02*xr 

358 except: 

359 pass 

360 if ymin is not None and ymay is not None: 

361 yr = abs(ymax-ymin) 

362 try: 

363 if yr/(ymin+ymax) > 1.e-18: 

364 ymin, ymax = ymin-0.02*yr, ymax+0.02*yr 

365 except: 

366 pass 

367 

368 fig.add_plot(xdata, ydata, label=label, color=color, linewidth=linewidth, 

369 style=style, marker=marker, side=side) 

370 

371 return fig.show(title=title, xlabel=xlabel, ylabel=ylabel, y2label=y2label, 

372 xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax, show=show) 

373 

374 

375def multi_plot(plotsets): 

376 """plot multiple traces with an array of dictionaries emulating 

377 multiplot calls to plot: 

378 

379 instead of 

380 

381 >>> plot(x1, y1, label='thing1', color='blue') 

382 >>> plot(x2, y2, label='thing2', color='red') 

383 

384 you can do 

385 

386 >>> multi_plot([dict(xdata=x1, ydata=y1, label='thing1', color='blue'), 

387 dict(xdata=x2, ydata=y2, label='thing2', color='red')]) 

388 

389 """ 

390 for pset in plotsets[:]: 

391 side = pset.get('side', None) 

392 fig = BokehFigure() 

393 fig.clear() 

394 

395 sopts = dict(title=None, xlabel=None, ylabel=None) 

396 ropts = dict(xmin=None, xmax=None, ymin=None, ymax=None) 

397 

398 for pset in plotsets[:]: 

399 xdata = pset['xdata'] 

400 ydata = pset['ydata'] 

401 popts = dict(label=None, color=None, side='left', style=None, 

402 linewidth=3, marker=None) 

403 for w in ('label', 'color', 'style', 'linewidth', 'marker', 'side'): 

404 if w in pset: 

405 popts[w] = pset[w] 

406 for w in ('title', 'xlabel', 'ylabel'): 

407 if w in pset: 

408 sopts[w] = pset[w] 

409 

410 for w in ('xmin', 'xmax', 'ymin', 'ymax'): 

411 if w in pset: 

412 ropts[w] = pset[w] 

413 

414 fig.add_plot(xdata, ydata, **popts) 

415 

416 sopts['xaxis_title'] = sopts.pop('xlabel') 

417 sopts['yaxis_title'] = sopts.pop('ylabel') 

418 fig.style.update(sopts) 

419 return fig.show(**ropts) 

420 

421def plot_mu(dgroup, show_norm=False, show_flat=False, show_deriv=False, 

422 show_pre=False, show_post=False, show_e0=False, with_deriv=False, 

423 emin=None, emax=None, label='mu', offset=0, title=None, fig=None, show=True): 

424 """ 

425 plot_mu(dgroup, norm=False, deriv=False, show_pre=False, show_post=False, 

426 show_e0=False, show_deriv=False, emin=None, emax=None, label=None, 

427 show=True, fig=None) 

428 

429 Plot mu(E) for an XAFS data group in various forms 

430 

431 Arplguments 

432 ---------- 

433 dgroup group of XAFS data after pre_edge() results (see Note 1) 

434 show_norm bool whether to show normalized data [False] 

435 show_flat bool whether to show flattened, normalized data [False] 

436 show_deriv bool whether to show derivative of normalized data [False] 

437 show_pre bool whether to show pre-edge curve [False] 

438 show_post bool whether to show post-edge curve [False] 

439 show_e0 bool whether to show E0 [False] 

440 with_deriv bool whether to show deriv (dmu/de) together with mu [False] 

441 emin min energy to show, absolute or relative to E0 [None, start of data] 

442 emax max energy to show, absolute or relative to E0 [None, end of data] 

443 label string for label [None: 'mu', `dmu/dE', or 'mu norm'] 

444 title string for plot title [None, may use filename if available] 

445 offset vertical offset to use for y-array [0] 

446 show display the BokehFig now [True] 

447 fig BokehFig to reuse [None] 

448 

449 Notes 

450 ----- 

451 1. The input data group must have the following attributes: 

452 energy, mu, norm, e0, pre_edge, edge_step 

453 """ 

454 if not HAS_BOKEH: 

455 logging.getLogger().error('Need bokeh installed') 

456 return 

457 

458 if hasattr(dgroup, 'mu'): 

459 mu = dgroup.mu 

460 elif hasattr(dgroup, 'mutrans'): 

461 mu = dgroup.mutrans 

462 elif hasattr(dgroup, 'mufluor'): 

463 mu = dgroup.mufluor 

464 else: 

465 raise ValueError("XAFS data group has no array for mu") 

466 #endif 

467 ylabel = plotlabels.mu 

468 if label is None: 

469 label = getattr(dgroup, 'filename', 'mu') 

470 #endif 

471 if show_deriv: 

472 mu = dgroup.dmude 

473 ylabel = f"{ylabel} (deriv)" 

474 dlabel = plotlabels.dmude 

475 elif show_norm: 

476 mu = dgroup.norm 

477 ylabel = plotlabels.norm 

478 dlabel = plotlabels.norm 

479 #endif 

480 elif show_flat: 

481 mu = dgroup.flat 

482 ylabel = f"{ylabel} (flat)" 

483 dlabel = plotlabels.flat 

484 #endif 

485 emin, emax = _get_erange(dgroup, emin, emax) 

486 title = _get_title(dgroup, title=title) 

487 

488 if fig is None: 

489 fig = BokehFigure() 

490 fig.add_plot(dgroup.energy, mu+offset, label=label) 

491 

492 y2label = None 

493 if with_deriv: 

494 y2label = plotlabels.dmude 

495 fig.add_plot(dgroup.energy, dgroup.dmude+offset, label=ylabel, y2label=y2label, side='right') 

496 else: 

497 if not show_norm and show_pre: 

498 fig.add_plot(dgroup.energy, dgroup.pre_edge+offset, label='pre_edge') 

499 if not show_norm and show_post: 

500 fig.add_plot(dgroup.energy, dgroup.post_edge+offset, label='post_edge') 

501 

502 if show_e0: 

503 fig.add_vline(x=dgroup.e0, line_width=2, line_dash="dash", line_color="#AAC") 

504 

505 return fig.show(title=title, xlabel=plotlabels.energy, ylabel=ylabel, 

506 y2label=y2label, xmin=emin, xmax=emax, show=show) 

507 

508 

509def plot_bkg(dgroup, norm=True, emin=None, emax=None, show_e0=False, 

510 label=None, title=None, offset=0): 

511 """ 

512 plot_bkg(dgroup, norm=True, emin=None, emax=None, show_e0=False, label=None, new=True) 

513 

514 Plot mu(E) and background mu0(E) for XAFS data group 

515 

516 Arguments 

517 ---------- 

518 dgroup group of XAFS data after autobk() results (see Note 1) 

519 norm bool whether to show normalized data [True] 

520 emin min energy to show, absolute or relative to E0 [None, start of data] 

521 emax max energy to show, absolute or relative to E0 [None, end of data] 

522 show_e0 bool whether to show E0 [False] 

523 label string for label [``None``: 'mu'] 

524 title string for plot titlte [None, may use filename if available] 

525 offset vertical offset to use for y-array [0] 

526 

527 Notes 

528 ----- 

529 1. The input data group must have the following attributes: 

530 energy, mu, bkg, norm, e0, pre_edge, edge_step, filename 

531 """ 

532 if hasattr(dgroup, 'mu'): 

533 mu = dgroup.mu 

534 elif hasattr(dgroup, 'mutrans'): 

535 mu = dgroup.mutrans 

536 else: 

537 raise ValueError("XAFS data group has no array for mu") 

538 

539 bkg = dgroup.bkg 

540 ylabel = plotlabels.mu 

541 if label is None: 

542 label = 'mu' 

543 

544 emin, emax = _get_erange(dgroup, emin, emax) 

545 if norm: 

546 mu = dgroup.norm 

547 bkg = (dgroup.bkg - dgroup.pre_edge) / dgroup.edge_step 

548 ylabel = plotlabels.norm 

549 label = ylabel 

550 #endif 

551 title = _get_title(dgroup, title=title) 

552 

553 fig = BokehFigure() 

554 fig.add_plot(dgroup.energy, mu+offset, label=label) 

555 fig.add_plot(dgroup.energy, bkg+offset, label='bkg') 

556 

557 if show_e0: 

558 fig.add_vline(x=dgroup.e0, line_width=2, line_dash="dash", line_color="#AAC") 

559 

560 return fig.show(title=title, xlabel=plotlabels.energy, ylabel=ylabel, xmin=emin, xmax=emax) 

561 

562 

563def plot_chie(dgroup, emin=-5, emax=None, label=None, title=None, 

564 eweight=0, offset=0, how_k=False, fig=None, show=True): 

565 """ 

566 plot_chie(dgroup, emin=None, emax=None, label=None, new=True, fig=None): 

567 

568 Plot chi(E) for XAFS data group 

569 

570 Arguments 

571 ---------- 

572 dgroup group of XAFS data after autobk() results (see Note 1) 

573 emin min energy to show, absolute or relative to E0 [-25] 

574 emax max energy to show, absolute or relative to E0 [None, end of data] 

575 label string for label [``None``: 'mu'] 

576 title string for plot title [None, may use filename if available] 

577 eweight energy weightingn for energisdef es>e0 [0] 

578 offset vertical offset to use for y-array [0] 

579 show display the BokehFigure now [True] 

580 fig BokehFigure to re-use [None] 

581 

582 Notes 

583 ----- 

584 1. The input data group must have the following attributes: 

585 energy, mu, bkg, norm, e0, pre_edge, edge_step, filename 

586 """ 

587 if hasattr(dgroup, 'mu'): 

588 mu = dgroup.mu 

589 elif hasattr(dgroup, 'mutrans'): 

590 mu = dgroup.mutrans 

591 else: 

592 raise ValueError("XAFS data group has no array for mu") 

593 #endif 

594 e0 = dgroup.e0 

595 chie = (mu - dgroup.bkg) 

596 ylabel = plotlabels.chie 

597 if abs(eweight) > 1.e-2: 

598 chie *= (dgroup.energy-e0)**(eweight) 

599 ylabel = set_label_weight(plotlabels.chiew, eweight) 

600 xlabel = plotlabels.energy 

601 

602 emin, emax = _get_erange(dgroup, emin, emax) 

603 if emin is not None: 

604 emin = emin - e0 

605 if emax is not None: 

606 emax = emax - e0 

607 

608 title = _get_title(dgroup, title=title) 

609 def ek_formatter(x, pos): 

610 ex = float(x) 

611 if ex < 0: 

612 s = '' 

613 else: 

614 s = f"\n[{etok(ex):.2f}]" 

615 return r"%1.4g%s" % (x, s) 

616 

617 if fig is None: 

618 fig = BokehFigure() 

619 fig.add_plot(dgroup.energy-e0, chie+offset, label=label) 

620 return fig.show(title=title, xlabel=xlabel, ylabel=ylabel, xmin=emin, xmax=emax, show=show) 

621 

622def plot_chik(dgroup, kweight=None, kmax=None, show_window=True, 

623 scale_window=True, label=None, title=None, offset=0, show=True, fig=None): 

624 """ 

625 plot_chik(dgroup, kweight=None, kmax=None, show_window=True, label=None, 

626 fig=None) 

627 

628 Plot k-weighted chi(k) for XAFS data group 

629 

630 Arguments 

631 ---------- 

632 dgroup group of XAFS data after autobk() results (see Note 1) 

633 kweight k-weighting for plot [read from last xftf(), or 0] 

634 kmax max k to show [None, end of data] 

635 show_window bool whether to also plot k-window [True] 

636 scale_window bool whether to scale k-window to max |chi(k)| [True] 

637 label string for label [``None`` to use 'chi'] 

638 title string for plot title [None, may use filename if available] 

639 offset vertical offset to use for y-array [0] 

640 show display the BokehFig now [True] 

641 fig BokehFigure to re-use [None] 

642 

643 Notes 

644 ----- 

645 1. The input data group must have the following attributes: 

646 k, chi, kwin, filename 

647 """ 

648 kweight = _get_kweight(dgroup, kweight) 

649 chi = dgroup.chi * dgroup.k ** kweight 

650 

651 if label is None: 

652 label = 'chi' 

653 

654 title = _get_title(dgroup, title=title) 

655 

656 if fig is None: 

657 fig = BokehFigure() 

658 fig.add_plot(dgroup.k, chi+offset, label=label) 

659 

660 if show_window and hasattr(dgroup, 'kwin'): 

661 kwin = dgroup.kwin 

662 if scale_window: 

663 kwin = kwin*max(abs(chi)) 

664 fig.add_plot(dgroup.k, kwin+offset, label='window') 

665 

666 return fig.show(title=title, xlabel=plotlabels.k, xmin=0, xmax=kmax, 

667 ylabel=set_label_weight(plotlabels.chikw, kweight), 

668 show=show) 

669 

670def plot_chir(dgroup, show_mag=True, show_real=False, show_imag=False, 

671 show_window=False, rmax=None, label=None, title=None, 

672 offset=0, show=True, fig=None): 

673 """ 

674 plot_chir(dgroup, show_mag=True, show_real=False, show_imag=False, 

675 rmax=None, label=None, fig=None) 

676 

677 Plot chi(R) for XAFS data group 

678 

679 Arguments 

680 ---------- 

681 dgroup group of XAFS data after xftf() results (see Note 1) 

682 show_mag bool whether to plot |chi(R)| [True] 

683 show_real bool whether to plot Re[chi(R)] [False] 

684 show_imag bool whether to plot Im[chi(R)] [False] 

685 show_window bool whether to R-windw for back FT (will be scaled) [False] 

686 label string for label [``None`` to use 'chir'] 

687 title string for plot title [None, may use filename if available] 

688 rmax max R to show [None, end of data] 

689 offset vertical offset to use for y-array [0] 

690 show display the BokehFig now [True] 

691 fig BokehFigure to re-use [None] 

692 

693 Notes 

694 ----- 

695 1. The input data group must have the following attributes: 

696 r, chir_mag, chir_im, chir_re, kweight, filename 

697 """ 

698 kweight = _get_kweight(dgroup, None) 

699 

700 title = _get_title(dgroup, title=title) 

701 

702 ylabel = plotlabels.chirlab(kweight, show_mag=show_mag, 

703 show_real=show_real, show_imag=show_imag) 

704 

705 if not hasattr(dgroup, 'r'): 

706 print("group does not have chi(R) data") 

707 return 

708 #endif 

709 if label is None: 

710 label = 'chir' 

711 

712 if fig is None: 

713 fig = BokehFigure() 

714 if show_mag: 

715 fig.add_plot(dgroup.r, dgroup.chir_mag+offset, label=f'{label} (mag)') 

716 if show_real: 

717 fig.add_plot(dgroup.r, dgroup.chir_re+offset, label=f'{label} (real)') 

718 

719 if show_imag: 

720 fig.add_plot(dgroup.r, dgroup.chir_im+offset, label=f'{label} (imag)') 

721 

722 if show_window and hasattr(dgroup, 'rwin'): 

723 rwin = dgroup.rwin * max(dgroup.chir_mag) 

724 fig.add_plot(dgroup.r, rwin+offset, label='window') 

725 

726 return fig.show(title=title, xlabel=plotlabels.r, ylabel=ylabel, xmax=rmax, show=show) 

727 

728 

729def plot_chiq(dgroup, kweight=None, kmin=0, kmax=None, show_chik=False, label=None, 

730 title=None, offset=0, show_window=False, scale_window=True, 

731 show=True, fig=None): 

732 """ 

733 plot_chiq(dgroup, kweight=None, kmax=None, show_chik=False, label=None, 

734 new=True, win=1) 

735 

736 Plot Fourier filtered chi(k), optionally with k-weighted chi(k) for XAFS data group 

737 

738 Arguments 

739 ---------- 

740 dgroup group of XAFS data after autobk() results (see Note 1) 

741 kweight k-weighting for plot [read from last xftf(), or 0] 

742 kmax max k to show [None, end of data] 

743 show_chik bool whether to also plot k-weighted chi(k) [False] 

744 show_window bool whether to also plot FT k-window [False] 

745 scale_window bool whether to scale FT k-window to max |chi(q)| [True] 

746 label string for label [``None`` to use 'chi'] 

747 title string for plot title [None, may use filename if available] 

748 offset vertical offset to use for y-array [0] 

749 show display the BokehFig now [True] 

750 fig BokehFigure to re-use [None] 

751 

752 Notes 

753 ----- 

754 1. The input data group must have the following attributes: 

755 k, chi, kwin, filename 

756 """ 

757 kweight = _get_kweight(dgroup, kweight) 

758 nk = len(dgroup.k) 

759 chiq = dgroup.chiq_re[:nk] 

760 

761 if label is None: 

762 label = 'chi(q) (filtered)' 

763 

764 title = _get_title(dgroup, title=title) 

765 if fig is None: 

766 fig = BokehFigure() 

767 fig.add_plot(dgroup.k, chiq+offset, label=label) 

768 if kmax is None: 

769 kmax = max(dgroup.k) 

770 

771 if show_chik: 

772 chik = dgroup.chi * dgroup.k ** kweight 

773 fig.add_plot(dgroup.k, chik+offset, label='chi(k) (unfiltered)') 

774 

775 if show_window and hasattr(dgroup, 'kwin'): 

776 kwin = dgroup.kwin 

777 if scale_window: 

778 kwin = kwin*max(abs(chiq)) 

779 fig.add_plot(dgroup.k, kwin+offset, label='window') 

780 

781 ylabel = set_label_weight(plotlabels.chikw, kweight) 

782 return fig.show(title=title, xlabel=plotlabels.k, 

783 ylabel=ylabel, xmin=kmin, xmax=kmax, show=show) 

784 

785 

786 

787def plot_chifit(dataset, kmin=0, kmax=None, kweight=None, rmax=None, 

788 show_mag=True, show_real=False, show_imag=False, 

789 show_bkg=False, use_rebkg=False, title=None, offset=0): 

790 """ 

791 plot_chifit(dataset, kmin=0, kmax=None, rmax=None, 

792 show_mag=True, show_real=False, show_imag=False) 

793 

794 Plot k-weighted chi(k) and chi(R) for fit to feffit dataset 

795 

796 Arguments 

797 ---------- 

798 dataset feffit dataset, after running feffit() 

799 kmin min k to show [0] 

800 kmax max k to show [None, end of data] 

801 kweight kweight to show [None, taken from dataset] 

802 rmax max R to show [None, end of data] 

803 show_mag bool whether to plot |chidr(R)| [True] 

804 show_real bool whether to plot Re[chi(R)] [False] 

805 show_imag bool whether to plot Im[chi(R)] [False] 

806 title string for plot title [None, may use filename if available] 

807 offset vertical offset to use for y-array [0] 

808 

809 

810 """ 

811 if kweight is None: 

812 kweight = dataset.transform.kweight 

813 #endif 

814 if isinstance(kweight, (list, tuple, np.ndarray)): 

815 kweight=kweight[0] 

816 

817 title = _get_title(dataset, title=title) 

818 

819 mod = dataset.model 

820 dat = dataset.data 

821 if use_rebkg and hasattr(dataset, 'data_rebkg'): 

822 dat = dataset.data_rebkg 

823 title += ' (refined bkg)' 

824 

825 data_chik = dat.chi * dat.k**kweight 

826 model_chik = mod.chi * mod.k**kweight 

827 

828 # k-weighted chi(k) in first plot window 

829 fig = BokehFigure() 

830 fig.add_plot(dat.k, data_chik+offset, label='data') 

831 fig.add_plot(mod.k, model_chik+offset, label='fit') 

832 

833 ylabel = set_label_weight(plotlabels.chikw, kweight) 

834 fig.show(title=title, xlabel=plotlabels.k, 

835 ylabel=ylabel, xmin=kmin, xmax=kmax) 

836 

837 # chi(R) in first plot window 

838 rfig = BokehFigure() 

839 

840 if show_mag: 

841 rfig.add_plot(dat.r, dat.chir_mag+offset, label='|data|') 

842 rfig.add_plot(mod.r, mod.chir_mag+offset, label='|fit|') 

843 

844 if show_real: 

845 rfig.add_plot(dat.r, dat.chir_re+offset, label='Re[data]') 

846 rfig.add_plot(mod.r, mod.chir_re+offset, label='Re[fit]') 

847 if show_imag: 

848 rfig.add_plot(dat.r, dat.chir_im+offset, label='Im[data]') 

849 rfig.add_plot(mod.r, mod.chir_im+offset, label='Im[fit]') 

850 

851 ylabel = chirlab(kweight, show_mag=show_mag, show_real=show_real, show_imag=show_imag) 

852 rfig.show(title=title, xlabel=plotlabels.r, ylabel=ylabel, xmin=0, xmax=rmax) 

853 return fig, rfig 

854 

855def plot_path_k(dataset, ipath=0, kmin=0, kmax=None, offset=0, label=None, fig=None): 

856 """ 

857 plot_path_k(dataset, ipath, kmin=0, kmax=None, offset=0, label=None) 

858 

859 Plot k-weighted chi(k) for a single Path of a feffit dataset 

860 

861 Arguments 

862 ---------- 

863 dataset feffit dataset, after running feffit() 

864 ipath index of path, starting count at 0 [0] 

865 kmin min k to show [0] 

866 kmax max k to show [None, end of data] 

867 offset vertical offset to use for plot [0] 

868 label path label ['path %d' % ipath] 

869 fig BokehFigure for reuse 

870 """ 

871 kweight = dataset.transform.kweight 

872 path = dataset.pathlist[ipath] 

873 if label is None: 

874 label = 'path %i' % (1+ipath) 

875 title = _get_title(dataset, title=title) 

876 

877 chi_kw = offset + path.chi * path.k**kweight 

878 if fig is None: 

879 fig = BokehFigure() 

880 fig.add_plot(path.k, chi_kw, label=label) 

881 return fig.set_style(title=title, xlabel=plotlabels.k, 

882 yabel=set_label_weight(plotlabels.chikw, kweight), 

883 xmin=kmin, xmax=kmax) 

884 

885def plot_path_r(dataset, ipath, rmax=None, offset=0, label=None, 

886 show_mag=True, show_real=False, show_imag=True, fig=None): 

887 """ 

888 plot_path_r(dataset, ipath,rmax=None, offset=0, label=None, 

889 show_mag=True, show_real=False, show_imag=True, fig=None) 

890 

891 Plot chi(R) for a single Path of a feffit dataset 

892 

893 Arguments 

894 ---------- 

895 dataset feffit dataset, after running feffit() 

896 ipath index of path, starting count at 0 [0] 

897 rmax max R to show [None, end of data] 

898 offset vertical offset to use for plot [0] 

899 label path label ['path %d' % ipath] 

900 show_mag bool whether to plot |chi(R)| [True] 

901 show_real bool whether to plot Re[chi(R)] [False] 

902 show_imag bool whether to plot Im[chi(R)] [False] 

903 fig BokehFigure for reuse 

904 """ 

905 path = dataset.pathlist[ipath] 

906 if label is None: 

907 label = 'path %i' % (1+ipath) 

908 

909 title = _get_title(dataset, title=title) 

910 kweight =dataset.transform.kweight 

911 ylabel = plotlabels.chirlab(kweight, show_mag=show_mag, 

912 show_real=show_real, show_imag=show_imag) 

913 

914 if fig is None: 

915 fig = BokehFigure() 

916 if show_mag: 

917 fig.add_plot(path.r, offset+path.chir_mag, label=f'|{label}|') 

918 

919 if show_real: 

920 fig.add_plot(path.r, offset+path.chir_re, label=f'Re[{label}|') 

921 

922 if show_imag: 

923 fig.add_plot(path.r, offset+path.chir_im, label=f'Im[{label}|') 

924 

925 return fig.show(title=title, xlabel=plotlabels.r, ylabel=chirlab(kweight), 

926 xmax=rmax) 

927 

928 

929def plot_paths_k(dataset, offset=-1, kmin=0, kmax=None, title=None, fig=None): 

930 """ 

931 plot_paths_k(dataset, offset=-1, kmin=0, kmax=None, fig=None) 

932 

933 Plot k-weighted chi(k) for model and all paths of a feffit dataset 

934 

935 Arguments 

936 ---------- 

937 dataset feffit dataset, after running feffit() 

938 kmin min k to show [0] 

939 kmax max k to show [None, end of data] 

940 offset vertical offset to use for paths for plot [-1] 

941 title string for plot title [None, may use filename if available] 

942 fig BokehFigure for reuse 

943 """ 

944 # make k-weighted chi(k) 

945 kweight = dataset.transform.kweight 

946 model = dataset.model 

947 

948 model_chi_kw = model.chi * model.k**kweight 

949 

950 title = _get_title(dataset, title=title) 

951 if fig is None: 

952 fig = BokehFigure() 

953 fig.add_plot(model.k, model_chi_kw, label='sum') 

954 

955 for ipath in range(len(dataset.pathlist)): 

956 path = dataset.pathlist[ipath] 

957 label = 'path %i' % (1+ipath) 

958 chi_kw = offset*(1+ipath) + path.chi * path.k**kweight 

959 fig.add_plot(path.k, chi_kw, label=label) 

960 

961 return fig.show(title=title, xlabel=plotlabels.k, 

962 ylabel=set_label_weight(plotlabels.chikw, kweight), 

963 xmin=kmin, xmax=kmax) 

964 

965def plot_paths_r(dataset, offset=-0.25, rmax=None, show_mag=True, 

966 show_real=False, show_imag=False, title=None, fig=None): 

967 """ 

968 plot_paths_r(dataset, offset=-0.5, rmax=None, show_mag=True, show_real=False, 

969 show_imag=False) 

970 

971 Plot chi(R) for model and all paths of a feffit dataset 

972 

973 Arguments 

974 ---------- 

975 dataset feffit dataset, after running feffit() 

976 offset vertical offset to use for paths for plot [-0.5] 

977 rmax max R to show [None, end of data] 

978 show_mag bool whether to plot |chi(R)| [True] 

979 show_real bool whether to plot Re[chi(R)] [False] 

980 show_imag bool whether to plot Im[chi(R)] [False] 

981 title string for plot title [None, may use filename if available] 

982 fig BokehFigure for reuse 

983 """ 

984 kweight = dataset.transform.kweight 

985 model = dataset.model 

986 

987 title = _get_title(dataset, title=title) 

988 if fig is None: 

989 fig = BokehFigure() 

990 

991 if show_mag: 

992 fig.add_plot(model.r, model.chir_mag, label='|sum|') 

993 

994 if show_real: 

995 fig.add_plot(model.r, model.chir_re, label='Re[sum]') 

996 

997 if show_imag: 

998 fig.add_plot(model.r, model.chir_re, label='Im[sum]') 

999 

1000 for ipath in range(len(dataset.pathlist)): 

1001 path = dataset.pathlist[ipath] 

1002 label = 'path %i' % (1+ipath) 

1003 off = (ipath+1)*offset 

1004 if show_mag: 

1005 fig.add_plot(path.r, off+path.chir_mag, label=f'|{label}|') 

1006 

1007 if show_real: 

1008 fig.add_plot(path.r, off+path.chir_re, label=f'Re[{label}]') 

1009 

1010 if show_imag: 

1011 fig.add_plot(path.r, off+path.chir_im, label=f'Im[{label}]') 

1012 

1013 return fig.show(title=title, xlabel=plotlabels.r, 

1014 ylabel=chirlab(kweight), xmax=rmax) 

1015 

1016def plot_prepeaks_baseline(dgroup, subtract_baseline=False, show_fitrange=True, 

1017 show_peakrange=True): 

1018 """Plot pre-edge peak baseline fit, as from `pre_edge_baseline` or XAS Viewer 

1019 

1020 dgroup must have a 'prepeaks' attribute 

1021 """ 

1022 if not hasattr(dgroup, 'prepeaks'): 

1023 raise ValueError('Group needs prepeaks') 

1024 #endif 

1025 ppeak = dgroup.prepeaks 

1026 

1027 px0, px1, py0, py1 = extend_plotrange(dgroup.xdat, dgroup.ydat, 

1028 xmin=ppeak.emin, xmax=ppeak.emax) 

1029 

1030 title = "pre_edge baseline\n %s" % dgroup.filename 

1031 

1032 fig = BokehFigure() 

1033 

1034 ydat = dgroup.ydat 

1035 xdat = dgroup.xdat 

1036 if subtract_baseline: 

1037 fig.add_plot(ppeak.energy, ppeak.baseline, label='baseline subtracted peaks') 

1038 else: 

1039 fig.add_plot(ppeak.energy, ppeak.baseline, label='baseline') 

1040 fig.add_plot(xdat, ydat, label='data') 

1041 

1042 if show_fitrange: 

1043 for x in (ppeak.emin, ppeak.emax): 

1044 fig.add_vline(x=x, line_width=2, line_dash="dash", line_color="#DDDDCC") 

1045 fig.add_vline(x=ppeak.centroid, line_width=2, line_dash="dash", line_color="#EECCCC") 

1046 

1047 if show_peakrange: 

1048 for x in (ppeak.elo, ppeak.ehi): 

1049 y = ydat[index_of(xdat, x)] 

1050 fig.add_plot([x], [y], marker='o', marker_size=7) 

1051 

1052 return fig.show(title=title, xlabel=plotlabels.energy, ylabel='mu (normalized)', 

1053 xmin=px0, xmax=px1, ymin=py0, ymax=py1) 

1054 

1055 

1056def plot_prepeaks_fit(dgroup, nfit=0, show_init=False, subtract_baseline=False, 

1057 show_residual=False): 

1058 """plot pre-edge peak fit, as from Larix 

1059 

1060 dgroup must have a 'peakfit_history' attribute 

1061 """ 

1062 if not hasattr(dgroup, 'prepeaks'): 

1063 raise ValueError('Group needs prepeaks') 

1064 #endif 

1065 if show_init: 

1066 result = pkfit = dgroup.prepeaks 

1067 else: 

1068 hist = getattr(dgroup.prepeaks, 'fit_history', None) 

1069 if nfit > len(hist): 

1070 nfit = 0 

1071 pkfit = hist[nfit] 

1072 result = pkfit.result 

1073 #endif 

1074 

1075 if pkfit is None: 

1076 raise ValueError('Group needs prepeaks.fit_history or init_fit') 

1077 #endif 

1078 

1079 opts = pkfit.user_options 

1080 xeps = min(np.diff(dgroup.xdat)) / 5. 

1081 xdat = 1.0*pkfit.energy 

1082 ydat = 1.0*pkfit.norm 

1083 

1084 xdat_full = 1.0*dgroup.xdat 

1085 ydat_full = 1.0*dgroup.ydat 

1086 

1087 if show_init: 

1088 yfit = pkfit.init_fit 

1089 ycomps = None # pkfit.init_ycomps 

1090 ylabel = 'model' 

1091 else: 

1092 yfit = 1.0*result.best_fit 

1093 ycomps = pkfit.ycomps 

1094 ylabel = 'best fit' 

1095 

1096 baseline = 0.*ydat 

1097 if ycomps is not None: 

1098 for label, ycomp in ycomps.items(): 

1099 if label in opts['bkg_components']: 

1100 baseline += ycomp 

1101 

1102 fig = BokehFigure() 

1103 title ='%s:\npre-edge peak' % dgroup.filename 

1104 

1105 

1106 

1107 if subtract_baseline: 

1108 ydat -= baseline 

1109 yfit -= baseline 

1110 ydat_full = 1.0*ydat 

1111 xdat_full = 1.0*xdat 

1112 plotopts['ylabel'] = '%s-baseline' % plotopts['ylabel'] 

1113 

1114 dx0, dx1, dy0, dy1 = extend_plotrange(xdat_full, ydat_full, 

1115 xmin=opts['emin'], xmax=opts['emax']) 

1116 fx0, fx1, fy0, fy1 = extend_plotrange(xdat, yfit, 

1117 xmin=opts['emin'], xmax=opts['emax']) 

1118 

1119 ncolor = 0 

1120 popts = {} 

1121 plotopts.update(popts) 

1122 dymin = dymax = None 

1123 

1124 fig.add_plot(xdat, ydat, label='data') 

1125 fig.add_plot(xday, yfit, label='fit') 

1126 

1127 if show_residual: 

1128 dfig = BokehFigure() 

1129 dfig.add_plot(xdat, yfit-ydat, label='fit-data') 

1130 dy = yfit - ydat 

1131 dymax, dymin = dy.max(), dy.min() 

1132 dymax += 0.05 * (dymax - dymin) 

1133 dymin -= 0.05 * (dymax - dymin) 

1134 

1135 if ycomps is not None: 

1136 ncomps = len(ycomps) 

1137 if not subtract_baseline: 

1138 fig.add_plot(xdat, baseline, label='baseline') 

1139 for icomp, label in enumerate(ycomps): 

1140 ycomp = ycomps[label] 

1141 if label in opts['bkg_components']: 

1142 continue 

1143 fig.add_plot(xdat, ycomp, label=label) 

1144 

1145 if opts.get('show_fitrange', False): 

1146 for attr in ('emin', 'emax'): 

1147 fig.add_vline(opts[attr], line_width=2, line_dash="dash", line_color="#DDDDCC") 

1148 

1149 if opts.get('show_centroid', False): 

1150 pcen = getattr(dgroup.prepeaks, 'centroid', None) 

1151 if hasattr(result, 'params'): 

1152 pcen = result.params.get('fit_centroid', None) 

1153 if pcen is not None: 

1154 pcen = pcen.value 

1155 if pcen is not None: 

1156 fig.add_vlinee(pcen, color='#EECCCC') 

1157 

1158 fig.show(title=title, xlabel=plotlabels.energy, ylabel=opts['array_desc']) 

1159 dfig.show(title=tile, ylabel='fit-data', ymin=dymin, ymax=dymax) 

1160 return fig, dfig 

1161 

1162 

1163def _pca_ncomps(result, min_weight=0, ncomps=None): 

1164 if ncomps is None: 

1165 if min_weight > 1.e-12: 

1166 ncomps = np.where(result.variances < min_weight)[0][0] 

1167 else: 

1168 ncomps = np.argmin(result.ind) 

1169 return ncomps 

1170 

1171 

1172def plot_pca_components(result, min_weight=0, ncomps=None, min_variance=1.e-5): 

1173 """Plot components from PCA result 

1174 

1175 result must be output of `pca_train` 

1176 """ 

1177 title = "PCA components" 

1178 

1179 ncomps = int(result.nsig) 

1180 fig = BokehFigure() 

1181 fig.add_plot(result.x, result.mean, label='Mean') 

1182 for i, comp in enumerate(result.components): 

1183 if result.variances[i] > min_variance: 

1184 label = 'Comp# %d (%.4f)' % (i+1, result.variances[i]) 

1185 fig.add_plot(result.x, comp, label=label) 

1186 

1187 return fig.show(title=title, xlabel=plotlabels.energy, ylabel=plotlabels.norm, 

1188 xmin=result.xmin, xmax=result.xmax) 

1189 

1190def plot_pca_weights(result, min_weight=0, ncomps=None): 

1191 """Plot component weights from PCA result (aka SCREE plot) 

1192 

1193 result must be output of `pca_train` 

1194 """ 

1195 max_comps = len(result.components)-1 

1196 

1197 title = "PCA Variances (SCREE) and Indicator Values" 

1198 fig = BokehFigure() 

1199 

1200 ncomps = max(1, int(result.nsig)) 

1201 

1202 x0, x1, y0, y1 = extend_plotrange(result.variances, result.variances) 

1203 y0 = max(1.e-6, min(result.variances[:-1])) 

1204 x = 1+np.arange(ncomps) 

1205 y = result.variances[:ncomps] 

1206 fig.add_plot(x, y, label='significant', style='solid', marker='o') 

1207 

1208 xe = 1 + np.arange(ncomps-1, max_comps) 

1209 ye = result.variances[ncomps-1:ncomps+max_comps] 

1210 

1211 fig.add_plot(xe, ye, label='not significant', style='dashed', marker='o') 

1212 fig.set_ylog() 

1213 yi = result.ind[1:] 

1214 xi = 1 + np.arange(len(yi)) 

1215 

1216 x0, x1, yimin, yimax = extend_plotrange(xi, yi) 

1217 

1218 fig.add_plot(xi, result.ind[1:], y2label='Indicator Value', 

1219 style='solid', side='right') 

1220 # fig.update_yaxes(title_text='Indicator') # , secondary_y=True) 

1221 return fig.show(title=title, xlabel='Component #', ylabel='variance') 

1222 

1223 

1224def plot_pca_fit(dgroup, with_components=True): 

1225 """Plot data and fit result from pca_fit, which rom PCA result 

1226 

1227 result must be output of `pca_fit` 

1228 """ 

1229 title = "PCA fit: %s" % (dgroup.filename) 

1230 result = dgroup.pca_result 

1231 model = result.pca_model 

1232 

1233 fig = BokehFigure() 

1234 fig.add_plot(result.x, result.ydat, label='data') 

1235 fig.add_plot(result.x, result.yfit, label='fit') 

1236 if with_components: 

1237 fig.add_plot(result.x, model.mean, label='mean') 

1238 for n in range(len(result.weights)): 

1239 cval = model.components[n]*result.weights[n] 

1240 fig.add_plot(result.x, cval, label='Comp #%d' % (n+1)) 

1241 

1242 fig.show(title=title, xmin=model.xmin, xmax=model.xmax, 

1243 xlabel=plotlabels.energy, ylabel=plotlabels.norm) 

1244 

1245 dfig = BokehFigure() 

1246 dfig.add_plot(result.x, result.yfit-result.ydat, label='fit-data') 

1247 dfig.show(title=title, xmin=model.xmin, xmax=model.xmax, 

1248 xlabel=plotlabels.energy, ylabel='fit-data') 

1249 return fig, dfig 

1250 

1251def plot_diffkk(dgroup, emin=None, emax=None, new=True, label=None, 

1252 title=None, offset=0): 

1253 """ 

1254 plot_diffkk(dgroup, norm=True, emin=None, emax=None, show_e0=False, label=None): 

1255 

1256 Plot mu(E) and background mu0(E) for XAFS data group 

1257 

1258 Arguments 

1259 ---------- 

1260 dgroup group of XAFS data after autobk() results (see Note 1) 

1261 norm bool whether to show normalized data [True] 

1262 emin min energy to show, absolute or relative to E0 [None, start of data] 

1263 emax max energy to show, absolute or relative to E0 [None, end of data] 

1264 show_e0 bool whether to show E0 [False] 

1265 label string for label [``None``: 'mu'] 

1266 title string for plot title [None, may use filename if available] 

1267 offset vertical offset to use for y-array [0] 

1268 

1269 Notes 

1270 ----- 

1271 1. The input data group must have the following attributes: 

1272 energy, mu, bkg, norm, e0, pre_edge, edge_step, filename 

1273 """ 

1274 if hasattr(dgroup, 'f2'): 

1275 f2 = dgroup.f2 

1276 else: 

1277 raise ValueError("Data group has no array for f2") 

1278 #endif 

1279 ylabel = r'$$f \rm\,\, (e^{-})$$ ' 

1280 emin, emax = _get_erange(dgroup, emin, emax) 

1281 title = _get_title(dgroup, title=title) 

1282 

1283 labels = {'f2': r"$$f_2(E)$$", 'fpp': r"$$f''(E)$$", 'fp': r"$$f'(E)$$", 'f1': r"$$f_1(E)$$"} 

1284 

1285 fig = BokehFigure() 

1286 fig.add_plot(dgroup.energy, f2, label=labels['f2']) 

1287 

1288 for attr in ('fpp', 'f1', 'fp'): 

1289 yval = getattr(dgroup, attr) 

1290 if yval is not None: 

1291 fig.add_plot(dgroup.energy, yval, label=labels[attr]) 

1292 

1293 return fig.show(title=title, xlabel=plotlabels.energy, yaxis_label=ylabel, 

1294 xmin=emin, xmax=emax) 

1295 

1296 

1297def plot_feffdat(feffpath, with_phase=True, title=None, fig=None): 

1298 """ 

1299 plot_feffdat(feffpath, with_phase=True, title=None) 

1300 

1301 Plot Feff's magnitude and phase as a function of k for a FeffPath 

1302 

1303 Arguments 

1304 ---------- 

1305 feffpath feff path as read by feffpath() 

1306 with_pase whether to plot phase(k) as well as magnitude [True] 

1307 title string for plot title [None, may use filename if available] 

1308 

1309 Notes 

1310 ----- 

1311 1. The input data group must have the following attributes: 

1312 energy, mu, bkg, norm, e0, pre_edge, edge_step, filename 

1313 """ 

1314 if hasattr(feffpath, '_feffdat'): 

1315 fdat = feffpath._feffdat 

1316 else: 

1317 raise ValueError("must pass in a Feff path as from feffpath()") 

1318 

1319 if fig is None: 

1320 fig = BokehFigure() 

1321 fig.add_plot(result.x, result.ydat, label='data') 

1322 

1323 

1324 fig.add_plot(fdat.k, fdat.mag_feff, label='magnitude') 

1325 # xlabel=plotlabels.k, 

1326 # ylabel='|F(k)|', title=title, 

1327 

1328 if with_phase: 

1329 fig.add_plot(fdat.k, fdat.pha_feff, label='phase') 

1330 # fig.fig.update_yaxis(title_text='Phase(k)') #, secondary_y=True) 

1331 return fig.show(title=title, xlabel=plotlabels.k, ylabel='|F(k)|') 

1332 

1333#enddef 

1334 

1335def plot_wavelet(dgroup, show_mag=True, show_real=False, show_imag=False, 

1336 rmax=None, kmax=None, kweight=None, title=None): 

1337 """ 

1338 plot_wavelet(dgroup, show_mag=True, show_real=False, show_imag=False, 

1339 rmax=None, kmax=None, kweight=None, title=None) 

1340 

1341 Plot wavelet for XAFS data group 

1342 

1343 Arguments 

1344 ---------- 

1345 dgroup group of XAFS data after xftf() results (see Note 1) 

1346 show_mag bool whether to plot wavelet magnitude [True] 

1347 show_real bool whether to plot real part of wavelet [False] 

1348 show_imag bool whether to plot imaginary part of wavelet [False] 

1349 title string for plot title [None, may use filename if available] 

1350 rmax max R to show [None, end of data] 

1351 kmax max k to show [None, end of data] 

1352 kweight k-weight to use to construct wavelet [None, take from group] 

1353 

1354 Notes 

1355 ----- 

1356 The wavelet will be performed 

1357 """ 

1358 print("Image display not yet available with larch+bokeh") 

1359 kweight = _get_kweight(dgroup, kweight) 

1360 cauchy_wavelet(dgroup, kweight=kweight, rmax_out=rmax) 

1361 title = _get_title(dgroup, title=title) 

1362 

1363 opts = dict(title=title, x=dgroup.k, y=dgroup.wcauchy_r, xmax=kmax, 

1364 ymax=rmax, xlabel=plotlabels.k, ylabel=plotlabels.r, 

1365 show_axis=True) 

1366 if show_mag: 

1367 _imshow(dgroup.wcauchy_mag, **opts) 

1368 elif show_real: 

1369 _imshow(dgroup.wcauchy_real, **opts) 

1370 elif show_imag: 

1371 _imshow(dgroup.wcauchy_imag, **opts) 

1372 #endif 

1373#enddef