mirror of
https://github.com/metabarcoding/obitools4.git
synced 2026-03-25 13:30:52 +00:00
Refactor KmerSet and FrequencyFilter to use immutable K parameter and consistent Copy/Clone methods
This commit refactors the KmerSet and related structures to use an immutable K parameter and introduces consistent Copy methods instead of Clone. It also adds attribute API support for KmerSet and KmerSetGroup, and updates persistence logic to handle IDs and metadata correctly.
This commit is contained in:
@@ -26,7 +26,7 @@ func NewFrequencyFilter(k, minFreq int) *FrequencyFilter {
|
||||
// Utilise un itérateur pour éviter l'allocation d'un vecteur intermédiaire
|
||||
func (ff *FrequencyFilter) AddSequence(seq *obiseq.BioSequence) {
|
||||
rawSeq := seq.Sequence()
|
||||
for canonical := range IterNormalizedKmers(rawSeq, ff.K) {
|
||||
for canonical := range IterNormalizedKmers(rawSeq, ff.K()) {
|
||||
ff.addKmer(canonical)
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ func (ff *FrequencyFilter) addKmer(kmer uint64) {
|
||||
// GetFilteredSet retourne un KmerSet des k-mers avec fréquence ≥ minFreq
|
||||
func (ff *FrequencyFilter) GetFilteredSet() *KmerSet {
|
||||
// Les k-mers filtrés sont dans le dernier niveau
|
||||
return ff.Get(ff.MinFreq - 1).Clone()
|
||||
return ff.Get(ff.MinFreq - 1).Copy()
|
||||
}
|
||||
|
||||
// GetKmersAtLevel retourne un KmerSet des k-mers vus au moins (level+1) fois
|
||||
@@ -56,9 +56,9 @@ func (ff *FrequencyFilter) GetFilteredSet() *KmerSet {
|
||||
func (ff *FrequencyFilter) GetKmersAtLevel(level int) *KmerSet {
|
||||
ks := ff.Get(level)
|
||||
if ks == nil {
|
||||
return NewKmerSet(ff.K)
|
||||
return NewKmerSet(ff.K())
|
||||
}
|
||||
return ks.Clone()
|
||||
return ks.Copy()
|
||||
}
|
||||
|
||||
// Stats retourne des statistiques sur les niveaux de fréquence
|
||||
@@ -161,14 +161,14 @@ func (ff *FrequencyFilter) Load(path string) error {
|
||||
|
||||
// Contains vérifie si un k-mer a atteint la fréquence minimale
|
||||
func (ff *FrequencyFilter) Contains(kmer uint64) bool {
|
||||
canonical := NormalizeKmer(kmer, ff.K)
|
||||
canonical := NormalizeKmer(kmer, ff.K())
|
||||
return ff.Get(ff.MinFreq - 1).Contains(canonical)
|
||||
}
|
||||
|
||||
// GetFrequency retourne la fréquence approximative d'un k-mer
|
||||
// Retourne le niveau maximum atteint (freq ≥ niveau)
|
||||
func (ff *FrequencyFilter) GetFrequency(kmer uint64) int {
|
||||
canonical := NormalizeKmer(kmer, ff.K)
|
||||
canonical := NormalizeKmer(kmer, ff.K())
|
||||
|
||||
freq := 0
|
||||
for i := 0; i < ff.MinFreq; i++ {
|
||||
|
||||
@@ -10,7 +10,8 @@ import (
|
||||
// KmerSet encapsule un ensemble de k-mers stockés dans un Roaring Bitmap
|
||||
// Fournit des méthodes utilitaires pour manipuler des ensembles de k-mers
|
||||
type KmerSet struct {
|
||||
K int // Taille des k-mers
|
||||
id string // Identifiant unique du KmerSet
|
||||
k int // Taille des k-mers (immutable)
|
||||
bitmap *roaring64.Bitmap // Bitmap contenant les k-mers
|
||||
Metadata map[string]interface{} // Métadonnées utilisateur (clé=valeur atomique)
|
||||
}
|
||||
@@ -18,7 +19,7 @@ type KmerSet struct {
|
||||
// NewKmerSet crée un nouveau KmerSet vide
|
||||
func NewKmerSet(k int) *KmerSet {
|
||||
return &KmerSet{
|
||||
K: k,
|
||||
k: k,
|
||||
bitmap: roaring64.New(),
|
||||
Metadata: make(map[string]interface{}),
|
||||
}
|
||||
@@ -27,12 +28,17 @@ func NewKmerSet(k int) *KmerSet {
|
||||
// NewKmerSetFromBitmap crée un KmerSet à partir d'un bitmap existant
|
||||
func NewKmerSetFromBitmap(k int, bitmap *roaring64.Bitmap) *KmerSet {
|
||||
return &KmerSet{
|
||||
K: k,
|
||||
k: k,
|
||||
bitmap: bitmap,
|
||||
Metadata: make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
// K retourne la taille des k-mers (immutable)
|
||||
func (ks *KmerSet) K() int {
|
||||
return ks.k
|
||||
}
|
||||
|
||||
// Add ajoute un k-mer à l'ensemble
|
||||
func (ks *KmerSet) Add(kmer uint64) {
|
||||
ks.bitmap.Add(kmer)
|
||||
@@ -42,7 +48,7 @@ func (ks *KmerSet) Add(kmer uint64) {
|
||||
// Utilise un itérateur pour éviter l'allocation d'un vecteur intermédiaire
|
||||
func (ks *KmerSet) AddSequence(seq *obiseq.BioSequence) {
|
||||
rawSeq := seq.Sequence()
|
||||
for canonical := range IterNormalizedKmers(rawSeq, ks.K) {
|
||||
for canonical := range IterNormalizedKmers(rawSeq, ks.k) {
|
||||
ks.bitmap.Add(canonical)
|
||||
}
|
||||
}
|
||||
@@ -74,8 +80,8 @@ func (ks *KmerSet) Clear() {
|
||||
ks.bitmap.Clear()
|
||||
}
|
||||
|
||||
// Clone crée une copie de l'ensemble
|
||||
func (ks *KmerSet) Clone() *KmerSet {
|
||||
// Copy crée une copie de l'ensemble (cohérent avec BioSequence.Copy)
|
||||
func (ks *KmerSet) Copy() *KmerSet {
|
||||
// Copier les métadonnées
|
||||
metadata := make(map[string]interface{}, len(ks.Metadata))
|
||||
for k, v := range ks.Metadata {
|
||||
@@ -83,40 +89,51 @@ func (ks *KmerSet) Clone() *KmerSet {
|
||||
}
|
||||
|
||||
return &KmerSet{
|
||||
K: ks.K,
|
||||
id: ks.id,
|
||||
k: ks.k,
|
||||
bitmap: ks.bitmap.Clone(),
|
||||
Metadata: metadata,
|
||||
}
|
||||
}
|
||||
|
||||
// Id retourne l'identifiant du KmerSet (cohérent avec BioSequence.Id)
|
||||
func (ks *KmerSet) Id() string {
|
||||
return ks.id
|
||||
}
|
||||
|
||||
// SetId définit l'identifiant du KmerSet (cohérent avec BioSequence.SetId)
|
||||
func (ks *KmerSet) SetId(id string) {
|
||||
ks.id = id
|
||||
}
|
||||
|
||||
// Union retourne l'union de cet ensemble avec un autre
|
||||
func (ks *KmerSet) Union(other *KmerSet) *KmerSet {
|
||||
if ks.K != other.K {
|
||||
panic(fmt.Sprintf("Cannot union KmerSets with different k values: %d vs %d", ks.K, other.K))
|
||||
if ks.k != other.k {
|
||||
panic(fmt.Sprintf("Cannot union KmerSets with different k values: %d vs %d", ks.k, other.k))
|
||||
}
|
||||
result := ks.bitmap.Clone()
|
||||
result.Or(other.bitmap)
|
||||
return NewKmerSetFromBitmap(ks.K, result)
|
||||
return NewKmerSetFromBitmap(ks.k, result)
|
||||
}
|
||||
|
||||
// Intersect retourne l'intersection de cet ensemble avec un autre
|
||||
func (ks *KmerSet) Intersect(other *KmerSet) *KmerSet {
|
||||
if ks.K != other.K {
|
||||
panic(fmt.Sprintf("Cannot intersect KmerSets with different k values: %d vs %d", ks.K, other.K))
|
||||
if ks.k != other.k {
|
||||
panic(fmt.Sprintf("Cannot intersect KmerSets with different k values: %d vs %d", ks.k, other.k))
|
||||
}
|
||||
result := ks.bitmap.Clone()
|
||||
result.And(other.bitmap)
|
||||
return NewKmerSetFromBitmap(ks.K, result)
|
||||
return NewKmerSetFromBitmap(ks.k, result)
|
||||
}
|
||||
|
||||
// Difference retourne la différence de cet ensemble avec un autre (this - other)
|
||||
func (ks *KmerSet) Difference(other *KmerSet) *KmerSet {
|
||||
if ks.K != other.K {
|
||||
panic(fmt.Sprintf("Cannot subtract KmerSets with different k values: %d vs %d", ks.K, other.K))
|
||||
if ks.k != other.k {
|
||||
panic(fmt.Sprintf("Cannot subtract KmerSets with different k values: %d vs %d", ks.k, other.k))
|
||||
}
|
||||
result := ks.bitmap.Clone()
|
||||
result.AndNot(other.bitmap)
|
||||
return NewKmerSetFromBitmap(ks.K, result)
|
||||
return NewKmerSetFromBitmap(ks.k, result)
|
||||
}
|
||||
|
||||
// Iterator retourne un itérateur sur tous les k-mers de l'ensemble
|
||||
|
||||
362
pkg/obikmer/kmer_set_attributes.go
Normal file
362
pkg/obikmer/kmer_set_attributes.go
Normal file
@@ -0,0 +1,362 @@
|
||||
package obikmer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obiutils"
|
||||
)
|
||||
|
||||
// ==================================
|
||||
// KMER SET ATTRIBUTE API
|
||||
// Mimic BioSequence attribute API from obiseq/attributes.go
|
||||
// ==================================
|
||||
|
||||
// HasAttribute vérifie si une clé d'attribut existe
|
||||
func (ks *KmerSet) HasAttribute(key string) bool {
|
||||
_, ok := ks.Metadata[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
// GetAttribute récupère la valeur d'un attribut
|
||||
// Cas particuliers: "id" utilise Id(), "k" utilise K()
|
||||
func (ks *KmerSet) GetAttribute(key string) (interface{}, bool) {
|
||||
switch key {
|
||||
case "id":
|
||||
return ks.Id(), true
|
||||
case "k":
|
||||
return ks.K(), true
|
||||
default:
|
||||
value, ok := ks.Metadata[key]
|
||||
return value, ok
|
||||
}
|
||||
}
|
||||
|
||||
// SetAttribute définit la valeur d'un attribut
|
||||
// Cas particuliers: "id" utilise SetId(), "k" est immutable (panique)
|
||||
func (ks *KmerSet) SetAttribute(key string, value interface{}) {
|
||||
switch key {
|
||||
case "id":
|
||||
if id, ok := value.(string); ok {
|
||||
ks.SetId(id)
|
||||
} else {
|
||||
panic(fmt.Sprintf("id must be a string, got %T", value))
|
||||
}
|
||||
case "k":
|
||||
panic("k is immutable and cannot be modified via SetAttribute")
|
||||
default:
|
||||
ks.Metadata[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteAttribute supprime un attribut
|
||||
func (ks *KmerSet) DeleteAttribute(key string) {
|
||||
delete(ks.Metadata, key)
|
||||
}
|
||||
|
||||
// RemoveAttribute supprime un attribut (alias de DeleteAttribute)
|
||||
func (ks *KmerSet) RemoveAttribute(key string) {
|
||||
ks.DeleteAttribute(key)
|
||||
}
|
||||
|
||||
// RenameAttribute renomme un attribut
|
||||
func (ks *KmerSet) RenameAttribute(newName, oldName string) {
|
||||
if value, ok := ks.Metadata[oldName]; ok {
|
||||
ks.Metadata[newName] = value
|
||||
delete(ks.Metadata, oldName)
|
||||
}
|
||||
}
|
||||
|
||||
// GetIntAttribute récupère un attribut en tant qu'entier
|
||||
func (ks *KmerSet) GetIntAttribute(key string) (int, bool) {
|
||||
value, ok := ks.Metadata[key]
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
switch v := value.(type) {
|
||||
case int:
|
||||
return v, true
|
||||
case int64:
|
||||
return int(v), true
|
||||
case float64:
|
||||
return int(v), true
|
||||
case string:
|
||||
if i, err := strconv.Atoi(v); err == nil {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// GetFloatAttribute récupère un attribut en tant que float64
|
||||
func (ks *KmerSet) GetFloatAttribute(key string) (float64, bool) {
|
||||
value, ok := ks.Metadata[key]
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
switch v := value.(type) {
|
||||
case float64:
|
||||
return v, true
|
||||
case float32:
|
||||
return float64(v), true
|
||||
case int:
|
||||
return float64(v), true
|
||||
case int64:
|
||||
return float64(v), true
|
||||
case string:
|
||||
if f, err := strconv.ParseFloat(v, 64); err == nil {
|
||||
return f, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// GetNumericAttribute récupère un attribut numérique (alias de GetFloatAttribute)
|
||||
func (ks *KmerSet) GetNumericAttribute(key string) (float64, bool) {
|
||||
return ks.GetFloatAttribute(key)
|
||||
}
|
||||
|
||||
// GetStringAttribute récupère un attribut en tant que chaîne
|
||||
func (ks *KmerSet) GetStringAttribute(key string) (string, bool) {
|
||||
value, ok := ks.Metadata[key]
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
return v, true
|
||||
default:
|
||||
return fmt.Sprintf("%v", v), true
|
||||
}
|
||||
}
|
||||
|
||||
// GetBoolAttribute récupère un attribut en tant que booléen
|
||||
func (ks *KmerSet) GetBoolAttribute(key string) (bool, bool) {
|
||||
value, ok := ks.Metadata[key]
|
||||
if !ok {
|
||||
return false, false
|
||||
}
|
||||
|
||||
switch v := value.(type) {
|
||||
case bool:
|
||||
return v, true
|
||||
case int:
|
||||
return v != 0, true
|
||||
case string:
|
||||
if b, err := strconv.ParseBool(v); err == nil {
|
||||
return b, true
|
||||
}
|
||||
}
|
||||
return false, false
|
||||
}
|
||||
|
||||
// AttributeKeys retourne l'ensemble des clés d'attributs
|
||||
func (ks *KmerSet) AttributeKeys() obiutils.Set[string] {
|
||||
keys := obiutils.MakeSet[string]()
|
||||
for key := range ks.Metadata {
|
||||
keys.Add(key)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// Keys retourne l'ensemble des clés d'attributs (alias de AttributeKeys)
|
||||
func (ks *KmerSet) Keys() obiutils.Set[string] {
|
||||
return ks.AttributeKeys()
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// KMER SET GROUP ATTRIBUTE API
|
||||
// Métadonnées du groupe + accès via Get() pour les sets individuels
|
||||
// ==================================
|
||||
|
||||
// HasAttribute vérifie si une clé d'attribut existe pour le groupe
|
||||
func (ksg *KmerSetGroup) HasAttribute(key string) bool {
|
||||
_, ok := ksg.Metadata[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
// GetAttribute récupère la valeur d'un attribut du groupe
|
||||
// Cas particuliers: "id" utilise Id(), "k" utilise K()
|
||||
func (ksg *KmerSetGroup) GetAttribute(key string) (interface{}, bool) {
|
||||
switch key {
|
||||
case "id":
|
||||
return ksg.Id(), true
|
||||
case "k":
|
||||
return ksg.K(), true
|
||||
default:
|
||||
value, ok := ksg.Metadata[key]
|
||||
return value, ok
|
||||
}
|
||||
}
|
||||
|
||||
// SetAttribute définit la valeur d'un attribut du groupe
|
||||
// Cas particuliers: "id" utilise SetId(), "k" est immutable (panique)
|
||||
func (ksg *KmerSetGroup) SetAttribute(key string, value interface{}) {
|
||||
switch key {
|
||||
case "id":
|
||||
if id, ok := value.(string); ok {
|
||||
ksg.SetId(id)
|
||||
} else {
|
||||
panic(fmt.Sprintf("id must be a string, got %T", value))
|
||||
}
|
||||
case "k":
|
||||
panic("k is immutable and cannot be modified via SetAttribute")
|
||||
default:
|
||||
ksg.Metadata[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteAttribute supprime un attribut du groupe
|
||||
func (ksg *KmerSetGroup) DeleteAttribute(key string) {
|
||||
delete(ksg.Metadata, key)
|
||||
}
|
||||
|
||||
// RemoveAttribute supprime un attribut du groupe (alias)
|
||||
func (ksg *KmerSetGroup) RemoveAttribute(key string) {
|
||||
ksg.DeleteAttribute(key)
|
||||
}
|
||||
|
||||
// RenameAttribute renomme un attribut du groupe
|
||||
func (ksg *KmerSetGroup) RenameAttribute(newName, oldName string) {
|
||||
if value, ok := ksg.Metadata[oldName]; ok {
|
||||
ksg.Metadata[newName] = value
|
||||
delete(ksg.Metadata, oldName)
|
||||
}
|
||||
}
|
||||
|
||||
// GetIntAttribute récupère un attribut entier du groupe
|
||||
func (ksg *KmerSetGroup) GetIntAttribute(key string) (int, bool) {
|
||||
value, ok := ksg.GetAttribute(key)
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
switch v := value.(type) {
|
||||
case int:
|
||||
return v, true
|
||||
case int64:
|
||||
return int(v), true
|
||||
case float64:
|
||||
return int(v), true
|
||||
case string:
|
||||
if i, err := strconv.Atoi(v); err == nil {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// GetFloatAttribute récupère un attribut float64 du groupe
|
||||
func (ksg *KmerSetGroup) GetFloatAttribute(key string) (float64, bool) {
|
||||
value, ok := ksg.GetAttribute(key)
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
switch v := value.(type) {
|
||||
case float64:
|
||||
return v, true
|
||||
case float32:
|
||||
return float64(v), true
|
||||
case int:
|
||||
return float64(v), true
|
||||
case int64:
|
||||
return float64(v), true
|
||||
case string:
|
||||
if f, err := strconv.ParseFloat(v, 64); err == nil {
|
||||
return f, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// GetNumericAttribute récupère un attribut numérique du groupe
|
||||
func (ksg *KmerSetGroup) GetNumericAttribute(key string) (float64, bool) {
|
||||
return ksg.GetFloatAttribute(key)
|
||||
}
|
||||
|
||||
// GetStringAttribute récupère un attribut chaîne du groupe
|
||||
func (ksg *KmerSetGroup) GetStringAttribute(key string) (string, bool) {
|
||||
value, ok := ksg.GetAttribute(key)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
return v, true
|
||||
default:
|
||||
return fmt.Sprintf("%v", v), true
|
||||
}
|
||||
}
|
||||
|
||||
// GetBoolAttribute récupère un attribut booléen du groupe
|
||||
func (ksg *KmerSetGroup) GetBoolAttribute(key string) (bool, bool) {
|
||||
value, ok := ksg.GetAttribute(key)
|
||||
if !ok {
|
||||
return false, false
|
||||
}
|
||||
|
||||
switch v := value.(type) {
|
||||
case bool:
|
||||
return v, true
|
||||
case int:
|
||||
return v != 0, true
|
||||
case string:
|
||||
if b, err := strconv.ParseBool(v); err == nil {
|
||||
return b, true
|
||||
}
|
||||
}
|
||||
return false, false
|
||||
}
|
||||
|
||||
// AttributeKeys retourne l'ensemble des clés d'attributs du groupe
|
||||
func (ksg *KmerSetGroup) AttributeKeys() obiutils.Set[string] {
|
||||
keys := obiutils.MakeSet[string]()
|
||||
for key := range ksg.Metadata {
|
||||
keys.Add(key)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// Keys retourne l'ensemble des clés d'attributs du groupe (alias)
|
||||
func (ksg *KmerSetGroup) Keys() obiutils.Set[string] {
|
||||
return ksg.AttributeKeys()
|
||||
}
|
||||
|
||||
// ==================================
|
||||
// MÉTHODES POUR ACCÉDER AUX ATTRIBUTS DES SETS INDIVIDUELS VIA Get()
|
||||
// Architecture zero-copy: ksg.Get(i).SetAttribute(...)
|
||||
// ==================================
|
||||
|
||||
// Exemple d'utilisation:
|
||||
// Pour accéder aux métadonnées d'un KmerSet individuel dans un groupe:
|
||||
// ks := ksg.Get(0)
|
||||
// ks.SetAttribute("level", 1)
|
||||
// hasLevel := ks.HasAttribute("level")
|
||||
//
|
||||
// Pour les métadonnées du groupe:
|
||||
// ksg.SetAttribute("name", "FrequencyFilter")
|
||||
// name, ok := ksg.GetStringAttribute("name")
|
||||
|
||||
// AllAttributeKeys retourne toutes les clés d'attributs uniques du groupe ET de tous ses sets
|
||||
func (ksg *KmerSetGroup) AllAttributeKeys() obiutils.Set[string] {
|
||||
keys := obiutils.MakeSet[string]()
|
||||
|
||||
// Ajouter les clés du groupe
|
||||
for key := range ksg.Metadata {
|
||||
keys.Add(key)
|
||||
}
|
||||
|
||||
// Ajouter les clés de chaque set
|
||||
for _, ks := range ksg.sets {
|
||||
for key := range ks.Metadata {
|
||||
keys.Add(key)
|
||||
}
|
||||
}
|
||||
|
||||
return keys
|
||||
}
|
||||
@@ -9,9 +9,10 @@ import (
|
||||
// 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 {
|
||||
K int // Taille des k-mers
|
||||
sets []*KmerSet // Vecteur de KmerSet
|
||||
Metadata []map[string]interface{} // Métadonnées par KmerSet (même longueur que sets)
|
||||
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
|
||||
@@ -21,19 +22,22 @@ func NewKmerSetGroup(k int, n int) *KmerSetGroup {
|
||||
}
|
||||
|
||||
sets := make([]*KmerSet, n)
|
||||
metadata := make([]map[string]interface{}, n)
|
||||
for i := range sets {
|
||||
sets[i] = NewKmerSet(k)
|
||||
metadata[i] = make(map[string]interface{})
|
||||
}
|
||||
|
||||
return &KmerSetGroup{
|
||||
K: k,
|
||||
k: k,
|
||||
sets: sets,
|
||||
Metadata: metadata,
|
||||
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)
|
||||
@@ -54,8 +58,8 @@ 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))
|
||||
if ks.k != ksg.k {
|
||||
panic(fmt.Sprintf("KmerSet k mismatch: expected %d, got %d", ksg.k, ks.k))
|
||||
}
|
||||
ksg.sets[index] = ks
|
||||
}
|
||||
@@ -93,28 +97,37 @@ func (ksg *KmerSetGroup) Clear() {
|
||||
}
|
||||
}
|
||||
|
||||
// Clone crée une copie complète du groupe
|
||||
func (ksg *KmerSetGroup) Clone() *KmerSetGroup {
|
||||
clonedSets := make([]*KmerSet, len(ksg.sets))
|
||||
clonedMetadata := make([]map[string]interface{}, len(ksg.Metadata))
|
||||
|
||||
// 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 {
|
||||
clonedSets[i] = ks.Clone()
|
||||
copiedSets[i] = ks.Copy() // Copy chaque KmerSet avec ses métadonnées
|
||||
}
|
||||
|
||||
// Copier les métadonnées du groupe
|
||||
clonedMetadata[i] = make(map[string]interface{}, len(ksg.Metadata[i]))
|
||||
for k, v := range ksg.Metadata[i] {
|
||||
clonedMetadata[i][k] = v
|
||||
}
|
||||
// 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{
|
||||
K: ksg.K,
|
||||
sets: clonedSets,
|
||||
Metadata: clonedMetadata,
|
||||
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) {
|
||||
@@ -134,10 +147,10 @@ func (ksg *KmerSetGroup) AddSequences(sequences *obiseq.BioSequenceSlice, index
|
||||
// Union retourne l'union de tous les KmerSet du groupe
|
||||
func (ksg *KmerSetGroup) Union() *KmerSet {
|
||||
if len(ksg.sets) == 0 {
|
||||
return NewKmerSet(ksg.K)
|
||||
return NewKmerSet(ksg.k)
|
||||
}
|
||||
|
||||
result := ksg.sets[0].Clone()
|
||||
result := ksg.sets[0].Copy()
|
||||
for i := 1; i < len(ksg.sets); i++ {
|
||||
result = result.Union(ksg.sets[i])
|
||||
}
|
||||
@@ -147,10 +160,10 @@ func (ksg *KmerSetGroup) Union() *KmerSet {
|
||||
// Intersect retourne l'intersection de tous les KmerSet du groupe
|
||||
func (ksg *KmerSetGroup) Intersect() *KmerSet {
|
||||
if len(ksg.sets) == 0 {
|
||||
return NewKmerSet(ksg.K)
|
||||
return NewKmerSet(ksg.k)
|
||||
}
|
||||
|
||||
result := ksg.sets[0].Clone()
|
||||
result := ksg.sets[0].Copy()
|
||||
for i := 1; i < len(ksg.sets); i++ {
|
||||
result = result.Intersect(ksg.sets[i])
|
||||
}
|
||||
@@ -173,7 +186,7 @@ type KmerSetStats struct {
|
||||
|
||||
func (ksg *KmerSetGroup) Stats() KmerSetGroupStats {
|
||||
stats := KmerSetGroupStats{
|
||||
K: ksg.K,
|
||||
K: ksg.k,
|
||||
Size: len(ksg.sets),
|
||||
Sets: make([]KmerSetStats, len(ksg.sets)),
|
||||
}
|
||||
|
||||
@@ -36,12 +36,14 @@ func (f MetadataFormat) String() string {
|
||||
|
||||
// KmerSetMetadata contient les métadonnées d'un KmerSet ou KmerSetGroup
|
||||
type KmerSetMetadata struct {
|
||||
K int `toml:"k" yaml:"k" json:"k"` // Taille des k-mers
|
||||
Type string `toml:"type" yaml:"type" json:"type"` // "KmerSet" ou "KmerSetGroup"
|
||||
Size int `toml:"size" yaml:"size" json:"size"` // 1 pour KmerSet, n pour KmerSetGroup
|
||||
Files []string `toml:"files" yaml:"files" json:"files"` // Liste des fichiers .roaring
|
||||
UserMetadata map[string]interface{} `toml:"user_metadata,omitempty" yaml:"user_metadata,omitempty" json:"user_metadata,omitempty"` // Métadonnées KmerSet unique
|
||||
SetsMetadata []map[string]interface{} `toml:"sets_metadata,omitempty" yaml:"sets_metadata,omitempty" json:"sets_metadata,omitempty"` // Métadonnées par set (KmerSetGroup)
|
||||
ID string `toml:"id,omitempty" yaml:"id,omitempty" json:"id,omitempty"` // Identifiant unique
|
||||
K int `toml:"k" yaml:"k" json:"k"` // Taille des k-mers
|
||||
Type string `toml:"type" yaml:"type" json:"type"` // "KmerSet" ou "KmerSetGroup"
|
||||
Size int `toml:"size" yaml:"size" json:"size"` // 1 pour KmerSet, n pour KmerSetGroup
|
||||
Files []string `toml:"files" yaml:"files" json:"files"` // Liste des fichiers .roaring
|
||||
SetsIDs []string `toml:"sets_ids,omitempty" yaml:"sets_ids,omitempty" json:"sets_ids,omitempty"` // IDs des KmerSet individuels
|
||||
UserMetadata map[string]interface{} `toml:"user_metadata,omitempty" yaml:"user_metadata,omitempty" json:"user_metadata,omitempty"` // Métadonnées KmerSet ou KmerSetGroup
|
||||
SetsMetadata []map[string]interface{} `toml:"sets_metadata,omitempty" yaml:"sets_metadata,omitempty" json:"sets_metadata,omitempty"` // Métadonnées des KmerSet individuels dans un KmerSetGroup
|
||||
}
|
||||
|
||||
// SaveKmerSet sauvegarde un KmerSet dans un répertoire
|
||||
@@ -54,7 +56,8 @@ func (ks *KmerSet) Save(directory string, format MetadataFormat) error {
|
||||
|
||||
// Métadonnées
|
||||
metadata := KmerSetMetadata{
|
||||
K: ks.K,
|
||||
ID: ks.id,
|
||||
K: ks.k,
|
||||
Type: "KmerSet",
|
||||
Size: 1,
|
||||
Files: []string{"set_0.roaring"},
|
||||
@@ -109,6 +112,9 @@ func LoadKmerSet(directory string) (*KmerSet, error) {
|
||||
|
||||
ks := NewKmerSet(metadata.K)
|
||||
|
||||
// Charger l'ID
|
||||
ks.id = metadata.ID
|
||||
|
||||
// Charger les métadonnées utilisateur
|
||||
if metadata.UserMetadata != nil {
|
||||
ks.Metadata = metadata.UserMetadata
|
||||
@@ -135,12 +141,23 @@ func (ksg *KmerSetGroup) Save(directory string, format MetadataFormat) error {
|
||||
files[i] = fmt.Sprintf("set_%d.roaring", i)
|
||||
}
|
||||
|
||||
// Collecter les IDs et métadonnées de chaque KmerSet individuel
|
||||
setsIDs := make([]string, len(ksg.sets))
|
||||
setsMetadata := make([]map[string]interface{}, len(ksg.sets))
|
||||
for i, ks := range ksg.sets {
|
||||
setsIDs[i] = ks.id
|
||||
setsMetadata[i] = ks.Metadata
|
||||
}
|
||||
|
||||
metadata := KmerSetMetadata{
|
||||
K: ksg.K,
|
||||
ID: ksg.id,
|
||||
K: ksg.k,
|
||||
Type: "KmerSetGroup",
|
||||
Size: len(ksg.sets),
|
||||
Files: files,
|
||||
SetsMetadata: ksg.Metadata, // Sauvegarder les métadonnées de chaque set
|
||||
SetsIDs: setsIDs, // IDs de chaque set
|
||||
UserMetadata: ksg.Metadata, // Métadonnées du groupe
|
||||
SetsMetadata: setsMetadata, // Métadonnées de chaque set
|
||||
}
|
||||
|
||||
// Sauvegarder les métadonnées
|
||||
@@ -187,12 +204,29 @@ func LoadKmerSetGroup(directory string) (*KmerSetGroup, error) {
|
||||
// Créer le groupe
|
||||
ksg := NewKmerSetGroup(metadata.K, metadata.Size)
|
||||
|
||||
// Charger les métadonnées de chaque set
|
||||
// Charger l'ID du groupe
|
||||
ksg.id = metadata.ID
|
||||
|
||||
// Charger les métadonnées du groupe
|
||||
if metadata.UserMetadata != nil {
|
||||
ksg.Metadata = metadata.UserMetadata
|
||||
}
|
||||
|
||||
// Charger les IDs de chaque KmerSet
|
||||
if metadata.SetsIDs != nil && len(metadata.SetsIDs) == metadata.Size {
|
||||
for i := range ksg.sets {
|
||||
ksg.sets[i].id = metadata.SetsIDs[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Charger les métadonnées de chaque KmerSet individuel
|
||||
if metadata.SetsMetadata != nil {
|
||||
if len(metadata.SetsMetadata) != metadata.Size {
|
||||
return nil, fmt.Errorf("metadata size mismatch: expected %d, got %d", metadata.Size, len(metadata.SetsMetadata))
|
||||
return nil, fmt.Errorf("sets metadata size mismatch: expected %d, got %d", metadata.Size, len(metadata.SetsMetadata))
|
||||
}
|
||||
for i := range ksg.sets {
|
||||
ksg.sets[i].Metadata = metadata.SetsMetadata[i]
|
||||
}
|
||||
ksg.Metadata = metadata.SetsMetadata
|
||||
}
|
||||
|
||||
// Charger chaque bitmap
|
||||
|
||||
Reference in New Issue
Block a user