Skip to content

Specimens

Specimen

Bases: BaseModel

Store a single specimen specimen.

Parameters:

Name Type Description Default
id str

unique ID

required
genome str

genome

required
is_mutant bool

is this a mutant?

required
mass float

mass (g)

required
grid str

sample grid ID

''
x int

sample X coordinate

-1
y int

sample Y coordinate

-1
sampled date

Date sample taken

datetime.date(2025, 4, 1)
Source code in src/snailz/specimens.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
class Specimen(BaseModel):
    """Store a single specimen specimen."""

    id: str = Field(description="unique ID")
    genome: str = Field(min_length=1, description="genome")
    is_mutant: bool = Field(description="is this a mutant?")
    mass: float = Field(gt=0, description="mass (g)")
    grid: str = Field(default="", description="sample grid ID")
    x: int = Field(default=-1, description="sample X coordinate")
    y: int = Field(default=-1, description="sample Y coordinate")
    sampled: date = Field(default=DEFAULT_START_DATE, description="Date sample taken")

    _id_generator: ClassVar = generic_id_generator(lambda i: f"S{i:04d}")

    @staticmethod
    def generate(params, ref_genome, is_mutant, susc_locus, susc_base):
        """Generate a single specimen.

        Parameters:
            params (SpecimenParams): parameters
            ref_genome (str): reference genome
            is_mutant (bool): is this specimen a mutant?
            susc_locus (int): susceptible locus in genome
            susc_base (str): base indicating mutant

        Returns:
            (Specimen): randomly-generated specimen.
        """

        genome = [
            random.choice(OTHERS[b])
            if random.uniform(0.0, 1.0) < params.mut_prob
            else b
            for i, b in enumerate(ref_genome)
        ]
        if is_mutant:
            genome[susc_locus] = susc_base
        mass = abs(random.gauss(params.mass_mean, params.mass_sd))

        days = random.randint(0, 1 + (params.end_date - params.start_date).days)
        sampled = params.start_date + timedelta(days=days)

        return Specimen(
            id=next(Specimen._id_generator),
            genome="".join(genome),
            is_mutant=is_mutant,
            mass=mass,
            sampled=sampled,
        )

generate(params, ref_genome, is_mutant, susc_locus, susc_base) staticmethod

Generate a single specimen.

Parameters:

Name Type Description Default
params SpecimenParams

parameters

required
ref_genome str

reference genome

required
is_mutant bool

is this specimen a mutant?

required
susc_locus int

susceptible locus in genome

required
susc_base str

base indicating mutant

required

Returns:

Type Description
Specimen

randomly-generated specimen.

Source code in src/snailz/specimens.py
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
@staticmethod
def generate(params, ref_genome, is_mutant, susc_locus, susc_base):
    """Generate a single specimen.

    Parameters:
        params (SpecimenParams): parameters
        ref_genome (str): reference genome
        is_mutant (bool): is this specimen a mutant?
        susc_locus (int): susceptible locus in genome
        susc_base (str): base indicating mutant

    Returns:
        (Specimen): randomly-generated specimen.
    """

    genome = [
        random.choice(OTHERS[b])
        if random.uniform(0.0, 1.0) < params.mut_prob
        else b
        for i, b in enumerate(ref_genome)
    ]
    if is_mutant:
        genome[susc_locus] = susc_base
    mass = abs(random.gauss(params.mass_mean, params.mass_sd))

    days = random.randint(0, 1 + (params.end_date - params.start_date).days)
    sampled = params.start_date + timedelta(days=days)

    return Specimen(
        id=next(Specimen._id_generator),
        genome="".join(genome),
        is_mutant=is_mutant,
        mass=mass,
        sampled=sampled,
    )

AllSpecimens

Bases: BaseModel

Store a set of specimens.

Parameters:

Name Type Description Default
params SpecimenParams

generation parameters

required
ref_genome str

reference genome

required
susc_locus int

susceptible locus

required
susc_base str

susceptible mutation

required
samples list[Specimen]

specimens

required
Source code in src/snailz/specimens.py
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
class AllSpecimens(BaseModel):
    """Store a set of specimens."""

    params: SpecimenParams = Field(description="generation parameters")
    ref_genome: str = Field(description="reference genome")
    susc_locus: int = Field(description="susceptible locus")
    susc_base: str = Field(description="susceptible mutation")
    samples: list[Specimen] = Field(description="specimens")

    @staticmethod
    def generate(params, num):
        """Generate specimens.

        Parameters:
            params (SpecimenParams): parameters
            num (int): number of specimens required

        Returns:
            (AllSpecimens): collection of randomly-generated specimens.
        """

        if num <= 0:
            raise ValueError(f"invalid number of specimens {num}")

        ref_genome = "".join(random.choices(BASES, k=params.genome_length))
        susc_locus = random.choice(list(range(len(ref_genome))))
        susc_base = random.choice(OTHERS[ref_genome[susc_locus]])

        mutant_ids = set(
            random.choices(list(range(num)), k=math.ceil(params.mut_frac * num))
        )

        samples = [
            Specimen.generate(
                params, ref_genome, i in mutant_ids, susc_locus, susc_base
            )
            for i in range(num)
        ]

        return AllSpecimens(
            params=params,
            ref_genome=ref_genome,
            susc_locus=susc_locus,
            susc_base=susc_base,
            samples=samples,
        )

    def to_csv(self, writer):
        """Save specimens as CSV.

        Parameters:
            writer (stream): where to write
        """
        writer.writerow(["id", "genome", "mass", "grid", "x", "y", "sampled"])
        writer.writerows(
            [
                s.id,
                s.genome,
                round(s.mass, PRECISION),
                s.grid,
                s.x,
                s.y,
                s.sampled.isoformat(),
            ]
            for s in self.samples
        )

generate(params, num) staticmethod

Generate specimens.

Parameters:

Name Type Description Default
params SpecimenParams

parameters

required
num int

number of specimens required

required

Returns:

Type Description
AllSpecimens

collection of randomly-generated specimens.

Source code in src/snailz/specimens.py
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
@staticmethod
def generate(params, num):
    """Generate specimens.

    Parameters:
        params (SpecimenParams): parameters
        num (int): number of specimens required

    Returns:
        (AllSpecimens): collection of randomly-generated specimens.
    """

    if num <= 0:
        raise ValueError(f"invalid number of specimens {num}")

    ref_genome = "".join(random.choices(BASES, k=params.genome_length))
    susc_locus = random.choice(list(range(len(ref_genome))))
    susc_base = random.choice(OTHERS[ref_genome[susc_locus]])

    mutant_ids = set(
        random.choices(list(range(num)), k=math.ceil(params.mut_frac * num))
    )

    samples = [
        Specimen.generate(
            params, ref_genome, i in mutant_ids, susc_locus, susc_base
        )
        for i in range(num)
    ]

    return AllSpecimens(
        params=params,
        ref_genome=ref_genome,
        susc_locus=susc_locus,
        susc_base=susc_base,
        samples=samples,
    )

to_csv(writer)

Save specimens as CSV.

Parameters:

Name Type Description Default
writer stream

where to write

required
Source code in src/snailz/specimens.py
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
def to_csv(self, writer):
    """Save specimens as CSV.

    Parameters:
        writer (stream): where to write
    """
    writer.writerow(["id", "genome", "mass", "grid", "x", "y", "sampled"])
    writer.writerows(
        [
            s.id,
            s.genome,
            round(s.mass, PRECISION),
            s.grid,
            s.x,
            s.y,
            s.sampled.isoformat(),
        ]
        for s in self.samples
    )