MetadataModel

class MetadataModel(**kwargs)

Bases: MetaSubsetTabModel

Subclass of MetaSubsetTabModel for handling metadata-related processing and storage.

Public Methods

MetadataModel.build_all_points_histogram(event_generator: Generator, plot_type: str, bins: Any, sizes: bool, hist_min: float | None, hist_max: float | None) → Tuple[ndarray[tuple[int, ...], dtype[float64]], ndarray[tuple[int, ...], dtype[float64]], float, float]

Tally every sample of every event in a subset into one shared histogram.

The generator is walked twice, because the bins cannot be chosen until the extremes across all the events are known: the first pass takes the limits and the second counts into the edges they decide. The limits widen the shared ones the caller is already holding, so overlaid subsets stay on comparable bins.

Parameters:
  • event_generator (Generator) – the events of one subset, as the loader yields them

  • plot_type (str) – the all-points histogram variant being drawn

  • bins (Any) – a bin count, or a bin width when sizes is True, or None

  • sizes (bool) – does bins refer to a bin width (True) or a count (False)

  • hist_min (Optional[float]) – the shared lower limit so far, or None for the first dataset

  • hist_max (Optional[float]) – the shared upper limit so far, or None for the first dataset

Returns:

the bin centers, the counts, and the widened shared limits

Return type:

Tuple[npt.NDArray[np.float64], npt.NDArray[np.float64], float, float]

Raises:

ValueError – if plot_type is unrecognised, or bins cannot be resolved

MetadataModel.build_event_overlay(event_generator: Generator, plot_type: str) → List[Tuple[ndarray[tuple[int, ...], dtype[float64]], ndarray[tuple[int, ...], dtype[float64]]]]

Put every event of a subset on one baseline-subtracted, normalised axis.

Each event’s time base runs from its own padding: zero at the start of the event and one at its end, so events of different durations lie on top of each other and the paddings fall outside [0, 1].

Parameters:
  • event_generator (Generator) – the events of one subset, as the loader yields them

  • plot_type (str) – either ‘Raw Event Overlay’ or ‘Filtered Event Overlay’

Returns:

one (normalised time, rectified current) pair per event

Return type:

List[Tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]]

MetadataModel.calculate_heatmap(xdata: ndarray[tuple[int, ...], dtype[float64]], ydata: ndarray[tuple[int, ...], dtype[float64]], bins: Any, sizes: bool) → Tuple[ndarray[tuple[int, ...], dtype[float64]], ndarray[tuple[int, ...], dtype[float64]], ndarray[tuple[int, ...], dtype[float64]]]

Bin two columns into a 2-D histogram and return it log2-scaled.

The arrays arrive already filtered and log-scaled: that step is still MetaView’s, and stays there until all eight of its call sites can be converted together, so this method is handed exactly what the View’s own filter produced.

Empty bins come back as -1 rather than -inf, which is what lets the caller’s colourbar label them as a count of zero.

Parameters:
  • xdata (npt.NDArray[np.float64]) – the filtered x values

  • ydata (npt.NDArray[np.float64]) – the filtered y values

  • bins (Any) – number of bins (if sizes is False) or size of bins (if sizes is True); a list from the controls, or None to estimate

  • sizes (bool) – does the bins parameter refer to bin sizes (True) or counts (False)

Returns:

bin-center x values, bin-center y values, and the log2-scaled 2-D histogram counts

Return type:

Tuple[npt.NDArray[np.float64], npt.NDArray[np.float64], npt.NDArray[np.float64]]

Raises:

ValueError – if bins is an invalid entry when sizes is False

MetadataModel.categorical_counts(datasets: Sequence[ndarray[tuple[int, ...], dtype[Any]]]) → List[Tuple[List[str], ndarray[tuple[int, ...], dtype[float64]]]]

Tally how often each category occurs, for every overlaid dataset at once.

Counting occurrences is aggregation rather than drawing, and the tallies are exported with the plot, so they are made here rather than in the widget. The loop is here rather than a round trip per dataset, for the reason kernel_densities() records: the bar chart redraws every accumulated dataset on each update.

Missing values are counted as their own category rather than being allowed to reach np.unique, which sorts and so raises “’<’ not supported between instances of ‘NoneType’ and ‘str’” on a column holding SQL NULLs. A float column does not raise but labels the bar “nan”, which tells the user no more than “null” does and does not match what they see elsewhere. Counting them separately also keeps the real categories in the order they had before, which stringifying everything up front would not: “10” sorts before “2”.

The bars come back largest first, so the tallest is drawn on the left. The order is decided from the total across every overlaid dataset rather than per dataset, because matplotlib takes a category axis’s order from the first series drawn: ordering each dataset by its own counts would leave only the first one looking sorted. Ties keep the order the tally gave them, which is the numeric order for a numeric column.

Parameters:

datasets (Sequence[npt.NDArray[Any]]) – one array of raw column values per overlaid dataset

Returns:

per dataset, its category names and their counts as floats

Return type:

List[Tuple[List[str], npt.NDArray[np.float64]]]

MetadataModel.fit_capture_rate(data: ndarray[tuple[int, ...], dtype[float64]], bins: Any, sizes: bool = False) → Tuple[ndarray[tuple[int, ...], dtype[float64]], ndarray[tuple[int, ...], dtype[float64]], ndarray[tuple[int, ...], dtype[float64]], ndarray[tuple[int, ...], dtype[float64]], float, float]

Bin log inter-event times, fit an exponential to them, and report the rate.

The bin edges come back so the View can draw the histogram on exactly the edges the fit was made against, rather than on its own binning of the same request. The counts come back for the same reason.

This path answers an overflow in the automatic bin rule with Sturges’ expression rather than the 100 bins the density and histogram paths use, which is why it does not share _resolve_1d_bins().

Parameters:
  • data (npt.NDArray[np.float64]) – the base-10 logarithm of the inter-event times

  • bins (Any) – a bin count, or a bin width when sizes is True, or None to estimate one

  • sizes (bool) – does bins refer to a bin width (True) or a count (False)

Returns:

bin edges, bin centers, counts, the fitted curve, the rate in Hz, and its error

Return type:

Tuple[npt.NDArray[np.float64], npt.NDArray[np.float64], npt.NDArray[np.float64], npt.NDArray[np.float64], float, float]

MetadataModel.histogram_bin_edges(all_data: ndarray[tuple[int, ...], dtype[float64]], bins: Any, sizes: bool, hist_min: float, hist_max: float) → Tuple[ndarray[tuple[int, ...], dtype[float64]], ndarray[tuple[int, ...], dtype[float64]], ndarray[tuple[int, ...], dtype[float64]]]

Choose one set of bin edges for every overlaid histogram dataset.

The count is decided from all the overlaid data at once and the edges span the shared limits, which is what puts every dataset on the same bins and lets them be compared.

The histogram path used to coerce the bin width with float() inside a bare except Exception where the density path divided directly inside an except TypeError. The two differ only for a width of zero or a non-numeric one, and the controls refuse both - validate_inputs disables the plot button on bin_value <= 0 or a value float() rejects - so _resolve_1d_bins() serves both and no behaviour changes.

Parameters:
  • all_data (npt.NDArray[np.float64]) – every overlaid dataset’s filtered values, concatenated

  • bins (Any) – a bin count, or a bin width when sizes is True, or None

  • sizes (bool) – does bins refer to a bin size (True) or a count (False)

  • hist_min (float) – the shared lower limit across overlaid datasets

  • hist_max (float) – the shared upper limit across overlaid datasets

Returns:

the bin edges, the bin centers, and the bin widths

Return type:

Tuple[npt.NDArray[np.float64], npt.NDArray[np.float64], npt.NDArray[np.float64]]

MetadataModel.interevent_log_times(times: ndarray[tuple[int, ...], dtype[float64]]) → ndarray[tuple[int, ...], dtype[float64]]

Turn a column of event times into the log10 inter-event times to fit.

Capture is Poisson, so the quantity the capture-rate fit is about is the gap between consecutive events rather than the times themselves; the fit then works in log-time, which is why _log_exp_pdf() carries a Jacobian. Sorting first makes the gaps meaningful for a column that arrived in any order.

Non-positive intervals are dropped because log10 has nothing to say about them - two events sharing a timestamp produce a zero gap.

Parameters:

times (npt.NDArray[np.float64]) – the event times, in any order

Returns:

the base-10 logarithm of the positive inter-event times

Return type:

npt.NDArray[np.float64]

MetadataModel.kernel_densities(datasets: Sequence[ndarray[tuple[int, ...], dtype[float64]]], bins: Any, sizes: bool, hist_min: float | None, hist_max: float | None) → List[Tuple[ndarray[tuple[int, ...], dtype[float64]], ndarray[tuple[int, ...], dtype[float64]]]]

Estimate the kernel density of every overlaid dataset in one call.

The density plot redraws every accumulated dataset on each update, so the loop runs here rather than round-tripping per dataset and parking each answer on the widget.

Parameters:
  • datasets (Sequence[npt.NDArray[np.float64]]) – one filtered array per overlaid dataset

  • bins (Any) – a bin count, or a bin width when sizes is True, or None

  • sizes (bool) – does bins refer to a bin size (True) or a count (False)

  • hist_min (Optional[float]) – the shared lower limit across overlaid datasets, if known

  • hist_max (Optional[float]) – the shared upper limit across overlaid datasets, if known

Returns:

one (positions, density) pair per dataset, index-aligned with them

Return type:

List[Tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]]

MetadataModel.kernel_density(data: ndarray[tuple[int, ...], dtype[float64]], bins: Any, sizes: bool, hist_min: float | None, hist_max: float | None) → Tuple[ndarray[tuple[int, ...], dtype[float64]], ndarray[tuple[int, ...], dtype[float64]]]

Estimate one dataset’s kernel density, evaluated across its own range.

The evaluated curve comes back with the positions, rather than the estimator itself, so the View needs no scipy to draw it. The View used to call the estimator three times on the same points - once to plot, once to shade, once to cache - and gets one array now.

Parameters:
  • data (npt.NDArray[np.float64]) – the filtered values

  • bins (Any) – a bin count, or a bin width when sizes is True, or None

  • sizes (bool) – does bins refer to a bin size (True) or a count (False)

  • hist_min (Optional[float]) – the shared lower limit across overlaid datasets, if known

  • hist_max (Optional[float]) – the shared upper limit across overlaid datasets, if known

Returns:

the positions the density was evaluated at, and the density there

Return type:

Tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]

MetadataModel.logscale_and_filter_datasets(datasets: Sequence[ndarray[tuple[int, ...], dtype[Any]]], logscale: bool) → List[ndarray[tuple[int, ...], dtype[float64]]]

Filter and log-scale each overlaid dataset on its own.

The columns within one plot are filtered together, because a row dropped for one axis has to go for all of them. Overlaid datasets are different subsets of different lengths, so each is filtered by itself - handing them to logscale_and_filter_columns() together would mask them against one another and fail on the first pair whose lengths differ.

Parameters:
  • datasets (Sequence[npt.NDArray[Any]]) – one raw column array per overlaid dataset

  • logscale (bool) – log-scale the values before they are binned?

Returns:

the surviving values, one array per dataset and in the same order

Return type:

List[npt.NDArray[np.float64]]

MetadataModel.overlaid_histograms(datasets: Sequence[ndarray[tuple[int, ...], dtype[float64]]], bins: Any, sizes: bool, hist_min: float, hist_max: float, norm: bool) → Tuple[ndarray[tuple[int, ...], dtype[float64]], ndarray[tuple[int, ...], dtype[float64]], ndarray[tuple[int, ...], dtype[float64]], List[ndarray[tuple[int, ...], dtype[float64]]]]

Bin every overlaid dataset onto one shared set of edges, and count them.

The bin decision and the counting are both here, together. Tallying values into bins is aggregation, and the counts are exported with the plot, so the widget should not be doing it - the comment that used to say the counting “stays with the drawing” was reasoning from which import it needed rather than from whose responsibility it is.

The edges come from all the overlaid data at once, which is what puts every dataset on comparable bins; the counts are then per dataset so each draws its own bars.

Parameters:
  • datasets (Sequence[npt.NDArray[np.float64]]) – one filtered array per overlaid dataset

  • bins (Any) – a bin count, or a bin width when sizes is True, or None

  • sizes (bool) – does bins refer to a bin width (True) or a count (False)

  • hist_min (float) – the shared lower limit across overlaid datasets

  • hist_max (float) – the shared upper limit across overlaid datasets

  • norm (bool) – express each dataset as a fraction of itself rather than a count

Returns:

the bin edges, the bin centers, the bin widths, and one count array per dataset

Return type:

Tuple[npt.NDArray[np.float64], npt.NDArray[np.float64], npt.NDArray[np.float64], List[npt.NDArray[np.float64]]]

MetadataModel.widen_shared_limits(values: ndarray[tuple[int, ...], dtype[float64]], hist_min: float | None, hist_max: float | None) → Tuple[float, float]

Stretch the limits the overlay is binned against to take one more dataset.

The limits describe the filtered values, which is what is drawn, and they only ever widen: a dataset lying inside the range already accumulated leaves it alone, so every overlaid subset stays on comparable bins.

Parameters:
  • values (npt.NDArray[np.float64]) – the newest dataset’s filtered values

  • hist_min (Optional[float]) – the lower limit so far, or None for the first dataset

  • hist_max (Optional[float]) – the upper limit so far, or None for the first dataset

Returns:

the widened lower and upper limits

Return type:

Tuple[float, float]

Private Methods

MetadataModel._init() → None

Perform additional initialization specific to the algorithm being implemented. Must be implemented by subclasses.

This function is called at the end of the class constructor to perform additional initialization specific to the algorithm being implemented. kwargs provided to the base class constructor are available as class attributes.