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 of Int 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.

Implementation detail

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.

Warning

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 endnewlabel (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.meta524-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.11.1
Note

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) |> DataFrame1×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.MetaDataType
MetaData{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

source