Source code for oscar_colony.optimise.surplus_summary

from dataclasses import dataclass, field

import pandas as pd

from oscar_colony.breeding_scheme import (
    BreedingScheme,
    Genotype,
)
from oscar_colony.optimise.estimate_offspring import ExpectedOffspring


[docs] @dataclass class GenotypeSurplus: """Summary of surplus for a single genotype""" total_n: float = 0 total_n_surplus: float = 0 percent_surplus: float = 0
[docs] @dataclass class SurplusSummary: """Summary of surplus across all genotypes""" total_n: float = 0 total_n_surplus: float = 0 surplus_per_genotype: dict[tuple[Genotype, ...], GenotypeSurplus] = field( default_factory=dict )
[docs] def create_genotype_df( self, decimal_places: int | None = None ) -> pd.DataFrame: """Create a pandas dataframe from surplus_per_genotype. Columns are: 'Genotype', 'Required N', 'Total N', 'Total N Surplus' and 'Percent Surplus' Parameters ---------- decimal_places : int | None, optional Number of decimal places to round float values to Returns ------- pd.DataFrame DataFrame summarising totals and surplus per genotype """ rows = [] for genotype, surplus in self.surplus_per_genotype.items(): required_n = surplus.total_n - surplus.total_n_surplus rows.append( ( Genotype.to_string(genotype), required_n, surplus.total_n, surplus.total_n_surplus, surplus.percent_surplus, ) ) genotype_df = pd.DataFrame( rows, columns=[ "Genotype", "Required N", "Total N", "Total N Surplus", "Percent Surplus", ], ) if decimal_places is not None: genotype_df = genotype_df.round(decimals=decimal_places) return genotype_df
[docs] def create_surplus_summary( required_n_per_genotype: dict[tuple[Genotype, ...], int], n_matings_per_scheme: dict[BreedingScheme, int], offspring_per_scheme: dict[BreedingScheme, ExpectedOffspring], ) -> SurplusSummary: """Create a summary of the total and surplus numbers for the given combination of breeding schemes. Parameters ---------- required_n_per_genotype : dict[tuple[Genotype, ...], int] Required number of individuals of each genotype n_matings_per_scheme : dict[BreedingScheme, int] Optimal number of matings per breeding scheme offspring_per_scheme : dict[BreedingScheme, ExpectedOffspring] The estimated number of offspring produced per mating of each breeding scheme Returns ------- SurplusSummary Summary of total and surplus numbers """ surplus_summary = SurplusSummary() surplus_per_genotype = surplus_summary.surplus_per_genotype # Get number of expected offspring overall / per genotype for breeding_scheme, n_matings in n_matings_per_scheme.items(): expected_offspring = offspring_per_scheme[breeding_scheme] surplus_summary.total_n += expected_offspring.total_n * n_matings n_per_genotype = expected_offspring.n_per_genotype for genotype, n_per_mating in n_per_genotype.items(): if genotype not in surplus_per_genotype: surplus_per_genotype[genotype] = GenotypeSurplus() surplus_per_genotype[genotype].total_n += n_per_mating * n_matings # Calculate total surplus total_required = sum(required_n_per_genotype.values()) surplus_summary.total_n_surplus = surplus_summary.total_n - total_required # Calculate surplus per genotype for genotype, surplus in surplus_per_genotype.items(): if genotype in required_n_per_genotype: required_n = required_n_per_genotype[genotype] else: required_n = 0 surplus.total_n_surplus = surplus.total_n - required_n surplus.percent_surplus = ( surplus.total_n_surplus / surplus.total_n ) * 100 return surplus_summary