Skip to content

Assays

Represent and generate assays.

Assay

Bases: BaseModel

Represent a single assay.

Parameters:

Name Type Description Default
id str

unique ID

required
specimen_id str

specimen assayed

required
machine_id str

machine used

required
person_id str

who did assay

required
performed date

date assay performed

datetime.date(2025, 4, 1)
treatments Grid

treatments applied

required
readings Grid

readings obtained

required
Source code in src/snailz/assays.py
 15
 16
 17
 18
 19
 20
 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
 70
 71
 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
class Assay(BaseModel):
    """Represent a single assay."""

    id: str = Field(description="unique ID")
    specimen_id: str = Field(description="specimen assayed")
    machine_id: str = Field(description="machine used")
    person_id: str = Field(description="who did assay")
    performed: date = Field(
        default=DEFAULT_START_DATE, description="date assay performed"
    )
    treatments: Grid = Field(description="treatments applied")
    readings: Grid = Field(description="readings obtained")

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

    @staticmethod
    def generate(params, specimen, machine, person):
        """Generate an assay for a specimen."""

        treatments = Assay._make_treatments(params)
        readings = Assay._make_readings(params, specimen, treatments)
        return Assay(
            id=next(Assay._id_generator),
            specimen_id=specimen.id,
            machine_id=machine.id,
            person_id=person.id,
            performed=choose_assay_date(params, specimen),
            treatments=treatments,
            readings=readings,
        )

    @staticmethod
    def _make_treatments(params):
        """Generate grid of treatments."""

        grid = Grid(size=params.plate_size)
        for x in range(grid.size):
            for y in range(grid.size):
                grid[x, y] = random.choice("CS")

        return grid

    @staticmethod
    def _make_readings(params, specimen, treatments):
        """Make grid of readings."""
        grid = Grid(size=params.plate_size)
        for x in range(grid.size):
            for y in range(grid.size):
                if treatments[x, y] == "C":
                    mean = 0.0
                elif specimen.is_mutant:
                    mean = params.mean_mutant
                else:
                    mean = params.mean_normal
                grid[x, y] = abs(mean + random.gauss(0, params.reading_noise))

        return grid

    @staticmethod
    def all_csv(writer, assays):
        """Write all assays as a single CSV."""
        writer.writerow(
            [
                "id",
                "specimen",
                "machine",
                "person",
                "performed",
                "row",
                "col",
                "treatment",
                "reading",
            ]
        )
        for a in assays:
            for x in range(a.readings.size):
                for y in range(a.readings.size):
                    writer.writerow(
                        [
                            a.id,
                            a.specimen_id,
                            a.machine_id,
                            a.person_id,
                            a.performed.isoformat(),
                            y + 1,
                            chr(ord("A") + x),
                            a.treatments[x, y],
                            round(a.readings[x, y], PRECISION),
                        ]
                    )

    def to_csv(self, writer, write_treatments):
        """Save as CSV."""
        padding = [""] * (self.treatments.size - 4)
        for name, value in (
            ("id", self.id),
            ("specimen", self.specimen_id),
            ("machine", self.machine_id),
            ("person", self.person_id),
            ("performed", self.performed.isoformat()),
        ):
            writer.writerow([name, value] + padding)

        if write_treatments:
            grid = self.treatments
            convert = lambda x: x
        else:
            grid = self.readings
            convert = lambda x: round(x, PRECISION)

        title = [""] + [chr(ord("A") + i) for i in range(grid.size)]
        writer.writerow(title)
        for y in range(grid.size):
            row = [f"{y + 1}"] + [convert(grid[x, y]) for x in range(grid.size)]
            writer.writerow(row)

generate(params, specimen, machine, person) staticmethod

Generate an assay for a specimen.

Source code in src/snailz/assays.py
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@staticmethod
def generate(params, specimen, machine, person):
    """Generate an assay for a specimen."""

    treatments = Assay._make_treatments(params)
    readings = Assay._make_readings(params, specimen, treatments)
    return Assay(
        id=next(Assay._id_generator),
        specimen_id=specimen.id,
        machine_id=machine.id,
        person_id=person.id,
        performed=choose_assay_date(params, specimen),
        treatments=treatments,
        readings=readings,
    )

_make_treatments(params) staticmethod

Generate grid of treatments.

Source code in src/snailz/assays.py
46
47
48
49
50
51
52
53
54
55
@staticmethod
def _make_treatments(params):
    """Generate grid of treatments."""

    grid = Grid(size=params.plate_size)
    for x in range(grid.size):
        for y in range(grid.size):
            grid[x, y] = random.choice("CS")

    return grid

_make_readings(params, specimen, treatments) staticmethod

Make grid of readings.

Source code in src/snailz/assays.py
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
@staticmethod
def _make_readings(params, specimen, treatments):
    """Make grid of readings."""
    grid = Grid(size=params.plate_size)
    for x in range(grid.size):
        for y in range(grid.size):
            if treatments[x, y] == "C":
                mean = 0.0
            elif specimen.is_mutant:
                mean = params.mean_mutant
            else:
                mean = params.mean_normal
            grid[x, y] = abs(mean + random.gauss(0, params.reading_noise))

    return grid

all_csv(writer, assays) staticmethod

Write all assays as a single CSV.

Source code in src/snailz/assays.py
 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
@staticmethod
def all_csv(writer, assays):
    """Write all assays as a single CSV."""
    writer.writerow(
        [
            "id",
            "specimen",
            "machine",
            "person",
            "performed",
            "row",
            "col",
            "treatment",
            "reading",
        ]
    )
    for a in assays:
        for x in range(a.readings.size):
            for y in range(a.readings.size):
                writer.writerow(
                    [
                        a.id,
                        a.specimen_id,
                        a.machine_id,
                        a.person_id,
                        a.performed.isoformat(),
                        y + 1,
                        chr(ord("A") + x),
                        a.treatments[x, y],
                        round(a.readings[x, y], PRECISION),
                    ]
                )

to_csv(writer, write_treatments)

Save as CSV.

Source code in src/snailz/assays.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
def to_csv(self, writer, write_treatments):
    """Save as CSV."""
    padding = [""] * (self.treatments.size - 4)
    for name, value in (
        ("id", self.id),
        ("specimen", self.specimen_id),
        ("machine", self.machine_id),
        ("person", self.person_id),
        ("performed", self.performed.isoformat()),
    ):
        writer.writerow([name, value] + padding)

    if write_treatments:
        grid = self.treatments
        convert = lambda x: x
    else:
        grid = self.readings
        convert = lambda x: round(x, PRECISION)

    title = [""] + [chr(ord("A") + i) for i in range(grid.size)]
    writer.writerow(title)
    for y in range(grid.size):
        row = [f"{y + 1}"] + [convert(grid[x, y]) for x in range(grid.size)]
        writer.writerow(row)