pyfli.data_vnp.cv_plot#

Plot the coefficient of variation of a fitted parameter map against photon count.

This module belongs to pyfli.data_vnp and is part of PyFLI visualization, normalization, plotting, and mono-versus-bi-exponential comparison tools. Public API includes class CVPlot.

Classes

CVPlot([save_path, fig_name])

Bin a fitted parameter map (typically a lifetime map) by photon count and plot its per-bin coefficient of variation, cv = std(parameter) / mean(parameter).

class CVPlot(save_path=None, fig_name=None)[source]#

Bases: object

Bin a fitted parameter map (typically a lifetime map) by photon count and plot its per-bin coefficient of variation, cv = std(parameter) / mean(parameter).

For a shot-noise-limited lifetime estimate, cv is expected to scale as photon_count ** -0.5 (the Cramer-Rao bound for exponential-decay data) – this class is the standard way to check that scaling empirically, pooled over all pixels or broken out per spatial cluster.

The class operates directly on a flat {key: (H, W) array} maps dict, i.e. exactly results["results"]["maps"] as returned by any of PyFLI’s fitters – FLICPUProcessor, FLIGPUProcessor, or an MLEFLIFitter-backed CPU run – as well as the ground-truth maps dict produced by the simulator, since all of them share the same key naming convention (tau_map / tau1_map / tau2_map / photon_count_map / …). It has no dependency on how those maps were produced.

Parameters:
  • save_path (str | None) – Output path used when saving generated figures.

  • fig_name (str | None) – Figure name or output stem used when saving; defaults to "cv_plot".

compute(maps, tau_keys, photon_map=None, photon_key='photon_count_map', mask=None, cluster_mask=None, cluster_names=None, n_bins=10, bin_mode='quantile', bin_scope='pooled')[source]#

Bin pixels by photon count and compute per-bin mean/std/cv for each of tau_keys, pooled over mask or broken out per cluster_mask label.

Parameters:
  • maps (dict[str, np.ndarray]) – Flat {key: (H, W) array} dict of fitted (or ground-truth) parameter maps, e.g. results["results"]["maps"] from any PyFLI fitter.

  • tau_keys (str | list[str]) – Key(s) in maps to compute the coefficient of variation for (not limited to literal lifetimes – any per-pixel parameter map works, e.g. "tau_map", ["tau1_map", "tau2_map", "tau_mean_map"], "alpha1_map").

  • photon_map (np.ndarray | None) – Photon-count map to bin by, e.g. decay.sum(axis=-1) computed directly from the raw decay cube. Takes precedence over photon_key when given – preferred when available, since a fitted photon_count_map amplitude can differ in scale/definition from the true detected photon count.

  • photon_key (str) – Key in maps to use as the photon-count map when photon_map is not given.

  • mask (np.ndarray | None) – Boolean (H, W) mask selecting pixels to include. None keeps every finite pixel.

  • cluster_mask (np.ndarray | None) – Integer (H, W) label map for per-region binning. 0 = background (excluded); 1, 2, 3, ... = cluster labels. None pools every selected pixel together instead.

  • cluster_names (dict[int, str] | None) – Optional {label: name} mapping for display; must cover every non-zero label present in cluster_mask. Defaults to f"cluster_{label}".

  • n_bins (int) – Number of photon-count bins.

  • bin_mode (str) – "quantile" (equal-frequency bins) or "linear" (equal-width bins).

  • bin_scope (str) – "pooled" (default) computes ONE set of bin edges shared by every cluster, so clusters sit at directly comparable photon-count positions. "per_group" gives each cluster (or the single pooled group, if no cluster_mask) its own edges from its own photon-count distribution.

Returns:

Long-form frame with columns cluster (None when cluster_mask is not given), parameter, bin_rank, bin_center, bin_low, bin_high, mean, std, cv, count.

Return type:

pd.DataFrame

plot(df, target_keys=None, ncols=3, figsize=None, logx=False, palette=None, show_ideal_trend=False, show_powerlaw_fit=False, ideal_fit_space='linear', ideal_fit_weighted=False)[source]#

Plot cv (from compute) against the photon-count bin.

Without a cluster grouping (compute(cluster_mask=None)), draws ONE subplot with one line per target_keys entry. With a cluster grouping, draws a grid with one subplot PER target_keys entry, each with one line per cluster.

Parameters:
  • df (pd.DataFrame) – Output of compute.

  • target_keys (list[str] | None) – Which parameter values to plot; defaults to every parameter in df.

  • ncols (int) – Number of subplot columns (cluster grid only).

  • figsize (tuple[float, float] | None) – Figure size passed to Matplotlib.

  • logx (bool) – Use a log-scaled photon-count axis.

  • palette (dict[str, Any] | None) – {cluster_name: color} mapping (cluster grid only); defaults to a qualitative Seaborn palette.

  • show_ideal_trend (bool) – Overlay the theoretical shot-noise-limited trend cv = C / sqrt(N) as a dotted line for each data series (same color as the series), so deviation from ideal Poisson-limited precision is easy to spot. C is fit by least squares to that series’ own data; the -0.5 exponent is fixed (it’s the Cramer-Rao-bound slope, not a free fit parameter). Off by default.

  • show_powerlaw_fit (bool) – Overlay an a * N ** b power-law regression fit to each data series (log- log ordinary least squares, the closed-form fit that maximizes R^2 for this model) as a dashed line, labeled with the fitted equation and R^2. Off by default.

  • ideal_fit_space (str) – Residual space for fitting C of the ideal trend: "linear" (default; low-photon bins dominate) or "log" (every bin counts by its relative error). Only used when show_ideal_trend is True.

  • ideal_fit_weighted (bool) – Weight each bin by its pixel count when fitting C of the ideal trend, so sparsely populated bins pull it less. Only used when show_ideal_trend is True. Off by default.

Returns:

(fig, axes) – axes is a length-1 array in the pooled case, or the full subplot grid in the cluster case.

Return type:

tuple[Any, Any]

compute_and_plot(maps, tau_keys, photon_map=None, photon_key='photon_count_map', mask=None, cluster_mask=None, cluster_names=None, n_bins=10, bin_mode='quantile', bin_scope='pooled', target_keys=None, ncols=3, figsize=None, logx=False, palette=None, show_ideal_trend=False, show_powerlaw_fit=False, ideal_fit_space='linear', ideal_fit_weighted=False)[source]#

Convenience wrapper: compute then plot in one call. show_ideal_trend, show_powerlaw_fit, ideal_fit_space and ideal_fit_weighted are passed straight through to plot – see there.

Parameters:
Return type:

tuple[DataFrame, Any, Any]