def measurement(array, confidence=0.95, silent=False): """ Give the measurement results with condifence interval assuming the standard deviation is unknown. Parameters ---------- array : ndarray The array containing the measured values confidence : float, optional The desired confidence level. Must be _between 0 and 1. silent : bool, optional Whether to print results immediately. Default is `False`. Returns ------- mean: float The mean of the given array conf: tuple-like (interval) The confidence interval Examples --------- >>> import numpy as np >>> from pysprint.utils import measurement >>> a = np.array([123.783, 121.846, 122.248, 125.139, 122.569]) >>> mean, interval = measurement(a, 0.99) 123.117000 ± 2.763022 >>> mean 123.117 >>> interval (120.35397798230359, 125.88002201769642) Notes ----- I decided to print the results immediately, because people often don't use it for further code. Of course, they are also returned if needed. """ precision = _get_config_value("precision") mean = np.mean(array) conf = st.t.interval(confidence, len(array) - 1, loc=mean, scale=st.sem(array)) if not silent: pprint_math_or_default( f"{mean:.{precision}f} ± {(mean - conf[0]):.{precision}f}" ) return mean, conf
def __init__(self, file: PathOrBuffer, phase: Phase, verbosity: Union[int, None] = None): self.file = file if not self.file.endswith((".log", ".txt")): self.file += ".log" # if os.path.exists(self.file): # warnings.warn(f"File {self.file} exists, opening it in append mode.", PySprintWarning) self.phase = phase self.verbosity = verbosity or _get_config_value("verbosity")
def result_wrapper(self): precision = _get_config_value("precision") labels = ("GD", "GDD", "TOD", "FOD", "QOD", "SOD") params = self.p0[3:] for i, (label, param) in enumerate(zip(labels, params)): if run_from_ipython(): from IPython.display import display, Math # noqa display( Math( f"{label} = {(params[i] * factorial(i + 1)):.{precision}f} \\ fs^{i + 1}" ) ) else: print(f"{label} = {(params[i] * factorial(i + 1)):.{precision}f} fs^{i + 1}")
def run(self, r_extend_by, r_threshold, max_tries=5000, show_endpoint=True): precision = _get_config_value("precision") if not self._init_set: raise ValueError("Set the initial conditions.") self._fit() while self._fit_goodness() > r_threshold: # self.figure.savefig(f'{self.counter}.eps') # self.update_plot() self._extend_region(r_extend_by) self._fit() self.counter += 1 self._step_up_func() if self._fit() is True: if show_endpoint: self.update_plot() # self.figure.savefig(f'{self.counter}.eps') self.result_wrapper() if run_from_ipython(): from IPython.display import display, Math # noqa display(Math(f"with \\ R^2 = {(self._fit_goodness()):.{precision}f}.")) else: print(f"with R^2 = {(self._fit_goodness()):.{precision}f}.") return self.popt if self.counter == max_tries: if show_endpoint: self.update_plot() print( f"""Max tries (currently set to {max_tries}) reached without finding a suitable fit.""" ) return np.zeros_like(self.popt) while self._fit_goodness() < r_threshold: self._fit() # self._finetune() self.counter += 1 if self.counter == max_tries: if show_endpoint: self.update_plot() print( f"""Max tries (currently set to {max_tries}) reached without finding a suitable fit.""" ) return np.zeros_like(self.popt)
def calculate(self, reference_point): """ Cosine fit's calculate function. Parameters ---------- reference_point: float Reference point on x axis. Returns ------- dispersion : array-like [GD, GDD, TOD, FOD, QOD, SOD] dispersion_std : array-like Standard deviations due to uncertainty of the fit. They are only calculated if lmfit is installed. [GD_std, GDD_std, TOD_std, FOD_std, QOD_std, SOD_std] fit_report : str Not implemented yet. It returns an empty string for the time being. Notes ------ Decorated with pprint_disp, so the results are immediately printed without explicitly saying so. """ dispersion, self.fit = cff_method( self.x, self.y, self.ref, self.sam, ref_point=reference_point, p0=self.params, maxtries=self.mt, ) precision = _get_config_value("precision") self.r_squared = self._get_r_squared() pprint_math_or_default(f"R^2 = {self.r_squared:.{precision}f}\n") dispersion = pad_with_trailing_zeros(dispersion, 6) return ( dispersion, [0, 0, 0, 0, 0, 0], "", )
def plot_result(self): """ If the curve fitting is done, draws the fitted curve on the original dataset. Also prints the coeffitient of determination of the fit (a.k.a. r^2). """ precision = _get_config_value("precision") try: self._get_r_squared() pprint_math_or_default(f"R^2 = {self.r_squared:.{precision}f}") except NotCalculatedException as e: raise ValueError("There's nothing to plot.") from e if self.fit is not None: self.plt.plot(self.x, self.fit, "k--", label="fit", zorder=99) self.plt.legend() self.plot() self.show() else: self.plot() self.show()
def wrapping(*args, **kwargs): disp, disp_std, stri = f(*args, **kwargs) labels = ("GD", "GDD", "TOD", "FOD", "QOD", "SOD") disp = np.trim_zeros(disp, "b") disp_std = disp_std[:len(disp)] precision = _get_config_value("precision") for i, (label, disp_item, disp_std_item) in enumerate(zip(labels, disp, disp_std)): if run_from_ipython(): from IPython.display import display, Math # noqa display( Math( f"{label} = {disp_item:.{precision}f} ± {disp_std_item:.{precision}f} fs^{i + 1}" )) else: print( f"{label} = {disp_item:.{precision}f} ± {disp_std_item:.{precision}f} fs^{i + 1}" ) return disp, disp_std, stri
def __str__(self): _unit = self._render_unit(self.unit) precision = _get_config_value("precision") string = dedent(f""" {type(self).__name__} ---------- Parameters ---------- Datapoints: {len(self.x)} Predicted domain: {'wavelength' if self.probably_wavelength else 'frequency'} Range: from {np.min(self.x):.{precision}f} to {np.max(self.x):.{precision}f} {_unit} Normalized: {self._is_normalized} Delay value: {str(self._format_delay()) + ' fs' if self._delay is not None else 'Not given'} SPP position(s): {str(self._format_positions()) + ' PHz' if np.all(self._positions) else 'Not given'} ---------------------------- Metadata extracted from file ---------------------------- """) string = re.sub('^\s+', '', string, flags=re.MULTILINE) string += json.dumps(self.meta, indent=4, sort_keys=True) return string
def _prepare_content(self): if self.phase.coef_array is None: raise NotCalculatedException precision = _get_config_value("precision") iter_num = len(self.phase.x) output = dedent(f""" --------------------------------------------------------------------------------------- Date: {datetime.datetime.now()} Datapoints used: {iter_num} R^2: {self.phase._get_r_squared():.{precision}f} Results: """) for i, (label, value) in enumerate(zip(self.LABELS, self.phase.coef_array)): if value is not None and value != 0: output += f"{label} = {value:.{precision}f} fs^{i + 1}\n" if self.verbosity > 0: with np.printoptions( threshold=sys.maxsize, linewidth=np.inf, precision=precision, ): output += dedent(f""" Values used: x: {self.phase.x} y: {self.phase.y} """) return output
def __str__(self): precision = _get_config_value("precision") return f"Window(center={self.center:.{precision}f}, fwhm={self.fwhm}, order={self.order})"
def _repr_html_(self): # TODO: move this to a separate template file _unit = self._render_unit(self.unit) precision = _get_config_value("precision") t = f""" <div id="header" class="row" style="height:10%;width:100%;"> <div style='float:left' class="column"> <table style="border:1px solid black;float:top;"> <tbody> <tr> <td colspan=2 style="text-align:center"> <font size="5">{type(self).__name__}</font> </td> </tr> <tr> <td colspan=2 style="text-align:center"> <font size="3.5">Parameters</font> </td> </tr> <tr> <td style="text-align:center"><b>Datapoints<b></td> <td style="text-align:center"> {len(self.x)}</td> </tr> <tr> <td style="text-align:center"><b>Predicted domain<b> </td> <td style="text-align:center"> {'wavelength' if self.probably_wavelength else 'frequency'} </td> </tr> <tr> <td style="text-align:center"> <b>Range min</b> </td> <td style="text-align:center">{np.min(self.x):.{precision}f} {_unit}</td> </tr> <tr> <td style="text-align:center"> <b>Range max</b> </td> <td style="text-align:center">{np.max(self.x):.{precision}f} {_unit}</td> </tr> <tr> <td style="text-align:center"> <b>Normalized</b></td> <td style="text-align:center"> {self._is_normalized} </td> </tr> <tr> <td style="text-align:center"><b>Delay value</b></td> <td style="text-align:center">{str(self._format_delay()) + ' fs' if self._delay is not None else 'Not given'}</td> </tr> <tr> <td style="text-align:center"><b>SPP position(s)</b></td> <td style="text-align:center">{str(self._format_positions()) + ' PHz' if np.all(self._positions) else 'Not given'}</td> </tr> <tr> <td colspan=2 style="text-align:center"> <font size="3.5">Metadata</font> </td> </tr> """ jjstring = Template(""" {% for key, value in meta.items() %} <tr> <th style="text-align:center"> <b>{{ key }} </b></th> <td style="text-align:center"> {{ value }} </td> </tr> {% endfor %} </tbody> </table> </div> <div style='float:leftt' class="column">""") rendered_fig = self._render_html_plot() return t + jjstring.render(meta=self.meta) + rendered_fig
def GD_lookup(self, reference_point=None, engine="cwt", silent=False, **kwargs): """ Quick GD lookup: it finds extremal points near the `reference_point` and returns an average value of 2*pi divided by distances between consecutive minimal or maximal values. Since it's relying on peak detection, the results may be irrelevant in some cases. If the parent class is `~pysprint.CosFitMethod`, then it will set the predicted value as initial parameter for fitting. Parameters ---------- reference_point : float The reference point for the algorithm. engine : str, optional The backend to use. Must be "cwt", "normal" or "fft". "cwt" will use `scipy.signal.find_peaks_cwt` function to detect peaks, "normal" will use `scipy.signal.find_peaks` to detect peaks. The "fft" engine uses Fourier-transform and looks for the outer peak to guess delay value. It's not reliable when working with low delay values. silent : bool, optional Whether to print the results immediately. Default in `False`. kwargs : dict, optional Additional keyword arguments to pass for peak detection algorithms. These are: pmin, pmax, threshold, width, floor_thres, etc.. Most of them are described in the `find_peaks` and `find_peaks_cwt` docs. """ precision = _get_config_value("precision") if engine not in ("cwt", "normal", "fft"): raise ValueError("Engine must be `cwt`, `fft` or `normal`.") if reference_point is None and engine != "fft": warnings.warn( f"Engine `{engine}` isn't available without reference point, falling back to FFT based prediction.", PySprintWarning) engine = "fft" if engine == "fft": pred, _ = find_center(*ifft_method(self.x, self.y)) if pred is None: if not silent: print("Prediction failed, skipping.") return print(f"The predicted GD is ± {pred:.{precision}f} fs.") if hasattr(self, "params"): self.params[3] = pred return if engine == "cwt": widths = kwargs.pop("widths", np.arange(1, 20)) floor_thres = kwargs.pop("floor_thres", 0.05) x_min, _, x_max, _ = self.detect_peak_cwt(widths=widths, floor_thres=floor_thres) # just validation _ = kwargs.pop("pmin", 0.1) _ = kwargs.pop("pmax", 0.1) _ = kwargs.pop("threshold", 0.35) else: pmin = kwargs.pop("pmin", 0.1) pmax = kwargs.pop("pmax", 0.1) threshold = kwargs.pop("threshold", 0.35) x_min, _, x_max, _ = self.detect_peak(pmin=pmin, pmax=pmax, threshold=threshold) # just validation _ = kwargs.pop("widths", np.arange(1, 10)) _ = kwargs.pop("floor_thres", 0.05) if kwargs: raise TypeError(f"Invalid argument:{kwargs}") try: closest_val, idx1 = find_nearest(x_min, reference_point) m_closest_val, m_idx1 = find_nearest(x_max, reference_point) except (ValueError, IndexError): if not silent: print("Prediction failed, skipping.. ") return try: truncated = np.delete(x_min, idx1) second_closest_val, _ = find_nearest(truncated, reference_point) except (IndexError, ValueError): if not silent: print("Prediction failed, skipping.. ") return try: m_truncated = np.delete(x_max, m_idx1) m_second_closest_val, _ = find_nearest(m_truncated, reference_point) except (IndexError, ValueError): if not silent: print("Prediction failed, skipping.. ") return lowguess = 2 * np.pi / np.abs(closest_val - second_closest_val) highguess = 2 * np.pi / np.abs(m_closest_val - m_second_closest_val) # estimate the GD with that if hasattr(self, "params"): self.params[3] = (lowguess + highguess) / 2 if not silent: print( f"The predicted GD is ± {((lowguess + highguess) / 2):.{precision}f} fs" f" based on reference point of {reference_point:.{precision}f}." )