Pairwise Comparisons

The pairwise module provides data structures for unit-by-unit comparison matrices. PairwiseCompMatrix holds a single (N, N) matrix (e.g., correlation or distance between all pairs of units), while PairwiseCompMatrixStack holds an (N, N, S) stack of such matrices across multiple conditions or time windows.

PairwiseCompMatrix

class spikelab.spikedata.pairwise.PairwiseCompMatrix(matrix, labels=None, metadata=<factory>)[source]

Bases: object

A data class for n x n pairwise comparison matrices (e.g., correlation, STTC).

matrix

The n x n comparison matrix.

Type:

np.ndarray

labels

Labels for the rows/columns (e.g., unit IDs).

Type:

list or None

metadata

Additional information about the matrix.

Type:

dict

Examples

Creating a PairwiseCompMatrix:

>>> matrix = np.array([[1.0, 0.5], [0.5, 1.0]])
>>> pcm = PairwiseCompMatrix(matrix=matrix, labels=["A", "B"])

Exporting to NetworkX:

>>> G = pcm.to_networkx()
>>> G = pcm.to_networkx(threshold=0.3)  # Only edges with |weight| > 0.3
>>> G = pcm.to_networkx(invert_weights=True)  # For shortest path algorithms

Getting a binary thresholded matrix:

>>> binary_pcm = pcm.threshold(0.4)  # Values > 0.4 become 1, else 0
to_networkx(threshold=None, invert_weights=False)[source]

Export the matrix to a NetworkX graph.

Parameters:
  • threshold (float or None) – If provided, only edges with absolute weight > threshold will be included.

  • invert_weights (bool) – If True, edge weights are set to (1 - value) instead of value. This is useful for weighted network metrics like shortest path length, where strong correlations (e.g., 0.9) should represent short/cheap paths rather than long/expensive paths.

Returns:

The exported graph.

Return type:

G (networkx.Graph)

Notes

When using NetworkX for weighted shortest path algorithms (e.g., nx.shortest_path_length), edge weights are interpreted as distances. For correlation matrices where high values indicate strong relationships, set invert_weights=True so that: - Strong correlation (0.9) -> weight 0.1 (short path) - Weak correlation (0.1) -> weight 0.9 (long path)

threshold(threshold)[source]

Create a binary matrix based on a threshold.

Parameters:

threshold (float) – Values with absolute value > threshold become 1, otherwise 0.

Returns:

A new PairwiseCompMatrix with binary

(0/1) values.

Return type:

result (PairwiseCompMatrix)

Examples

>>> matrix = np.array([[1.0, 0.8, 0.2], [0.8, 1.0, 0.5], [0.2, 0.5, 1.0]])
>>> pcm = PairwiseCompMatrix(matrix=matrix)
>>> binary_pcm = pcm.threshold(0.4)
>>> print(binary_pcm.matrix)
[[1. 1. 0.]
 [1. 1. 1.]
 [0. 1. 1.]]
normalize(method='min_max', *, axis=None)[source]

Return a normalized copy of this matrix.

Parameters:
  • method (str) – Normalization method. One of "min_max" (scale to [0, 1]), "z_score" (subtract mean, divide by std), "row" (per-row min-max), or "col" (per-column min-max).

  • axis (str or None) – When set to "row" or "col", normalization is applied per-row or per-column instead of globally. When None (default), the entire matrix is normalized at once.

Returns:

A new PairwiseCompMatrix with

normalized values.

Return type:

result (PairwiseCompMatrix)

Notes

  • NaN values are ignored during computation and preserved in the output.

  • For "z_score", if the standard deviation is zero the result is filled with zeros (no division by zero).

remove_by_condition(condition, op, threshold, fill=nan)[source]

Return a copy with entries removed where a condition matrix satisfies a comparison.

Entries where the comparison op(condition, threshold) evaluates to True are replaced by fill; all other entries keep their original value from self.

Parameters:
  • condition (PairwiseCompMatrix) – Matrix to evaluate the comparison on. Must have the same shape as self.

  • op (str) – Comparison operator applied element-wise to the condition matrix. Standard: "lt" (<), "le" (<=), "gt" (>), "ge" (>=), "eq" (==), "ne" (!=). Absolute-value variants: "abs_lt", "abs_le", "abs_gt", "abs_ge" — these compare |condition| against the threshold.

  • threshold (float) – Threshold value for the comparison.

  • fill (float) – Replacement value for removed entries (default: NaN).

Returns:

Copy of self where entries satisfying

the condition are replaced by fill. Labels and metadata are preserved from self.

Return type:

result (PairwiseCompMatrix)

extract_lower_triangle()[source]

Extract lower triangle (excluding diagonal) from this correlation matrix.

Returns:

Lower triangle values as a 1D array with

shape (F,) where F = n*(n-1)/2.

Return type:

values (np.ndarray)

plot(ax=None, cmap=None, vmin=None, vmax=None, colorbar_label='', font_size=14, tick_labels=None, save_path=None)[source]

Plot the pairwise matrix as a heatmap.

Parameters:
  • ax (matplotlib.axes.Axes or None) – Target axes. If None a standalone figure is created.

  • cmap (str or None) – Matplotlib colormap name. If None, auto-selects "RdBu_r" for diverging data (contains both negative and positive values) or "viridis" otherwise.

  • vmin (float or None) – Colormap minimum.

  • vmax (float or None) – Colormap maximum.

  • colorbar_label (str) – Label for the colorbar.

  • font_size (int) – Font size for labels and ticks.

  • tick_labels (list[str] or None) – Custom tick labels for both axes. If None, uses self.labels (or integer indices when labels are not set).

  • save_path (str or None) – If provided (and ax is None), save the figure to this path and close it.

Returns:

(fig, ax) when ax is None, otherwise just ax.

Return type:

result

extract_pairs_by_group(unit_labels)[source]

Extract upper-triangle pair values grouped by unit label combinations.

Given a label array of length N (one per unit), splits the upper triangle of the matrix into groups based on each pair’s label combination. For example, a boolean label is_lower yields three groups: (False, False), (False, True), (True, True).

Parameters:

unit_labels (array-like) – Labels of length N assigning each unit to a group. Can be boolean, integer, or string values.

Returns:

Mapping from (label_a, label_b) tuples to 1D

arrays of pair values. Keys are canonically ordered so that label_a <= label_b. Only groups with at least one pair are included. The values within each group preserve the order produced by np.triu_indices, making results from different matrices with the same labels directly alignable for paired tests.

Return type:

groups (dict)

plot_spatial_network(ax, positions, edge_threshold=None, top_pct=None, node_size_range=(2, 20), node_cmap='viridis', node_linewidth=0.2, edge_color='red', edge_linewidth=0.6, edge_alpha_range=(0.15, 1.0), scale_bar_um=500, font_size=None)[source]

Plot this pairwise matrix as a spatial network on MEA positions.

Unit positions must be supplied as positions – extract them from SpikeData.neuron_attributes (e.g. np.array([[na['x'], na['y']] for na in sd.neuron_attributes])).

Thin wrapper around plot_utils.plot_spatial_network.

Parameters:
  • ax (matplotlib.axes.Axes) – Target axes.

  • positions (np.ndarray) – Unit positions, shape (N, 2) with columns [x, y] in micrometres.

  • edge_threshold (float or None) – Minimum matrix value to draw an edge.

  • top_pct (float or None) – Percentage of top edges to draw.

  • node_size_range (tuple) – (min_size, max_size) in points² for scatter markers.

  • node_cmap (str) – Matplotlib colourmap for node colour.

  • node_linewidth (float) – Outline width of node markers.

  • edge_color (str) – Colour for network edges.

  • edge_linewidth (float) – Line width for network edges.

  • edge_alpha_range (tuple) – (min_alpha, max_alpha) for edge transparency.

  • scale_bar_um (float) – Scale bar length in micrometres (0 to omit).

  • font_size (int or None) – Font size for scale bar label.

Returns:

The scatter

artist, useful for adding a colorbar.

Return type:

scatter (matplotlib.collections.PathCollection)

__init__(matrix, labels=None, metadata=<factory>)

PairwiseCompMatrixStack

class spikelab.spikedata.pairwise.PairwiseCompMatrixStack(stack, labels=None, times=None, metadata=<factory>)[source]

Bases: object

A data class for a stack of n x n pairwise comparison matrices (e.g., across slices or time bins).

stack

The n x n x S stack of comparison matrices, where S is the number of slices.

Type:

np.ndarray

labels

Labels for the rows/columns (e.g., unit IDs).

Type:

list or None

times

Time windows (start, end) associated with each matrix in the stack.

Type:

list of tuple or None

metadata

Additional information about the stack.

Type:

dict

The stack supports flexible indexing:

  • Single index: Returns a PairwiseCompMatrix for that slice.

    >>> stack[0]  # First matrix as PairwiseCompMatrix
    
  • Slice: Returns a new PairwiseCompMatrixStack with the selected range.

    >>> stack[0:5]  # First 5 matrices as a new stack
    >>> stack[::2]  # Every other matrix
    
  • Iteration: Iterate over all matrices in the stack.

    >>> for matrix in stack:
    ...     print(matrix.matrix.shape)
    
  • subslice(): Select specific non-contiguous slices by index.

    >>> stack.subslice([0, 2, 5])  # Select slices 0, 2, and 5
    

Examples

Creating a stack:

>>> stack_data = np.random.rand(5, 5, 10)  # 5x5 matrices, 10 slices
>>> stack = PairwiseCompMatrixStack(stack=stack_data)

Slicing:

>>> sub_stack = stack[0:3]  # Get first 3 slices
>>> single_matrix = stack[5]  # Get 6th slice as PairwiseCompMatrix

Binary thresholding:

>>> binary_stack = stack.threshold(0.5)  # Threshold all matrices
subslice(indices)[source]

Select specific slices from the stack by their indices.

Parameters:

indices (list of int) – List of slice indices to select.

Returns:

A new stack containing only the

selected slices.

Return type:

result (PairwiseCompMatrixStack)

Examples

>>> stack = PairwiseCompMatrixStack(stack=np.random.rand(5, 5, 10))
>>> sub = stack.subslice([0, 2, 5, 9])  # Select specific slices
>>> len(sub)  # 4
threshold(threshold)[source]

Create a binary stack based on a threshold.

Parameters:

threshold (float) – Values with absolute value > threshold become 1, otherwise 0.

Returns:

A new stack with binary (0/1)

values.

Return type:

result (PairwiseCompMatrixStack)

Examples

>>> stack = PairwiseCompMatrixStack(stack=np.random.rand(5, 5, 10))
>>> binary_stack = stack.threshold(0.5)
normalize(method='min_max', *, axis=None, per_slice=False)[source]

Return a normalized copy of this stack.

Parameters:
  • method (str) – Normalization method ("min_max", "z_score", "row", or "col"). See PairwiseCompMatrix.normalize for details.

  • axis (str or None) – "row" or "col" for per-row / per-column normalization within each N x N slice, or None for global normalization.

  • per_slice (bool) – When True, each slice is normalized independently. When False (default), statistics are computed across the entire stack.

Returns:

A new stack with

normalized values.

Return type:

result (PairwiseCompMatrixStack)

remove_by_condition(condition, op, threshold, fill=nan)[source]

Return a copy with entries removed where a condition satisfies a comparison.

Entries where op(condition, threshold) evaluates to True are replaced by fill; all other entries keep their original value from self. The condition is applied element-wise across all slices.

Parameters:
  • condition (PairwiseCompMatrix or PairwiseCompMatrixStack) – Matrix or stack to evaluate the comparison on. A single PairwiseCompMatrix is broadcast across all slices. A PairwiseCompMatrixStack must have the same shape (N, N, S) as self.

  • op (str) – Comparison operator applied element-wise to the condition. Standard: "lt" (<), "le" (<=), "gt" (>), "ge" (>=), "eq" (==), "ne" (!=). Absolute-value variants: "abs_lt", "abs_le", "abs_gt", "abs_ge" — these compare |condition| against the threshold.

  • threshold (float) – Threshold value for the comparison.

  • fill (float) – Replacement value for removed entries (default: NaN).

Returns:

Copy of self where entries

satisfying the condition are replaced by fill. Labels, times, and metadata are preserved from self.

Return type:

result (PairwiseCompMatrixStack)

mean(ignore_nan=True)[source]

Compute the mean matrix across the stack.

Parameters:

ignore_nan (bool) – Whether to use np.nanmean to ignore NaN values in the average.

Returns:

The element-wise mean across all

slices.

Return type:

mean_matrix (PairwiseCompMatrix)

plot_mean(ax=None, ignore_nan=True, cmap=None, vmin=None, vmax=None, colorbar_label='', font_size=14, tick_labels=None, save_path=None)[source]

Plot the mean matrix across all slices as a heatmap.

Computes nanmean (or mean) over the stack axis and delegates to PairwiseCompMatrix.plot().

Parameters:
  • ax (matplotlib.axes.Axes or None) – Target axes. If None a standalone figure is created.

  • ignore_nan (bool) – Use np.nanmean to ignore NaN values.

  • cmap (str or None) – Matplotlib colormap name. If None, auto-selects based on whether the mean matrix is diverging.

  • vmin (float or None) – Colormap minimum.

  • vmax (float or None) – Colormap maximum.

  • colorbar_label (str) – Label for the colorbar.

  • font_size (int) – Font size for labels and ticks.

  • tick_labels (list[str] or None) – Custom tick labels for both axes. If None, uses self.labels (or integer indices when labels are not set).

  • save_path (str or None) – If provided (and ax is None), save the figure to this path and close it.

Returns:

(fig, ax) when ax is None, otherwise just ax.

Return type:

result

extract_lower_triangle_features()[source]

Extract lower triangle (excluding diagonal) from each correlation matrix in the stack.

Returns:

2D matrix of shape (S, F) where each

row contains lower triangle values for that correlation matrix. F = n*(n-1)/2 (number of unique pairs).

Return type:

features (np.ndarray)

dim_red_on_lower_diagonal_corr_matrix(method='PCA', n_components=2, **kwargs)[source]

Apply dimensionality reduction (PCA or UMAP) to the lower triangle of each correlation matrix in the stack.

Parameters:
  • method (str) – Dimensionality reduction method to use. "PCA" (default) or "UMAP".

  • n_components (int) – Number of components (dimensions) in the output manifold.

  • **kwargs – Additional keyword arguments passed through to UMAP when method='UMAP' (e.g., n_neighbors, min_dist, metric).

Returns:

For PCA: a 3-tuple

(embedding, explained_variance_ratio, components) with shapes (S, n_components), (n_components,), and (n_components, F) where F = N*(N-1)//2. For UMAP: a 2-tuple (embedding, trustworthiness) with embedding shape (S, n_components) and trustworthiness a float in [0, 1].

Return type:

result (tuple)

__init__(stack, labels=None, times=None, metadata=<factory>)