Metadata
Every Population
has MetaData
attached. They store for every genotype
- population count. While in principle redundant, it is significantly cheaper to keep a tally during a simulation than to iterate the entire lattice.
- fitness value
- TODO: death rate
- mutations: either
nothing
, or a vector ofInt
if mutations are present - the time when the genotype entered the population
Additionally, the field misc
is a dictionary for storing arbitrary user-defined key-value pairs.
Aside from using the value of genotype
to index into metadata, one can use the linear index of the underlying array in which they are stored. The function index
queries the linear index of a given genotype. An example is given below.
While insertions only happen at the end and leave linear indices unchanged, deletions will shift them. You should not rely on a particular index mapping to a given genotype when deletions are performed.
Convenient getter and setter are provided. Let's demonstrate on an example similar to the one from the Quick Start section, but label genotypes with strings of fixed length (from InlineStrings.jl)
julia> using InlineStrings
julia> import Base: zero
julia> zero(::Type{String7}) = String7("00-00")
zero (generic function with 83 methods)
julia> state, _ = spheref(HexagonalLattice, 128, f=1/10, g1=String7("00-00"), g2=String7("00-AA"))
(HexagonalLattice{InlineStrings.String7, Matrix{InlineStrings.String7}} 1 genotypes 1789 population, CartesianIndex{2}[CartesianIndex(60, 43), CartesianIndex(62, 43), CartesianIndex(63, 43), CartesianIndex(64, 43), CartesianIndex(65, 43), CartesianIndex(66, 43), CartesianIndex(67, 43), CartesianIndex(68, 43), CartesianIndex(70, 43), CartesianIndex(56, 44) … CartesianIndex(66, 86), CartesianIndex(67, 86), CartesianIndex(68, 86), CartesianIndex(69, 86), CartesianIndex(70, 86), CartesianIndex(71, 86), CartesianIndex(73, 86), CartesianIndex(63, 87), CartesianIndex(65, 87), CartesianIndex(67, 87)])
julia> function newlabel(state,g) l = String7( join(rand('0':'9',2))*"-"*join(rand('A':'Z',2)) ) while l in state.meta[:, :genotype] l = String7( join(rand('0':'9',2))*"-"*join(rand('A':'Z',2)) ) end return l end
newlabel (generic function with 1 method)
julia> eden_with_density!(state; label=newlabel, T=1024, # timesteps mu=1e0, # mutation rate per genome (not site!) d=1/100, # death rate fitness=(s,g_old,g_new)->1.0 + 0.1*randn() # function to assign fitness to new genotypes )
julia> state.meta
524-element MetaData{InlineStrings.String7}: (genotype = "00-AA", npop = 1752, fitness = 1.0, snps = nothing, age = (0, 0.0)) (genotype = "46-YQ", npop = 2, fitness = 0.8026922162866656, snps = [110084262, 253811787, 491383196, 565145360, 749194060], age = (0, 0.0)) (genotype = "96-XJ", npop = 1, fitness = 0.869823904746547, snps = [314518933, 400909946], age = (5, 0.10023335135522374)) (genotype = "17-SF", npop = 1, fitness = 0.9834021327434191, snps = [435520975], age = (6, 0.10185923369599444)) (genotype = "71-ST", npop = 1, fitness = 0.9218723537854466, snps = [479112480, 517628781, 943209862, 954631554], age = (14, 0.17555368249455572)) (genotype = "54-ZW", npop = 1, fitness = 0.8723606893869594, snps = [238562147], age = (18, 0.2019425087395775)) (genotype = "91-BB", npop = 4, fitness = 1.1097052357136297, snps = [205402397, 520761909, 569074237, 874639413], age = (23, 0.24336823090956902)) (genotype = "73-LC", npop = 1, fitness = 0.9429685908671765, snps = [360126036], age = (26, 0.2748001729518197)) (genotype = "35-UW", npop = 2, fitness = 1.0978922556179902, snps = [672492348], age = (27, 0.29377129088071935)) (genotype = "22-CM", npop = 3, fitness = 0.9108345792617524, snps = [435219219], age = (28, 0.30010234602896746)) ⋮ (genotype = "79-NI", npop = 1, fitness = 0.9196751074441047, snps = [776304358, 862648082], age = (1010, 7.599596469291105)) (genotype = "36-HN", npop = 1, fitness = 0.9471067845342037, snps = [266825313, 363001712, 506492735, 706422805, 767131980], age = (1011, 7.617345626199722)) (genotype = "66-ZQ", npop = 1, fitness = 0.876064872204234, snps = [377471091, 706940438, 857078752], age = (1012, 7.6278173847330795)) (genotype = "19-QF", npop = 1, fitness = 0.9579578044887453, snps = [108939159, 193120121, 222302266, 294783611, 498556788, 666825034, 871535173], age = (1013, 7.633293548955527)) (genotype = "26-HW", npop = 1, fitness = 1.0985091020078388, snps = [243554223, 603146202, 752612901, 839893191, 945853422, 974079460], age = (1017, 7.659144400146129)) (genotype = "04-EZ", npop = 1, fitness = 1.1177314304176673, snps = [19027929, 521511857], age = (1018, 7.6616833761353655)) (genotype = "07-YI", npop = 1, fitness = 0.9599311049567875, snps = [182584633, 642509645, 754868161, 909947272, 921455577, 989635317], age = (1019, 7.663833254109903)) (genotype = "42-PD", npop = 1, fitness = 0.9871456754594935, snps = [501650746, 647956475, 831204264], age = (1020, 7.673283160925875)) (genotype = "26-SD", npop = 1, fitness = 1.144614231051611, snps = [45842811, 61818289, 100338695, 293302154, 302462774, 345865540, 398472030, 585210393, 621587900, 744600308, 989340177], age = (1021, 7.675782629293754))
Query according to index
julia> state.meta[2, :genotype]
"46-YQ"
julia> state.meta[5:10, :npop]
6-element Vector{Int64}: 1 1 4 1 2 3
or genotype
julia> state.meta[g="46-YQ"]
(genotype = InlineStrings.String7("46-YQ"), npop = 2, fitness = 0.8026922162866656, snps = [110084262, 253811787, 491383196, 565145360, 749194060], age = (0, 0.0))
julia> state.meta[g="46-YQ", :fitness] = 1.1
1.1
In places where performance is paramount, getter and setter should be called like state.meta[2, Val(:genotype)]
to circumvent dynamic dispatch.
Alternatively, getgenotype(state, id)
, setgenotype!(state, id)
, etc. are provided.
Because MetaData
implements Julia's array interface, iterating is supported, and because each MetaDatum
is a NamedTuple
, conversion to e.g. a DataFrame
is straightforward
julia> using DataFrames
julia> filter(v->v.npop>10, state.meta) |> DataFrame
1×5 DataFrame Row │ genotype npop fitness snps age │ String7 Int64 Float64 Union… Tuple… ─────┼──────────────────────────────────────────── 1 │ 00-AA 1752 1.0 (0, 0.0)
API
GrowthDynamics.Populations.MetaData
— TypeMetaData{T}
Indexable structure to store information about a population.
Access
Use meta[id, :field]
or meta[g=G, :field]
to access and set information for genotype G
, or the genotype with id id
respectively.
id
is also the number of the vertex in the phylogeny corresponding to a given genotype.
:field
is one of
:npop
- population size:fitness
- fitness value:genotype
- genotype corresponding to a given id:snps
: - vector of integers representing mutations a genotype carries.nothing
if no mutations are present.:age
: tuple of(timestep, realtime)
of when the genotype was first instantiated.
Useful for putting lengths on the branches of a phylogeny.
Additionally, a global field meta.misc::Dict{Any,Any}
exists to store arbitrary, user-defined information.
See also MetaDatum
GrowthDynamics.Populations.MetaDatum
— TypeMetaDatum{T,S}
NamedTuple
to store information about a single genotype of type T
that carries mutations of type S
(default Int
).
Dictionaries.index
— Methodindex(M, g)
Return the index of genotype g
, or nothing
if g
is not in meta data M
.
GrowthDynamics.Populations.index!
— Functionindex!(::MetaData)
Reindex the metadata.
DataFrames.rename!
— Functionrename!(S, g1 => g2)
Rename genotype g1
to g2
.
GrowthDynamics.Populations.hassnps
— Functionhassnps(M::MetaData; g)
Return true
if genotype contains mutations.
GrowthDynamics.Populations.lastgenotype
— Functionlastgenotype(M::MetaData)
Return genotype that was added last.
Genotype that was last added to the population.
Base.length
— Functionlength(L::RealLattice)
Number of elements of L.data
.