Tutorial 3 — Cluster units into types
Featurise each unit, reduce dimensionality, cluster with HDBSCAN, and listen to the cluster exemplars.
Featurise
The simplest hand-rolled feature is a flattened mel-patch. mel_patch_feature returns a fixed-length vector per unit (default: MEL_DIM ≈ flattened
log-mel + auxiliary scalars).
import numpy as np
from decipher import mel_patch_feature
X = np.stack([
mel_patch_feature(u.source_samples, u.sr)
for u in units
])
print(X.shape) # (n_units, MEL_DIM)
For frequency ranges that differ from the default species (humpback, default
parameters), use auto_mel_params to pick frequency-adaptive FFT
parameters first.
Cluster
from decipher import cluster_features
result = cluster_features(
X,
min_cluster_size=10,
min_samples=3,
n_pca_dims=20,
cluster_selection_epsilon=0.3,
cluster_selection_method="eom",
)
print(result.n_clusters, "clusters")
print(result.labels.shape, result.labels_assigned.shape)
Two label arrays come back:
labels— raw HDBSCAN output.-1= noise.labels_assigned— noise rows reassigned to the nearest centroid in PCA space. Use this for downstream symbol streams; it has no-1holes.
Listen to the clusters
Group units by cluster, then write a few exemplars per cluster:
from collections import defaultdict
from decipher import write_wav
by_cluster = defaultdict(list)
for u, k in zip(units, result.labels_assigned):
by_cluster[int(k)].append(u)
for k, group in by_cluster.items():
for i, u in enumerate(group[:3]): # first 3 exemplars
write_wav(f"audio_samples/03_clusters/cluster_{k:02d}/ex{i:02d}.wav",
u.source_samples, u.sr)