mirror of
https://github.com/metabarcoding/obitools4.git
synced 2026-03-25 13:30:52 +00:00
Ajout de la fonctionnalité de sauvegarde et de chargement pour FrequencyFilter en utilisant le KmerSetGroup sous-jacent. - Nouvelle méthode Save() pour enregistrer le filtre dans un répertoire avec formatage des métadonnées - Nouvelle méthode LoadFrequencyFilter() pour charger un filtre depuis un répertoire - Initialisation des métadonnées lors de la création du filtre - Optimisation des méthodes Union() et Intersect() du KmerSetGroup - Mise à jour du commit hash
263 lines
6.8 KiB
Go
263 lines
6.8 KiB
Go
package obikmer
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obiseq"
|
|
)
|
|
|
|
// KmerSetGroup représente un vecteur de KmerSet
|
|
// Utilisé pour gérer plusieurs ensembles de k-mers (par exemple, par niveau de fréquence)
|
|
type KmerSetGroup struct {
|
|
id string // Identifiant unique du KmerSetGroup
|
|
k int // Taille des k-mers (immutable)
|
|
sets []*KmerSet // Vecteur de KmerSet
|
|
Metadata map[string]interface{} // Métadonnées du groupe (pas des sets individuels)
|
|
}
|
|
|
|
// NewKmerSetGroup crée un nouveau groupe de n KmerSets
|
|
func NewKmerSetGroup(k int, n int) *KmerSetGroup {
|
|
if n < 1 {
|
|
panic("KmerSetGroup size must be >= 1")
|
|
}
|
|
|
|
sets := make([]*KmerSet, n)
|
|
for i := range sets {
|
|
sets[i] = NewKmerSet(k)
|
|
}
|
|
|
|
return &KmerSetGroup{
|
|
k: k,
|
|
sets: sets,
|
|
Metadata: make(map[string]interface{}),
|
|
}
|
|
}
|
|
|
|
// K retourne la taille des k-mers (immutable)
|
|
func (ksg *KmerSetGroup) K() int {
|
|
return ksg.k
|
|
}
|
|
|
|
// Size retourne le nombre de KmerSet dans le groupe
|
|
func (ksg *KmerSetGroup) Size() int {
|
|
return len(ksg.sets)
|
|
}
|
|
|
|
// Get retourne le KmerSet à l'index donné
|
|
// Retourne nil si l'index est invalide
|
|
func (ksg *KmerSetGroup) Get(index int) *KmerSet {
|
|
if index < 0 || index >= len(ksg.sets) {
|
|
return nil
|
|
}
|
|
return ksg.sets[index]
|
|
}
|
|
|
|
// Set remplace le KmerSet à l'index donné
|
|
// Panique si l'index est invalide ou si le k ne correspond pas
|
|
func (ksg *KmerSetGroup) Set(index int, ks *KmerSet) {
|
|
if index < 0 || index >= len(ksg.sets) {
|
|
panic(fmt.Sprintf("Index out of bounds: %d (size: %d)", index, len(ksg.sets)))
|
|
}
|
|
if ks.k != ksg.k {
|
|
panic(fmt.Sprintf("KmerSet k mismatch: expected %d, got %d", ksg.k, ks.k))
|
|
}
|
|
ksg.sets[index] = ks
|
|
}
|
|
|
|
// Len retourne le nombre de k-mers dans un KmerSet spécifique
|
|
// Sans argument: retourne le nombre de k-mers dans le dernier KmerSet
|
|
// Avec argument index: retourne le nombre de k-mers dans le KmerSet à cet index
|
|
func (ksg *KmerSetGroup) Len(index ...int) uint64 {
|
|
if len(index) == 0 {
|
|
// Sans argument: dernier KmerSet
|
|
return ksg.sets[len(ksg.sets)-1].Len()
|
|
}
|
|
|
|
// Avec argument: KmerSet spécifique
|
|
idx := index[0]
|
|
if idx < 0 || idx >= len(ksg.sets) {
|
|
return 0
|
|
}
|
|
return ksg.sets[idx].Len()
|
|
}
|
|
|
|
// MemoryUsage retourne l'utilisation mémoire totale en bytes
|
|
func (ksg *KmerSetGroup) MemoryUsage() uint64 {
|
|
total := uint64(0)
|
|
for _, ks := range ksg.sets {
|
|
total += ks.MemoryUsage()
|
|
}
|
|
return total
|
|
}
|
|
|
|
// Clear vide tous les KmerSet du groupe
|
|
func (ksg *KmerSetGroup) Clear() {
|
|
for _, ks := range ksg.sets {
|
|
ks.Clear()
|
|
}
|
|
}
|
|
|
|
// Copy crée une copie complète du groupe (cohérent avec BioSequence.Copy)
|
|
func (ksg *KmerSetGroup) Copy() *KmerSetGroup {
|
|
copiedSets := make([]*KmerSet, len(ksg.sets))
|
|
for i, ks := range ksg.sets {
|
|
copiedSets[i] = ks.Copy() // Copy chaque KmerSet avec ses métadonnées
|
|
}
|
|
|
|
// Copier les métadonnées du groupe
|
|
groupMetadata := make(map[string]interface{}, len(ksg.Metadata))
|
|
for k, v := range ksg.Metadata {
|
|
groupMetadata[k] = v
|
|
}
|
|
|
|
return &KmerSetGroup{
|
|
id: ksg.id,
|
|
k: ksg.k,
|
|
sets: copiedSets,
|
|
Metadata: groupMetadata,
|
|
}
|
|
}
|
|
|
|
// Id retourne l'identifiant du KmerSetGroup (cohérent avec BioSequence.Id)
|
|
func (ksg *KmerSetGroup) Id() string {
|
|
return ksg.id
|
|
}
|
|
|
|
// SetId définit l'identifiant du KmerSetGroup (cohérent avec BioSequence.SetId)
|
|
func (ksg *KmerSetGroup) SetId(id string) {
|
|
ksg.id = id
|
|
}
|
|
|
|
// AddSequence ajoute tous les k-mers d'une séquence à un KmerSet spécifique
|
|
func (ksg *KmerSetGroup) AddSequence(seq *obiseq.BioSequence, index int) {
|
|
if index < 0 || index >= len(ksg.sets) {
|
|
panic(fmt.Sprintf("Index out of bounds: %d (size: %d)", index, len(ksg.sets)))
|
|
}
|
|
ksg.sets[index].AddSequence(seq)
|
|
}
|
|
|
|
// AddSequences ajoute tous les k-mers de plusieurs séquences à un KmerSet spécifique
|
|
func (ksg *KmerSetGroup) AddSequences(sequences *obiseq.BioSequenceSlice, index int) {
|
|
if index < 0 || index >= len(ksg.sets) {
|
|
panic(fmt.Sprintf("Index out of bounds: %d (size: %d)", index, len(ksg.sets)))
|
|
}
|
|
ksg.sets[index].AddSequences(sequences)
|
|
}
|
|
|
|
// Union retourne l'union de tous les KmerSet du groupe
|
|
// Optimisation: part du plus grand ensemble pour minimiser les opérations
|
|
func (ksg *KmerSetGroup) Union() *KmerSet {
|
|
if len(ksg.sets) == 0 {
|
|
return NewKmerSet(ksg.k)
|
|
}
|
|
|
|
if len(ksg.sets) == 1 {
|
|
return ksg.sets[0].Copy()
|
|
}
|
|
|
|
// Trouver l'index du plus grand ensemble (celui avec le plus de k-mers)
|
|
maxIdx := 0
|
|
maxCard := ksg.sets[0].Len()
|
|
for i := 1; i < len(ksg.sets); i++ {
|
|
card := ksg.sets[i].Len()
|
|
if card > maxCard {
|
|
maxCard = card
|
|
maxIdx = i
|
|
}
|
|
}
|
|
|
|
// Copier le plus grand ensemble et faire les unions in-place
|
|
result := ksg.sets[maxIdx].bitmap.Clone()
|
|
for i := 0; i < len(ksg.sets); i++ {
|
|
if i != maxIdx {
|
|
result.Or(ksg.sets[i].bitmap)
|
|
}
|
|
}
|
|
|
|
return NewKmerSetFromBitmap(ksg.k, result)
|
|
}
|
|
|
|
// Intersect retourne l'intersection de tous les KmerSet du groupe
|
|
// Optimisation: part du plus petit ensemble pour minimiser les opérations
|
|
func (ksg *KmerSetGroup) Intersect() *KmerSet {
|
|
if len(ksg.sets) == 0 {
|
|
return NewKmerSet(ksg.k)
|
|
}
|
|
|
|
if len(ksg.sets) == 1 {
|
|
return ksg.sets[0].Copy()
|
|
}
|
|
|
|
// Trouver l'index du plus petit ensemble (celui avec le moins de k-mers)
|
|
minIdx := 0
|
|
minCard := ksg.sets[0].Len()
|
|
for i := 1; i < len(ksg.sets); i++ {
|
|
card := ksg.sets[i].Len()
|
|
if card < minCard {
|
|
minCard = card
|
|
minIdx = i
|
|
}
|
|
}
|
|
|
|
// Copier le plus petit ensemble et faire les intersections in-place
|
|
result := ksg.sets[minIdx].bitmap.Clone()
|
|
for i := 0; i < len(ksg.sets); i++ {
|
|
if i != minIdx {
|
|
result.And(ksg.sets[i].bitmap)
|
|
}
|
|
}
|
|
|
|
return NewKmerSetFromBitmap(ksg.k, result)
|
|
}
|
|
|
|
// Stats retourne des statistiques pour chaque KmerSet du groupe
|
|
type KmerSetGroupStats struct {
|
|
K int
|
|
Size int // Nombre de KmerSet
|
|
TotalBytes uint64 // Mémoire totale utilisée
|
|
Sets []KmerSetStats // Stats de chaque KmerSet
|
|
}
|
|
|
|
type KmerSetStats struct {
|
|
Index int // Index du KmerSet dans le groupe
|
|
Len uint64 // Nombre de k-mers
|
|
SizeBytes uint64 // Taille en bytes
|
|
}
|
|
|
|
func (ksg *KmerSetGroup) Stats() KmerSetGroupStats {
|
|
stats := KmerSetGroupStats{
|
|
K: ksg.k,
|
|
Size: len(ksg.sets),
|
|
Sets: make([]KmerSetStats, len(ksg.sets)),
|
|
}
|
|
|
|
for i, ks := range ksg.sets {
|
|
sizeBytes := ks.MemoryUsage()
|
|
stats.Sets[i] = KmerSetStats{
|
|
Index: i,
|
|
Len: ks.Len(),
|
|
SizeBytes: sizeBytes,
|
|
}
|
|
stats.TotalBytes += sizeBytes
|
|
}
|
|
|
|
return stats
|
|
}
|
|
|
|
func (ksgs KmerSetGroupStats) String() string {
|
|
result := fmt.Sprintf(`KmerSetGroup Statistics (k=%d, size=%d):
|
|
Total memory: %.2f MB
|
|
|
|
Set breakdown:
|
|
`, ksgs.K, ksgs.Size, float64(ksgs.TotalBytes)/1024/1024)
|
|
|
|
for _, set := range ksgs.Sets {
|
|
result += fmt.Sprintf(" Set[%d]: %d k-mers (%.2f MB)\n",
|
|
set.Index,
|
|
set.Len,
|
|
float64(set.SizeBytes)/1024/1024)
|
|
}
|
|
|
|
return result
|
|
}
|