BiotunerGroup (BETA)#
Warning
🧪 BETA Feature
The BiotunerGroup module is currently in beta. The API may change in future releases. We welcome feedback and contributions to help stabilize this feature.
BiotunerGroup: Group-level analysis for multiple time series
This module provides a clean, elegant interface for running biotuner analysis on multiple time series (trials, electrodes, etc.) with automatic aggregation, comparison, and visualization capabilities.
Author: Biotuner Team Date: December 2025
- class BiotunerGroup(data: ndarray, sf: int, axis_labels: List[str] | None = None, metadata: Dict[str, List] | None = None, store_objects: bool = True, **biotuner_kwargs)[source]#
Bases:
objectGroup-level biotuner analysis for multiple time series.
This class manages multiple biotuner objects, enabling batch processing, aggregation of metrics, group comparisons, and unified visualizations.
- Parameters:
data (ndarray) – Time series data with shape: - 2D: (n_series, n_samples) - e.g., electrodes × timepoints - 3D: (n_trials, n_channels, n_samples) - e.g., trials × electrodes × timepoints
sf (int) – Sampling frequency in Hz
axis_labels (list of str, optional) – Names for each dimension, e.g., [‘trials’, ‘electrodes’] If None, uses generic labels [‘dim0’, ‘dim1’, …]
metadata (dict, optional) – Metadata for grouping/comparison. Keys are column names, values are lists with length matching the first dimension of data. Example: {‘condition’: [‘rest’, ‘rest’, ‘task’, ‘task’], ‘subject’: [‘S1’, ‘S2’, ‘S1’, ‘S2’]}
store_objects (bool, default=True) – If True, stores all individual biotuner objects. If False, only stores results to save memory (individual objects not accessible).
**biotuner_kwargs (dict) – Default parameters passed to all compute_biotuner objects (e.g., peaks_function=’EMD’, precision=0.1, n_harm=10)
- objects#
Individual biotuner objects (if store_objects=True)
- Type:
list of compute_biotuner
- results#
Summary DataFrame with all computed metrics
- Type:
pandas.DataFrame
- shape#
Shape of the input data
- Type:
tuple
- n_series#
Total number of time series
- Type:
int
Examples
Basic usage with 2D data:
>>> # Single electrode across trials >>> data = np.random.randn(10, 5000) # 10 trials, 5000 samples >>> btg = BiotunerGroup(data, sf=1000) >>> btg.compute_peaks(peaks_function='FOOOF', min_freq=1, max_freq=50) >>> summary = btg.summary() >>> print(summary)
With 3D data and metadata:
>>> # Multiple electrodes across trials >>> data = np.random.randn(20, 64, 5000) # 20 trials, 64 channels, 5000 samples >>> metadata = {'condition': ['rest']*10 + ['task']*10} >>> btg = BiotunerGroup(data, sf=1000, axis_labels=['trials', 'electrodes'], metadata=metadata) >>> btg.compute_peaks(peaks_function='EMD') >>> btg.compute_metrics() >>> >>> # Compare groups >>> comparison = btg.compare_groups('condition', metric='harmsim') >>> >>> # Plot >>> btg.plot_metric_distribution('harmsim', groupby='condition')
Memory-efficient mode for large datasets:
>>> btg = BiotunerGroup(data, sf=1000, store_objects=False) >>> btg.compute_peaks() >>> summary = btg.summary() # Only summary stats kept in memory
- compute_peaks(peaks_function: str | None = None, min_freq: float = 1, max_freq: float = 60, precision: float | None = None, n_peaks: int = 5, n_jobs: int = 1, verbose: bool = False, **kwargs) BiotunerGroup[source]#
Extract spectral peaks for all time series.
- Parameters:
peaks_function (str, optional) – Peak extraction method. If None, uses value from biotuner_kwargs or ‘EMD’
min_freq (float, default=1) – Minimum frequency for peak extraction (Hz)
max_freq (float, default=60) – Maximum frequency for peak extraction (Hz)
precision (float, optional) – Frequency precision (Hz). If None, uses value from biotuner_kwargs or 0.1
n_peaks (int, default=5) – Number of peaks to extract
n_jobs (int, default=1) – Number of parallel jobs. -1 uses all CPUs, 1 for sequential processing. Recommended for large datasets (>50 series) or FOOOF peak detection.
verbose (bool, default=False) – Print progress
**kwargs (dict) – Additional parameters for peaks_extraction
- Returns:
self (BiotunerGroup) – Returns self for method chaining
- compute_extension(method: str = 'consonant_harmonic_fit', n_harm: int | None = None, n_jobs: int = 1, verbose: bool = False, **kwargs) BiotunerGroup[source]#
Compute peak extensions for all time series.
- Parameters:
method (str, default=’consonant_harmonic_fit’) – Extension method: ‘harmonic_fit’, ‘consonant’, ‘multi_consonant’, ‘consonant_harmonic_fit’, ‘multi_consonant_harmonic_fit’
n_harm (int, optional) – Number of harmonics. If None, uses value from biotuner_kwargs or 10
n_jobs (int, default=1) – Number of parallel jobs. -1 uses all CPUs, 1 for sequential processing.
verbose (bool, default=False) – Print progress
**kwargs (dict) – Additional parameters for peaks_extension
- Returns:
self (BiotunerGroup) – Returns self for method chaining
- compute_metrics(n_harm: int | None = None, delta_lim: int = 20, n_jobs: int = 1, verbose: bool = False, **kwargs) BiotunerGroup[source]#
Compute peak metrics for all time series.
- Parameters:
n_harm (int, optional) – Number of harmonics. If None, uses value from biotuner_kwargs or 10
delta_lim (int, default=20) – Delta limit for subharmonic tension calculation
n_jobs (int, default=1) – Number of parallel jobs. -1 uses all CPUs, 1 for sequential processing.
verbose (bool, default=False) – Print progress
**kwargs (dict) – Additional parameters for compute_peaks_metrics
- Returns:
self (BiotunerGroup) – Returns self for method chaining
- compute_diss_curve(input_type: str = 'peaks', denom: int = 1000, max_ratio: float = 2, n_jobs: int = 1, verbose: bool = False, **kwargs) BiotunerGroup[source]#
Compute dissonance curves for all time series.
- Parameters:
input_type (str, default=’peaks’) – Input type: ‘peaks’ or ‘extended_peaks’
denom (int, default=1000) – Denominator for dissonance curve resolution
max_ratio (float, default=2) – Maximum ratio (typically octave = 2)
n_jobs (int, default=1) – Number of parallel jobs. -1 uses all CPUs, 1 for sequential processing.
verbose (bool, default=False) – Print progress
**kwargs (dict) – Additional parameters for compute_diss_curve
- Returns:
self (BiotunerGroup) – Returns self for method chaining
- compute_harmonic_entropy(input_type: str = 'peaks', res: float = 0.001, spread: float = 0.01, octave: float = 2, verbose: bool = False, **kwargs) BiotunerGroup[source]#
Compute harmonic entropy for all time series.
- Parameters:
input_type (str, default=’peaks’) – Input type: ‘peaks’ or ‘extended_peaks’
res (float, default=0.001) – Resolution of ratio steps
spread (float, default=0.01) – Spread of normal distribution
octave (float, default=2) – Octave value
verbose (bool, default=False) – Print progress
**kwargs (dict) – Additional parameters for compute_harmonic_entropy
- Returns:
self (BiotunerGroup) – Returns self for method chaining
- compute_euler_fokker(method: str = 'peaks', octave: float = 2, verbose: bool = False) BiotunerGroup[source]#
Compute Euler-Fokker scales for all time series.
- Parameters:
method (str, default=’peaks’) – Method: ‘peaks’ or ‘extended_peaks’
octave (float, default=2) – Period interval
verbose (bool, default=False) – Print progress
- Returns:
self (BiotunerGroup) – Returns self for method chaining
- compute_harmonic_tuning(list_harmonics: List[int] | None = None, octave: float = 2, min_ratio: float = 1, max_ratio: float = 2, verbose: bool = False) BiotunerGroup[source]#
Compute harmonic tunings for all time series.
- Parameters:
list_harmonics (list of int, optional) – Harmonic positions for scale construction
octave (float, default=2) – Period reference
min_ratio (float, default=1) – Unison value
max_ratio (float, default=2) – Octave value
verbose (bool, default=False) – Print progress
- Returns:
self (BiotunerGroup) – Returns self for method chaining
- get_attribute(attr_name: str, as_array: bool = False, missing_value: any = nan) List | ndarray[source]#
Get an attribute from all biotuner objects.
- Parameters:
attr_name (str) – Attribute name (e.g., ‘peaks’, ‘peaks_ratios’, ‘peaks_metrics’)
as_array (bool, default=False) – If True, attempt to stack results as numpy array. Only works if all results have the same shape.
missing_value (any, default=np.nan) – Value to use if attribute doesn’t exist for an object
- Returns:
results (list or ndarray) – List or array of attribute values from all objects
Examples
>>> peaks_all = btg.get_attribute('peaks') >>> amps_all = btg.get_attribute('amps') >>> ratios_all = btg.get_attribute('peaks_ratios')
- summary(metrics: List[str] | None = None, aggregation: str | List[str] = 'all', include_index: bool = True) DataFrame[source]#
Generate summary DataFrame with aggregated metrics.
- Parameters:
metrics (list of str, optional) – Which metrics to include. If None, includes all available metrics. Examples: [‘harmsim’, ‘cons’, ‘tenney’, ‘euler’, ‘n_peaks’]
aggregation (str or list of str, default=’all’) – Aggregation methods to apply: - ‘mean’, ‘std’, ‘median’, ‘min’, ‘max’, ‘sem’ - ‘all’: computes all of the above - list: specific subset, e.g., [‘mean’, ‘std’]
include_index (bool, default=True) – Include index columns and metadata in output
- Returns:
df (pandas.DataFrame) – Summary DataFrame with one row per time series
Examples
>>> # Get all statistics >>> summary = btg.summary() >>> >>> # Get specific metrics and stats >>> summary = btg.summary(metrics=['harmsim', 'cons'], aggregation=['mean', 'std']) >>> >>> # Compare across conditions >>> summary = btg.summary() >>> summary.groupby('condition').mean()
- tuning_summary(include_index: bool = True) DataFrame[source]#
Generate summary DataFrame focused on tuning/scale metrics.
This is a convenience method that extracts only scale-related metrics from the full summary, making it easier to analyze tuning characteristics across time series.
- Parameters:
include_index (bool, default=True) – Include index columns and metadata in output
- Returns:
df (pandas.DataFrame) – Summary DataFrame with tuning metrics (scale_* columns)
Examples
>>> # Compute tunings >>> btg.compute_diss_curve().compute_harmonic_entropy() >>> >>> # Get tuning metrics summary >>> tuning_df = btg.tuning_summary() >>> >>> # Compare tuning metrics across conditions >>> tuning_df.groupby('condition').mean()
- get_tuning_scales(scale_type: str = 'diss_scale') List[source]#
Extract tuning scales from all biotuner objects.
- Parameters:
scale_type (str, default=’diss_scale’) – Type of scale to extract: - ‘diss_scale’: Dissonance curve scale - ‘HE_scale’: Harmonic entropy scale - ‘euler_fokker’: Euler-Fokker scale - ‘harm_tuning_scale’: Harmonic tuning scale
- Returns:
scales (list) – List of scales (each scale is a list of ratios)
Examples
>>> btg.compute_diss_curve() >>> scales = btg.get_tuning_scales('diss_scale') >>> >>> # Analyze scale sizes >>> scale_sizes = [len(s) for s in scales] >>> print(f"Mean scale size: {np.mean(scale_sizes):.1f} steps")
- compare_groups(groupby: str, metric: str = 'harmsim', test: str = 'ttest', alpha: float = 0.05, plot: bool = True) DataFrame[source]#
Compare metrics across metadata groups.
- Parameters:
groupby (str) – Metadata column to group by
metric (str, default=’harmsim’) – Metric to compare
test (str, default=’ttest’) – Statistical test: ‘ttest’, ‘anova’, ‘mannwhitneyu’, ‘kruskal’
alpha (float, default=0.05) – Significance level
plot (bool, default=True) – Generate comparison plot
- Returns:
results (pandas.DataFrame) – Comparison results with statistics
Examples
>>> comparison = btg.compare_groups('condition', metric='harmsim_mean') >>> print(comparison)
- plot_group_peaks(show_individual: bool = False, aggregate: str = 'mean', xmin: float = 1, xmax: float = 60, show_bands: bool = True, figsize: Tuple[float, float] | None = None, alpha_individual: float | None = None, **kwargs)[source]#
Plot aggregated peak spectrum across all time series.
- Parameters:
show_individual (bool, default=False) – Show individual PSDs in background
aggregate (str, default=’mean’) – Aggregation method: ‘mean’, ‘median’
xmin (float, default=1) – Minimum frequency (Hz)
xmax (float, default=60) – Maximum frequency (Hz)
show_bands (bool, default=True) – Show frequency band labels
figsize (tuple, optional) – Figure size (default from plot_config)
alpha_individual (float, optional) – Transparency for individual traces (default from plot_config)
**kwargs (dict) – Additional plotting parameters
- Returns:
fig, ax (matplotlib figure and axes)
- plot_peak_distribution(xmin: float = 1, xmax: float = 60, bins: int | None = None, show_bands: bool = True, figsize: Tuple[float, float] | None = None, **kwargs)[source]#
Plot distribution of all detected peaks across all time series.
- Parameters:
xmin (float, default=1) – Minimum frequency (Hz)
xmax (float, default=60) – Maximum frequency (Hz)
bins (int, optional) – Number of histogram bins (default from plot_config)
show_bands (bool, default=True) – Show frequency band markers
figsize (tuple, optional) – Figure size (default from plot_config)
**kwargs (dict) – Additional plotting parameters
- Returns:
fig, ax (matplotlib figure and axes)
- plot_metric_distribution(metric: str, groupby: str | None = None, kind: str = 'violin', figsize: Tuple[float, float] | None = None, **kwargs)[source]#
Plot distribution of a metric across time series.
- Parameters:
metric (str) – Metric to plot (column name from summary DataFrame)
groupby (str, optional) – Metadata column to group by
kind (str, default=’violin’) – Plot type: ‘violin’, ‘box’, ‘strip’, ‘swarm’, ‘bar’
figsize (tuple, optional) – Figure size (default from plot_config)
**kwargs (dict) – Additional plotting parameters passed to seaborn
- Returns:
fig, ax (matplotlib figure and axes)
- plot_metric_matrix(metric: str = 'harmsim_mean', cmap: str | None = None, figsize: Tuple[float, float] | None = None, **kwargs)[source]#
Plot metric as heatmap (for 3D data: trials × electrodes).
- Parameters:
metric (str, default=’harmsim_mean’) – Metric to plot
cmap (str, optional) – Colormap (default from plot_config)
figsize (tuple, optional) – Figure size (calculated dynamically if not provided)
**kwargs (dict) – Additional parameters for sns.heatmap
- Returns:
fig, ax (matplotlib figure and axes)
- plot_interval_histogram(scale_type: str = 'diss_scale', max_denom: int = 100, bins: int | None = None, figsize: Tuple[float, float] | None = None, show_common: bool = True, groupby: str | None = None)[source]#
Plot histogram of all intervals across time series to identify most recurrent intervals.
- Parameters:
scale_type (str, default=’diss_scale’) – Type of scale: ‘diss_scale’, ‘HE_scale’, ‘euler_fokker’, ‘harm_tuning_scale’
max_denom (int, default=100) – Maximum denominator for fraction simplification
bins (int, default=50) – Number of bins for histogram
figsize (tuple, default=(14, 6)) – Figure size
show_common (bool, default=True) – Annotate most common intervals
groupby (str, optional) – Metadata column to color by groups
- Returns:
fig, ax (matplotlib figure and axes)
Examples
>>> btg.compute_diss_curve() >>> fig, ax = btg.plot_interval_histogram('diss_scale') >>> plt.show()
- plot_scale_size_distribution(scale_type: str = 'diss_scale', figsize: Tuple[float, float] | None = None, groupby: str | None = None)[source]#
Plot distribution of scale sizes (number of notes) across time series.
- Parameters:
scale_type (str, default=’diss_scale’) – Type of scale to analyze
figsize (tuple, default=(12, 6)) – Figure size
groupby (str, optional) – Metadata column to group by
- Returns:
fig, ax (matplotlib figure and axes)
- plot_common_intervals(scale_type: str = 'diss_scale', top_n: int = 15, max_denom: int = 100, figsize: Tuple[float, float] | None = None, min_occurrence: int = 2)[source]#
Plot bar chart of most common intervals across all time series.
- Parameters:
scale_type (str, default=’diss_scale’) – Type of scale to analyze
top_n (int, default=15) – Number of top intervals to show
max_denom (int, default=100) – Maximum denominator for fractions
figsize (tuple, default=(12, 8)) – Figure size
min_occurrence (int, default=2) – Minimum number of occurrences to include
- Returns:
fig, ax (matplotlib figure and axes)
- plot_tuning_comparison(scale_type: str = 'diss_scale', indices: List[int] | None = None, max_denom: int = 100, figsize: Tuple[float, float] | None = None)[source]#
Compare tuning scales from multiple time series side by side.
- Parameters:
scale_type (str, default=’diss_scale’) – Type of scale to compare
indices (list of int, optional) – Which time series to compare (default: first 5)
max_denom (int, default=100) – Maximum denominator for fractions
figsize (tuple, default=(14, 8)) – Figure size
- Returns:
fig, ax (matplotlib figure and axes)
Overview#
BiotunerGroup is a powerful class for batch processing multiple time series with the Biotuner framework.
It extends the single-object compute_biotuner to handle multi-dimensional datasets with automatic
aggregation, group comparisons, and unified visualizations.
Key Features#
✅ Batch Processing - Run any biotuner method on multiple time series simultaneously ✅ Automatic Aggregation - Summary statistics computed across all series ✅ Metadata Support - Track experimental conditions, subjects, electrodes, etc. ✅ Group Comparisons - Statistical testing between conditions ✅ Unified Visualizations - Consistent, publication-ready plots ✅ Method Chaining - Clean, Pythonic API ✅ Memory Efficient - Option to store only summaries for large datasets ✅ Individual Access - Can still analyze individual biotuner objects
Quick Start#
Basic 2D Example#
import numpy as np
from biotuner import BiotunerGroup
# Your data: 10 trials × 5000 samples
data = np.random.randn(10, 5000)
# Create group object
btg = BiotunerGroup(
data=data,
sf=1000, # Sampling frequency
axis_labels=['trials']
)
# Run analysis pipeline (method chaining!)
btg.compute_peaks(peaks_function='FOOOF', min_freq=1, max_freq=50)
btg.compute_metrics(n_harm=10)
# Get summary statistics
summary = btg.summary()
print(summary.head())
3D Example with Metadata#
# Multiple electrodes across trials: 20 trials × 64 channels × 5000 samples
data = np.random.randn(20, 64, 5000)
metadata = {
'condition': ['rest']*10 + ['task']*10
}
btg = BiotunerGroup(
data,
sf=1000,
axis_labels=['trials', 'electrodes'],
metadata=metadata
)
# Compute peaks and metrics
btg.compute_peaks(peaks_function='EMD')
btg.compute_metrics()
# Compare groups
comparison = btg.compare_groups('condition', metrics=['harm_sim', 'tenney'])
print(comparison)
Main Methods#
Data Processing#
compute_peaks()- Extract spectral peaks from all seriescompute_extension()- Extend peaks to harmonic seriescompute_metrics()- Calculate harmonicity metricscompute_diss_curve()- Compute dissonance curvescompute_harmonic_entropy()- Calculate harmonic entropycompute_euler_fokker()- Compute Euler-Fokker genus metricscompute_harmonic_tuning()- Extract harmonic tuning information
Analysis & Comparison#
summary()- Get aggregated statistics across all seriestuning_summary()- Get musical tuning parametersget_tuning_scales()- Extract scale informationcompare_groups()- Statistical comparison between groupsget_attribute()- Access specific attributes from all objects
Visualization#
plot_peaks()- Visualize peaks across seriesplot_metric_comparison()- Compare metrics between groupsplot_euler_fokker()- Visualize Euler-Fokker genusplot_tuning_comparison()- Compare tuning across conditionsplot_harmonic_spectrum()- Display harmonic spectra
See Also#
Biotuner object - Single time series analysis
Harmonicity Metrics - Available harmonicity metrics
Peaks Extraction - Peak detection methods