clustering¶
Sequence groups and representative sequences are obtained by clustering sequences based on sequence identity, k-mer, fingerprint similarity, or DNA base model rep.
1) Identity clustering¶
- Purpose: Calculate the pairwise similarity of sequences and group them according to the connected components of the threshold graph, and return cluster labels, members and representative records, which are used to organize similar sequences and observe data structures.
-
Method: Compute pairwise identity, connect records meeting the threshold and take graph components as clusters.
-
API:
dnakit.datasets.cluster_sequences(records[required], config[optional]);configusesdnakit.datasets.ClusterConfig, this item is set tomethod="identity". -
Input: Required record set; optional identity threshold, representation policy, and comparison resource limit.
-
Output: Returns
ClusteringResultwithclusters,labels, and representative records. -
Example code:
from dnakit import DNARecord, DNASequence
from dnakit.datasets import ClusterConfig, cluster_sequences
records = [
DNARecord(DNASequence("AAAA"), "a"),
DNARecord(DNASequence("AAAT"), "b"),
DNARecord(DNASequence("CCCC"), "c"),
]
result = cluster_sequences(
records, config=ClusterConfig(method="identity", threshold=0.7)
)
print(result.labels)
- Example output:
2) k-mer clustering¶
- Purpose: Establish groups based on k-mer Jaccard, Containment or Cosine similarity between sequences and return cluster members and labels, suitable for constitutive clustering without base-level alignment.
-
Method: Compute k-mer similarities, build a threshold graph and group its connected components.
-
API:
dnakit.datasets.cluster_sequences(records[required], config[optional]);configusesdnakit.datasets.ClusterConfig, this item is set tomethod="kmer". -
Input: Required record set; optional k, canonical, threshold, representative strategy and pairwise comparison upper limit.
-
Output: Returns
ClusteringResultwithclusters,labels, and representative records. -
Example code:
from dnakit import DNARecord, DNASequence
from dnakit.datasets import ClusterConfig, cluster_sequences
records = [
DNARecord(DNASequence("AAAA"), "a"),
DNARecord(DNASequence("AAAT"), "b"),
DNARecord(DNASequence("CCCC"), "c"),
]
result = cluster_sequences(
records, config=ClusterConfig(method="kmer", threshold=0.5, k=2)
)
print(result.labels)
- Example output:
3) Fingerprint clustering¶
- Purpose: Group sequences using Tanimoto/Jaccard similarity of fixed schema DNA fingerprints, returning cluster labels and members for clustering by local patterns or Panel features.
-
Method: Compare compatible fingerprints, build a threshold graph and group connected components.
-
API:
dnakit.datasets.cluster_sequences(records[required], config[optional]);configusesdnakit.datasets.ClusterConfig, this item is set tomethod="fingerprint". -
Input: Required set of records; optional thresholds, representative policies, and resource caps.
-
Output: Returns
ClusteringResultwithclusters,labels, and representative records. -
Example code:
from dnakit import DNARecord, DNASequence
from dnakit.datasets import ClusterConfig, cluster_sequences
records = [
DNARecord(DNASequence("AAAA"), "a"),
DNARecord(DNASequence("AAAT"), "b"),
DNARecord(DNASequence("CCCC"), "c"),
]
result = cluster_sequences(
records, config=ClusterConfig(method="fingerprint", threshold=0.5)
)
print(result.labels, result.method)
- Example output:
4) Hierarchical clustering¶
- Purpose: Gradually merge the nearest clusters based on the pre-calculated sequence distance, and return linkage, hierarchical relationships and segmentation labels, which are used to draw dendrograms and observe groupings at different distance scales.
-
Method: Start from singleton clusters and iteratively merge the closest pair using single/complete/average linkage.
-
API:
dnakit.datasets.hierarchical_cluster(records[required], config[optional]);configusesdnakit.datasets.HierarchicalClusteringConfig. -
Input: Required record collection; optional identity/edit/k-mer/fingerprint methods and single/complete/average linkage.
-
Output: Returns a
HierarchicalClusteringResult; the example showslinkage. -
Example code:
from dnakit import DNARecord, DNASequence
from dnakit.datasets import HierarchicalClusteringConfig, hierarchical_cluster
records = [
DNARecord(DNASequence("AAAA"), "a"),
DNARecord(DNASequence("AAAT"), "b"),
DNARecord(DNASequence("CCCC"), "c"),
]
result = hierarchical_cluster(
records, config=HierarchicalClusteringConfig(linkage="average")
)
print(len(result.linkage), result.linkage[-1].member_count)
- Example output:
5) Representative sequence selection¶
- Purpose: Select a representative sequence from each cluster according to clear strategies such as first, longest, medoid, etc., and retain member mapping for compressed data sets and subsequent manual inspection.
-
Method: Group records by labels and select one representative using order, length or medoid distances.
-
API:
dnakit.datasets.select_representatives(records[required], labels[required], policy[optional], medoid_method[optional], k[optional], canonical[optional], max_records[optional], max_pairwise_comparisons[optional], max_alignment_cells[optional]). -
Input: Required records and equal-length labels; optional selection strategy, medoid method, and resource cap.
-
Output: Returns a
RepresentativeSelectionResult; the example showsrepresentative_ids. -
Example code:
from dnakit import DNARecord, DNASequence
from dnakit.datasets import select_representatives
records = [
DNARecord(DNASequence("AAAA"), "long"),
DNARecord(DNASequence("AA"), "short"),
DNARecord(DNASequence("CCCC"), "other"),
]
result = select_representatives(records, [7, 7, 2], policy="shortest")
print(result.representative_ids)
- Example output:
6) Neural Network Clustering¶
- Purpose: Extract a fixed-length rep for each sequence using the selected DNA base model, then perform normalization, optional PCA, and k-means with a fixed seed on the vector, returning the label, center, score, and center nearest representative sequence for grouping by model representation.
- Method: Pool model vectors, apply L2 normalization and optional PCA, then run seeded k-means++/Lloyd clustering. Select the record nearest each center as its representative.
- API:
dnakit.datasets.neural_cluster_sequences(records, config=NeuralClusteringConfig(...)). - Input: Nonempty DNA records without explicit gaps, the cluster count, seed and model configuration. Default LucaOne requires its dependencies and
allow_remote_code=True; IUPAC symbols map toNby default. - Output: Returns a
NeuralClusteringResult; the example showslabels,representatives,checkpoint_path. - Example code:
from dnakit import DNARecord, DNASequence
from dnakit.datasets import NeuralClusteringConfig, neural_cluster_sequences
from dnakit.representations import RepresentationConfig
records = [
DNARecord(DNASequence("AAAAAA"), "a"),
DNARecord(DNASequence("AAAAAT"), "b"),
DNARecord(DNASequence("CCCCCC"), "c"),
DNARecord(DNASequence("CCCCCG"), "d"),
]
result = neural_cluster_sequences(
records,
config=NeuralClusteringConfig(
representation=RepresentationConfig(
batch_size=2,
allow_remote_code=True,
),
n_clusters=2,
seed=7,
),
)
print(result.labels)
print(result.representatives.ids)
print(result.checkpoint_path)
- Example output: Four cluster labels, two representative IDs and the checkpoint path. The exact labels depend on the model and environment; model inference was not run here.
7) Cluster samples¶
- Purpose: Group similar samples by their features.
- Method: Apply the selected clustering method; MCL iterates expansion/inflation on a nonnegative affinity matrix.
- API:
dnakit.bioinformatics.features.cluster_features(values, method='kmeans', seed=0, parameters=None) - Input: A numeric feature matrix; scale it beforehand. MCL requires symmetric nonnegative affinity; requires
bio-analysis; see available methods. - Output: Returns a clustering model;
labels_gives the cluster label for each sample. - Example code:
from dnakit.bioinformatics.features import cluster_features
model = cluster_features([[0, 0], [0, 1], [9, 9], [9, 10]],
parameters={"n_clusters": 2}, seed=0)
print(model.labels_[0] == model.labels_[1])
print(model.labels_[0] != model.labels_[2])
- Example output:
Dependencies and details: FAQ.
8) Label-stratified diversity sampling¶
dnakit.datasets.diversity_sample selects a fixed number of original candidate records.
It defaults to frozen DNABERT2 mean embeddings, L2 normalization, and seeded
k-means++/Lloyd within each class. The model runs inference only; no training is performed.
The diversity argument selects high or low diversity:
| Argument | Cluster count | Selection |
|---|---|---|
diversity="high" (default) |
n_samples in total, allocated proportionally |
One nearest actual record per centroid |
diversity="low" |
Five per class with a nonzero sample quota by default | The tightest fixed-size neighborhood among the centers |
n_samples always sets the final sample size; it equals the total cluster count
only in high mode. low_n_clusters sets the per-class cluster count for low mode.
Explicit n_samples, diversity, and low_n_clusters override the corresponding
configuration values without modifying the configuration object. Omitted arguments
use the configuration values, defaulting to 400, "high", and 5 respectively.
Class quotas follow the candidate proportions using the largest-remainder method: floor each proportional quota, then assign remaining slots in descending remainder order. For 840 candidates with class counts 173 and 667, selecting 400 allocates 82 and 318 records respectively. High mode fits 82 and 318 clusters; low mode fits five per class, then selects 82 and 318 records. Quota and distance ties follow input order.
Install model dependencies with python -m pip install "dnakit[neural]".
This example expects sequence and label columns in candidates.csv:
from dnakit.datasets import DiversitySamplingConfig, diversity_sample
from dnakit.representations import RepresentationConfig
result = diversity_sample(
"candidates.csv",
n_samples=400,
diversity="high",
output_path="diversity_400",
config=DiversitySamplingConfig(
label_key="label",
seed=42,
representation=RepresentationConfig(
model="dnabert2",
pooling="mean",
allow_remote_code=True, # Load model code from the official checkpoint
# checkpoint_path="/path/to/dnabert2-117m", # Reuse local weights
),
),
)
print(len(result.records))
for allocation in result.allocations:
print(allocation.label, allocation.input_count, allocation.n_samples)
To select a low-diversity subset:
low_result = diversity_sample(
"candidates.csv",
n_samples=400,
diversity="low",
low_n_clusters=5,
output_path="low_diversity_400",
config=DiversitySamplingConfig(
seed=42,
representation=RepresentationConfig(model="dnabert2", allow_remote_code=True),
),
)
Low mode clusters each class separately. Each center proposes the class quota of nearest neighbors from all candidates in that class, yielding up to five proposals. Score each proposal by its mean Euclidean distance to its own mean embedding and choose the smallest score, breaking score ties by sorted original row indices. Neighborhoods may cross cluster boundaries, so a small cluster does not cause an underfilled sample. This is a compactness heuristic, not a global optimum.
File input produces diversity_400/diversity_sample.csv and diversity_400/result.json.
The CSV retains original columns and selected rows in input order; the JSON records
allocations, source indices, cluster membership, centers, and model provenance.
Alternatively pass Iterable[DNARecord] with labels in record.metadata["label"].
result.records contains the original records, preserving sequences, labels, annotations,
and metadata.
selected_indicesuses zero-based positions in the input;cluster_labelsmaps each input record to its cluster. In low mode, userecordsandselected_indicesfor the selected subset; cluster representatives alone do not constitute the sample. Results recorddiversity,low_n_clusters, andsampling_strategy; inspectlen(result.clusters)for the actual total cluster count.label_key=Nonesamples one unlabeled pool. Otherwise every record requires a non-null string, numeric, or boolean label. Types are distinct:1and"1"are separate classes.- Very small classes can receive zero slots; their
cluster_labelsentries areNone. - Set
normalize=Falseto cluster raw embeddings using Euclidean distance. No PCA is performed. Other DNAKit models can be selected withRepresentationConfig(model=...). n_samplesmust be positive and cannot exceed the input count. High mode rejects insufficient distinct embeddings. Low mode caps each class's cluster count at its number of distinct embeddings and permits distinct rows with identical embeddings. Neither mode repeats an input row. Empty fitted clusters raise an error. Both sampling and representationmax_recordslimits apply, defaulting to 10,000.- This method targets representative or compact subsets in model feature space. It does not guarantee maximum or minimum pairwise sequence distance or deduplicate across labels. Reproducibility also depends on input order, model weights, library versions, and the compute environment.
Tests cover both modes with synthetic 840-to-400 embeddings, 82/318 quotas, compact region selection, duplicate embeddings, and CSV export. They do not establish biological diversity gains or constitute DNABERT2 inference on a user's candidate pool. References: official DNABERT-2 implementation and scikit-learn KMeans.