mirror of
https://github.com/metabarcoding/obitools4.git
synced 2026-11-09 17:24:41 +00:00
This change introduces the core functionality for identifying, validating, and statistically evaluating primer pairs within the corpus. It includes new data structures for primer parameters, amplicon statistics, and sequence handling, along with methods for assigning taxa to sequences. Comprehensive tests are added to ensure the accuracy of the pair evaluation against expected outputs.
70 lines
1.9 KiB
Go
70 lines
1.9 KiB
Go
package obiprimer
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obitax"
|
|
)
|
|
|
|
// Taxa gives every corpus sequence its taxon at one rank (the species by
|
|
// default in ecoPrimers), as a dense index, and the number of distinct taxa
|
|
// among the examples and among the counterexamples.
|
|
//
|
|
// A sequence whose taxon has no ancestor at that rank (or no taxon at all) has
|
|
// the index -1; it is never counted as a taxon.
|
|
type Taxa struct {
|
|
Rank string
|
|
Of []int32 // Of[i] is the taxon index of Corpus.Seqs[i], or -1
|
|
ExampleTaxa int
|
|
CounterExamples int // distinct taxa among the counterexamples
|
|
nodes []*obitax.Taxon
|
|
}
|
|
|
|
// Name returns the taxon with the given index, for reporting.
|
|
func (t *Taxa) Name(i int32) *obitax.Taxon { return t.nodes[i] }
|
|
|
|
// AssignTaxa determines the taxon of every sequence of the corpus at rank.
|
|
func (c *Corpus) AssignTaxa(taxonomy *obitax.Taxonomy, rank string) (*Taxa, error) {
|
|
if taxonomy == nil {
|
|
return nil, fmt.Errorf("obiprimer: a taxonomy is required to compute taxon statistics")
|
|
}
|
|
ranks := taxonomy.RankList()
|
|
known := false
|
|
for _, r := range ranks {
|
|
if r == rank {
|
|
known = true
|
|
break
|
|
}
|
|
}
|
|
if !known {
|
|
return nil, fmt.Errorf("obiprimer: unknown taxonomic rank %q", rank)
|
|
}
|
|
|
|
t := &Taxa{Rank: rank, Of: make([]int32, len(c.Seqs))}
|
|
index := map[*obitax.TaxNode]int32{}
|
|
exSeen := map[int32]struct{}{}
|
|
coSeen := map[int32]struct{}{}
|
|
|
|
for i, seq := range c.Seqs {
|
|
t.Of[i] = -1
|
|
taxon := seq.Taxon(taxonomy).TaxonAtRank(rank)
|
|
if taxon == nil {
|
|
continue
|
|
}
|
|
id, ok := index[taxon.Node]
|
|
if !ok {
|
|
id = int32(len(t.nodes))
|
|
index[taxon.Node] = id
|
|
t.nodes = append(t.nodes, taxon)
|
|
}
|
|
t.Of[i] = id
|
|
if c.Roles[i] == Example {
|
|
exSeen[id] = struct{}{}
|
|
} else {
|
|
coSeen[id] = struct{}{}
|
|
}
|
|
}
|
|
t.ExampleTaxa, t.CounterExamples = len(exSeen), len(coSeen)
|
|
return t, nil
|
|
}
|