def interactive(self): """ Generates a jupyter widgets UI for exploring a spectra. """ import ipywidgets as wid from IPython.display import display is_nm = self.freq_unit is 'nm' if is_nm: ph.vis_mode() else: ph.ir_mode() ds = self.dataset x = ds.wavelengths if is_nm else ds.wavenumbers # fig, ax = plt.subplots() wl_slider = wid.FloatSlider(None, min=x.min(), max=x.max(), step=1, description='Freq (%s)' % self.freq_unit) def func(x): # ax.cla() self.trans([x]) # plt.show() ui = wid.interactive(func, x=wl_slider, continuous_update=False) display(ui) return ui
def interactive(self): """ Generates a jupyter widgets UI for exploring a spectra. """ import ipywidgets as wid from IPython.display import display is_nm = self.freq_unit is "nm" if is_nm: ph.vis_mode() else: ph.ir_mode() ds = self.dataset x = ds.wavelengths if is_nm else ds.wavenumbers # fig, ax = plt.subplots() wl_slider = wid.FloatSlider( None, min=x.min(), max=x.max(), step=1, description="Freq (%s)" % self.freq_unit, ) def func(x): # ax.cla() self.trans([x]) # plt.show() ui = wid.interactive(func, x=wl_slider, continuous_update=False) display(ui) return ui
def svd(self, n=5): """ Plot the SVD-components of the dataset. Parameters ---------- n : int or list of int Determines the plotted SVD-components. If `n` is an int, it plots the first n components. If `n` is a list of ints, then every number is a SVD-component to be plotted. """ is_nm = self.freq_unit is 'nm' if is_nm: ph.vis_mode() else: ph.ir_mode() ds = self.dataset x = ds.wavelengths if is_nm else ds.wavenumbers fig, axs = plt.subplots(3, 1, figsize=(4, 5)) u, s, v = np.linalg.svd(ds.data) axs[0].stem(s) axs[0].set_xlim(0, 11) try: len(n) comps = n except TypeError: comps = range(n) for i in comps: axs[1].plot(ds.t, u.T[i], label='%d' % i) axs[2].plot(x, v[i]) ph.lbl_trans(axs[1], use_symlog=True) self.lbl_spec(axs[2])
def svd(self, n=5): """ Plot the SVD-components of the dataset. Parameters ---------- n : int or list of int Determines the plotted SVD-components. If `n` is an int, it plots the first n components. If `n` is a list of ints, then every number is a SVD-component to be plotted. """ is_nm = self.freq_unit is "nm" if is_nm: ph.vis_mode() else: ph.ir_mode() ds = self.dataset x = ds.wavelengths if is_nm else ds.wavenumbers fig, axs = plt.subplots(3, 1, figsize=(4, 5)) u, s, v = np.linalg.svd(ds.data) axs[0].stem(s) axs[0].set_xlim(0, 11) try: len(n) comps = n except TypeError: comps = range(n) for i in comps: axs[1].plot(ds.t, u.T[i], label="%d" % i) axs[2].plot(x, v[i]) ph.lbl_trans(axs[1], use_symlog=True) self.lbl_spec(axs[2])
def spec(self, t_list, norm=False, ax=None, n_average=0, **kwargs): """ Plot spectra at given times. Parameters ---------- t_list : list or ndarray List of the times where the spectra are plotted. norm : bool If true, each spectral will be normalized. ax : plt.Axis or None. Axis where the spectra are plotted. If none, the current axis will be used. n_average : int For noisy data it may be preferred to average multiple spectra together. This function plots the average of `n_average` spectra around the specific time-points. Returns ------- list of `Lines2D` List containing the Line2D objects belonging to the spectra. """ if ax is None: ax = plt.gca() is_nm = self.freq_unit == 'nm' if is_nm: ph.vis_mode() else: ph.ir_mode() ds = self.dataset x = ds.wavelengths if is_nm else ds.wavenumbers li = [] for i in t_list: idx = dv.fi(ds.t, i) if n_average > 0: dat = uniform_filter(ds, (2 * n_average + 1, 1)).data[idx, :] elif n_average == 0: dat = ds.data[idx, :] else: raise ValueError('n_average must be an Integer >= 0.') if norm: dat = dat / abs(dat).max() li += ax.plot(x, dat, label=ph.time_formatter(ds.t[idx], ph.time_unit), **kwargs) self.lbl_spec(ax) if not is_nm: ax.set_xlim(x.max(), x.min()) return li
def spec(self, t_list, norm=False, ax=None, n_average=0, **kwargs): """ Plot spectra at given times. Parameters ---------- t_list : list or ndarray List of the times where the spectra are plotted. norm : bool If true, each spectral will be normalized. ax : plt.Axis or None. Axis where the spectra are plotted. If none, the current axis will be used. n_average : int For noisy data it may be preferred to average multiple spectra together. This function plots the average of `n_average` spectra around the specific time-points. Returns ------- list of `Lines2D` List containing the Line2D objects belonging to the spectra. """ if ax is None: ax = plt.gca() is_nm = self.freq_unit == "nm" if is_nm: ph.vis_mode() else: ph.ir_mode() ds = self.dataset x = ds.wavelengths if is_nm else ds.wavenumbers li = [] for i in t_list: idx = dv.fi(ds.t, i) if n_average > 0: dat = uniform_filter(ds, (2 * n_average + 1, 1)).data[idx, :] elif n_average == 0: dat = ds.data[idx, :] else: raise ValueError("n_average must be an Integer >= 0.") if norm: dat = dat / abs(dat).max() li += ax.plot( x, dat, label=ph.time_formatter(ds.t[idx], ph.time_unit), **kwargs ) self.lbl_spec(ax) if not is_nm: ax.set_xlim(x.max(), x.min()) return li
def trans_anisotropy(self, wls, symlog=True, ax=None, freq_unit='auto'): """ Plots the anisotropy over time for given frequencies. Parameters ---------- wls : list of floats Which frequencies are plotted. symlog : bool Use symlog scale ax : plt.Axes or None Matplotlib Axes, if `None`, defaults to `plt.gca()`. Returns ------- : list of Line2D List with the line objects. """ if ax is None: ax = ax.gca() ds = self.pol_ds tmp = self.freq_unit if freq_unit == 'auto' else freq_unit is_nm = tmp == 'nm' x = ds.wavelengths if is_nm else ds.wavenumbers if is_nm: ph.vis_mode() else: ph.ir_mode() l = [] for i in wls: idx = dv.fi(x, i) pa, pe = ds.para.data[:, idx], ds.perp.data[:, idx] aniso = (pa - pe) / (2 * pe + pa) l += ax.plot(ds.para.t, aniso, label=ph.time_formatter(ds.t[idx], ph.time_unit)) ph.lbl_trans(use_symlog=symlog) if symlog: ax.set_xscale('symlog') ax.set_xlim(-1, ) return l
def das(self, ax=None, **kwargs): """ Plot a DAS, if available. Parameters ---------- ax : plt.Axes or None Axes to plot. kwargs : dict Keyword args given to the plot function Returns ------- Tuple of (List of Lines2D) """ ds = self.pol_ds if not hasattr(self.pol_ds, 'fit_exp_result_'): raise ValueError('The PolTRSpec must have successfully fit the ' 'data') if ax is None: ax = plt.gca() is_nm = self.freq_unit == 'nm' if is_nm: ph.vis_mode() else: ph.ir_mode() f = ds.fit_exp_result_.fitter num_exp = f.num_exponentials leg_text = [ ph.nsf(i) + ' ' + ph.time_unit for i in f.last_para[-num_exp:] ] if max(f.last_para) > 5 * f.t.max(): leg_text[-1] = 'const.' n = ds.para.wavelengths.size x = ds.wavelengths if is_nm else ds.wavenumbers l1 = ax.plot(self.x, f.c[:n, :], **kwargs, **self.para_ls) l2 = ax.plot(self.x, f.c[n:, :], **kwargs, **self.perp_ls) dv.equal_color(l1, l2) ax.legend(l1, leg_text, title='Decay\nConstants') return l1, l2
def trans_anisotropy(self, wls, symlog=True, ax=None, freq_unit="auto"): """ Plots the anisotropy over time for given frequencies. Parameters ---------- wls : list of floats Which frequencies are plotted. symlog : bool Use symlog scale ax : plt.Axes or None Matplotlib Axes, if `None`, defaults to `plt.gca()`. Returns ------- : list of Line2D List with the line objects. """ if ax is None: ax = ax.gca() ds = self.pol_ds tmp = self.freq_unit if freq_unit == "auto" else freq_unit is_nm = tmp == "nm" x = ds.wavelengths if is_nm else ds.wavenumbers if is_nm: ph.vis_mode() else: ph.ir_mode() l = [] for i in wls: idx = dv.fi(x, i) pa, pe = ds.para.data[:, idx], ds.perp.data[:, idx] aniso = (pa - pe) / (2 * pe + pa) l += ax.plot( ds.para.t, aniso, label=ph.time_formatter(ds.t[idx], ph.time_unit) ) ph.lbl_trans(use_symlog=symlog) if symlog: ax.set_xscale("symlog") ax.set_xlim(-1) return l
def overview(self): """ Plots an overview figure. """ is_nm = self.freq_unit is "nm" if is_nm: ph.vis_mode() else: ph.ir_mode() ds = self.dataset x = ds.wavelengths if is_nm else ds.wavenumbers fig, axs = plt.subplots( 3, 1, figsize=(5, 12), gridspec_kw=dict(height_ratios=(2, 1, 1)) ) self.map(ax=axs[0]) times = np.hstack((0, np.geomspace(0.1, ds.t.max(), 6))) sp = self.spec(times, ax=axs[1]) freqs = np.unique(np.linspace(x.min(), x.max(), 6)) tr = self.trans(freqs, ax=axs[2]) OverviewPlot = namedtuple("OverviewPlot", "fig axs trans spec") return OverviewPlot(fig, axs, tr, sp)
def das(self, ax=None, **kwargs): """ Plot a DAS, if available. Parameters ---------- ax : plt.Axes or None Axes to plot. kwargs : dict Keyword args given to the plot function Returns ------- Tuple of (List of Lines2D) """ ds = self.pol_ds if not hasattr(self.pol_ds, "fit_exp_result_"): raise ValueError("The PolTRSpec must have successfully fit the " "data") if ax is None: ax = plt.gca() is_nm = self.freq_unit == "nm" if is_nm: ph.vis_mode() else: ph.ir_mode() f = ds.fit_exp_result_.fitter num_exp = f.num_exponentials leg_text = [ph.nsf(i) + " " + ph.time_unit for i in f.last_para[-num_exp:]] if max(f.last_para) > 5 * f.t.max(): leg_text[-1] = "const." n = ds.para.wavelengths.size x = ds.wavelengths if is_nm else ds.wavenumbers l1 = ax.plot(self.x, f.c[:n, :], **kwargs, **self.para_ls) l2 = ax.plot(self.x, f.c[n:, :], **kwargs, **self.perp_ls) dv.equal_color(l1, l2) ax.legend(l1, leg_text, title="Decay\nConstants") return l1, l2
def overview(self): """ Plots an overview figure. """ is_nm = self.freq_unit is 'nm' if is_nm: ph.vis_mode() else: ph.ir_mode() ds = self.dataset x = ds.wavelengths if is_nm else ds.wavenumbers fig, axs = plt.subplots(3, 1, figsize=(5, 12), gridspec_kw=dict(height_ratios=(2, 1, 1))) self.map(ax=axs[0]) times = np.hstack((0, np.geomspace(0.1, ds.t.max(), 6))) sp = self.spec(times, ax=axs[1]) freqs = np.unique(np.linspace(x.min(), x.max(), 6)) tr = self.trans(freqs, ax=axs[2]) OverviewPlot = namedtuple('OverviewPlot', 'fig axs trans spec') return OverviewPlot(fig, axs, tr, sp)
def trans(self, wls, symlog=True, norm=False, ax=None, freq_unit='auto', **kwargs): """ Plot the nearest transients for given frequencies. Parameters ---------- wls : list or ndarray Spectral positions, should be given in the same unit as `self.freq_unit`. symlog : bool Determines if the x-scale is symlog. norm : bool or float If `False`, no normalization is used. If `True`, each transient is divided by the maximum absolute value. If `norm` is a float, all transient are normalized by their signal at the time `norm`. ax : plt.Axes or None Takes a matplotlib axes. If none, it uses `plt.gca()` to get the current axes. The lines are plotted in this axis. freq_unit : 'auto', 'cm' or 'nm' How to interpret the given frequencies. If 'auto' it defaults to the plotters freq_unit. All other kwargs are forwarded to the plot function. Returns ------- list of Line2D List containing the plotted lines. """ if ax is None: ax = plt.gca() tmp = self.freq_unit if freq_unit is 'auto' else freq_unit is_nm = tmp == 'nm' if is_nm: ph.vis_mode() else: ph.ir_mode() ds = self.dataset x = ds.wavelengths if is_nm else ds.wavenumbers wl, t, d = ds.wl, ds.t, ds.data l, plotted_vals = [], [] for i in wls: idx = dv.fi(x, i) dat = d[:, idx] if norm is True: dat = np.sign(dat[np.argmax(abs(dat))]) * dat / abs(dat).max() elif norm is False: pass else: dat = dat / dat[dv.fi(t, norm)] plotted_vals.append(dat) l.extend( ax.plot(t, dat, label='%.1f %s' % (x[idx], ph.freq_unit), **kwargs)) if symlog: ax.set_xscale('symlog', linthreshx=1.) ph.lbl_trans(ax=ax, use_symlog=symlog) ax.legend(loc='best', ncol=3) ax.set_xlim(right=t.max()) ax.yaxis.set_tick_params(which='minor', left=True) return l
def map(self, symlog=True, equal_limits=True, plot_con=True, con_step=None, con_filter=None, ax=None, **kwargs): """ Plot a colormap of the dataset with optional contour lines. Parameters ---------- symlog : bool Determines if the yscale is symmetric logarithmic. equal_limits : bool If true, it makes to colors symmetric around zeros. Note this also sets the middle of the colormap to zero. Default is `True`. plot_con : bool Plot additional contour lines if `True` (default). con_step : float, array or None Controls the contour-levels. If `con_step` is a float, it is used as the step size between two levels. If it is an array, its elements are the levels. If `None`, it defaults to 20 levels. con_filter : None, int or `TimeResSpec`. Since contours are strongly affected by noise, it can be prefered to filter the dataset before calculating the contours. If `con_filter` is a dataset, the data of that set will be used for the contours. If it is a tuple of int, the data will be filtered with an uniform filter before calculation the contours. If `None`, no data prepossessing will be applied. ax : plt.Axis or None Takes a matplotlib axis. If none, it uses `plt.gca()` to get the current axes. The lines are plotted in this axis. """ if ax is None: ax = plt.gca() is_nm = self.freq_unit is 'nm' if is_nm: ph.vis_mode() else: ph.ir_mode() ds = self.dataset x = ds.wavelengths if is_nm else ds.wavenumbers cmap = kwargs.pop('colormap', "bwr") if equal_limits: m = np.max(np.abs(ds.data)) vmin, vmax = -m, m else: vmin, vmax = ds.data.max(), ds.data.min() mesh = ax.pcolormesh(x, ds.t, ds.data, vmin=vmin, vmax=vmax, cmap=cmap, **kwargs) if symlog: ax.set_yscale('symlog', linthreshy=1) ph.symticks(ax, axis='y') ax.set_ylim(-.5) plt.colorbar(mesh, ax=ax) if plot_con: if con_step is None: levels = 20 elif isinstance(con_step, np.ndarray): levels = con_step else: # TODO This assumes data has positive and negative elements. pos = np.arange(0, ds.data.max(), con_step) neg = np.arange(0, -ds.data.min(), con_step) levels = np.hstack((-neg[::-1][:-1], pos)) if isinstance(con_filter, TimeResSpec): data = con_filter.data elif con_filter is not None: # must be int or tuple of int if isinstance(con_filter, tuple): data = uniform_filter(ds, con_filter).data else: data = svd_filter(ds, con_filter).data else: data = ds.data ax.contour(x, ds.t, data, levels=levels, linestyles='solid', colors='k', linewidths=0.5) ph.lbl_map(ax, symlog) if not is_nm: ax.set_xlim(*ax.get_xlim()[::-1])
def trans(self, wls, symlog=True, norm=False, ax=None, freq_unit="auto", **kwargs): """ Plot the nearest transients for given frequencies. Parameters ---------- wls : list or ndarray Spectral positions, should be given in the same unit as `self.freq_unit`. symlog : bool Determines if the x-scale is symlog. norm : bool or float If `False`, no normalization is used. If `True`, each transient is divided by the maximum absolute value. If `norm` is a float, all transient are normalized by their signal at the time `norm`. ax : plt.Axes or None Takes a matplotlib axes. If none, it uses `plt.gca()` to get the current axes. The lines are plotted in this axis. freq_unit : 'auto', 'cm' or 'nm' How to interpret the given frequencies. If 'auto' it defaults to the plotters freq_unit. All other kwargs are forwarded to the plot function. Returns ------- list of Line2D List containing the plotted lines. """ if ax is None: ax = plt.gca() tmp = self.freq_unit if freq_unit is "auto" else freq_unit is_nm = tmp == "nm" if is_nm: ph.vis_mode() else: ph.ir_mode() ds = self.dataset x = ds.wavelengths if is_nm else ds.wavenumbers wl, t, d = ds.wl, ds.t, ds.data l, plotted_vals = [], [] for i in wls: idx = dv.fi(x, i) dat = d[:, idx] if norm is True: dat = np.sign(dat[np.argmax(abs(dat))]) * dat / abs(dat).max() elif norm is False: pass else: dat = dat / dat[dv.fi(t, norm)] plotted_vals.append(dat) l.extend( ax.plot(t, dat, label="%.1f %s" % (x[idx], ph.freq_unit), **kwargs) ) if symlog: ax.set_xscale("symlog", linthreshx=1.0) ph.lbl_trans(ax=ax, use_symlog=symlog) ax.legend(loc="best", ncol=3) ax.set_xlim(right=t.max()) ax.yaxis.set_tick_params(which="minor", left=True) return l
def map( self, symlog=True, equal_limits=True, plot_con=True, con_step=None, con_filter=None, ax=None, **kwargs ): """ Plot a colormap of the dataset with optional contour lines. Parameters ---------- symlog : bool Determines if the yscale is symmetric logarithmic. equal_limits : bool If true, it makes to colors symmetric around zeros. Note this also sets the middle of the colormap to zero. Default is `True`. plot_con : bool Plot additional contour lines if `True` (default). con_step : float, array or None Controls the contour-levels. If `con_step` is a float, it is used as the step size between two levels. If it is an array, its elements are the levels. If `None`, it defaults to 20 levels. con_filter : None, int or `TimeResSpec`. Since contours are strongly affected by noise, it can be prefered to filter the dataset before calculating the contours. If `con_filter` is a dataset, the data of that set will be used for the contours. If it is a tuple of int, the data will be filtered with an uniform filter before calculation the contours. If `None`, no data prepossessing will be applied. ax : plt.Axis or None Takes a matplotlib axis. If none, it uses `plt.gca()` to get the current axes. The lines are plotted in this axis. """ if ax is None: ax = plt.gca() is_nm = self.freq_unit is "nm" if is_nm: ph.vis_mode() else: ph.ir_mode() ds = self.dataset x = ds.wavelengths if is_nm else ds.wavenumbers cmap = kwargs.pop("colormap", "bwr") if equal_limits: m = np.max(np.abs(ds.data)) vmin, vmax = -m, m else: vmin, vmax = ds.data.max(), ds.data.min() mesh = ax.pcolormesh( x, ds.t, ds.data, vmin=vmin, vmax=vmax, cmap=cmap, **kwargs ) if symlog: ax.set_yscale("symlog", linthreshy=1) ph.symticks(ax, axis="y") ax.set_ylim(-0.5) plt.colorbar(mesh, ax=ax) if plot_con: if con_step is None: levels = 20 elif isinstance(con_step, np.ndarray): levels = con_step else: # TODO This assumes data has positive and negative elements. pos = np.arange(0, ds.data.max(), con_step) neg = np.arange(0, -ds.data.min(), con_step) levels = np.hstack((-neg[::-1][:-1], pos)) if isinstance(con_filter, TimeResSpec): data = con_filter.data elif con_filter is not None: # must be int or tuple of int if isinstance(con_filter, tuple): data = uniform_filter(ds, con_filter).data else: data = svd_filter(ds, con_filter).data else: data = ds.data ax.contour( x, ds.t, data, levels=levels, linestyles="solid", colors="k", linewidths=0.5, ) ph.lbl_map(ax, symlog) if not is_nm: ax.set_xlim(*ax.get_xlim()[::-1])