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 -1 holes.
PCA scatter colored by cluster
PCA-2 projection of the units, colored by cluster.

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)
Cluster 00 — exemplar 1
Cluster 00 — exemplar 2
Two members of the same cluster should sound similar.
mean spectrogram per cluster
Mean log-mel spectrogram per cluster — a visual catalogue of the discovered acoustic types.

Next

Build a symbol stream →