Properties of populations#

This vignette is “getting ahead” of ourselves a bit. In order for it to be useful, it is best to have a simulated population in hand. The following hidden code block gives us one. Feel free to expand it to take a look–the details will make sense once you have studied the later vignettes on setting up and running simulations. The simulation done here is taken from another vignette.

Hide code cell source
import fwdpy11

pop = fwdpy11.DiploidPopulation(500, 1.0)

rng = fwdpy11.GSLrng(54321)

optima = [
    fwdpy11.Optimum(when=0, optimum=0.0, VS=1.0),
    fwdpy11.Optimum(when=10 * pop.N - 200, optimum=1.0, VS=1.0),
]

GSS = fwdpy11.GaussianStabilizingSelection.single_trait(optima)

rho = 1000.

p = {
    "nregions": [],
    "gvalue": fwdpy11.Additive(2.0, GSS),
    "sregions": [fwdpy11.GaussianS(0, 1., 1, 0.1)],
    "recregions": [fwdpy11.PoissonInterval(0, 1., rho / float(4 * pop.N))],
    "rates": (0.0, 1e-3, None),
    "prune_selected": False,
    "demography": fwdpy11.ForwardDemesGraph.tubes([pop.N], burnin=10),
    "simlen": 10 * pop.N,
}
params = fwdpy11.ModelParams(**p)

fwdpy11.evolvets(rng, pop, params, 100, suppress_table_indexing=True)

Basic properties#

print(f"Total number of diploids = {pop.N}")
print(f"Current generation = {pop.generation}")
Total number of diploids = 500
Current generation = 5000

Mutations#

Mutations are instances of fwdpy11.Mutation stored in fwdpy11.DiploidPopulation.mutations. These objects do not have nice representations in Python:

[i for i in pop.mutations[:5]]
[<fwdpy11._fwdpy11.Mutation at 0x7f6434e40d30>,
 <fwdpy11._fwdpy11.Mutation at 0x7f6434e40af0>,
 <fwdpy11._fwdpy11.Mutation at 0x7f6434e25730>,
 <fwdpy11._fwdpy11.Mutation at 0x7f6434e24a30>,
 <fwdpy11._fwdpy11.Mutation at 0x7f6434e27970>]

So, let’s just look at the fields of the first mutation:

m = pop.mutations[0]
print(f"position = {m.pos}\neffect_size = {m.s}\ndominance = {m.h}\norigin time = {m.g}")
position = 0.2969475525896996
effect_size = 0.007760541335115791
dominance = 1.0
origin time = 4421

fwdpy11.Mutation has other attributes that are not relevant to the type of simulation done here.

Diploids#

Diploids are instances of fwdpy11.DiploidGenotype. They are stored in fwdpy11.DiploidPopulation.diploids. Diploids store the indexes of their individual genomes, which we describe below.

[i for i in pop.diploids[:5]]
[DiploidGenotype(first=1, second=624),
 DiploidGenotype(first=471, second=270),
 DiploidGenotype(first=210, second=596),
 DiploidGenotype(first=559, second=2),
 DiploidGenotype(first=567, second=4)]

Diploid meta data#

Diploid individuals have associated data, stored in fwdpy11.DiploidPopulation.diploid_metadata. The meta data are instances of fwdpy11.DiploidMetadata. That class is also a numpy.dtype, allowing us to access the raw data efficiently as a numpy.recarray:

import numpy as np
md = np.array(pop.diploid_metadata, copy=False)
md[:5]
array([(1.10704541, 0., 0.99428702, [0., 0., 0.], 0, [124,  38], 0, 0, [0, 1]),
       (0.72834507, 0., 0.96377424, [0., 0., 0.], 1, [264,  27], 0, 0, [2, 3]),
       (0.8343338 , 0., 0.98637108, [0., 0., 0.], 2, [111, 338], 0, 0, [4, 5]),
       (1.10704541, 0., 0.99428702, [0., 0., 0.], 3, [311, 274], 0, 0, [6, 7]),
       (0.9436857 , 0., 0.99841561, [0., 0., 0.], 4, [316, 124], 0, 0, [8, 9])],
      dtype=[('g', '<f8'), ('e', '<f8'), ('w', '<f8'), ('geography', '<f8', (3,)), ('label', '<u8'), ('parents', '<u8', (2,)), ('deme', '<i4'), ('sex', '<i4'), ('nodes', '<i4', (2,))])

The field names of the record array exactly match the attribute names of fwdpy11.DiploidMetadata.

The record arrays allow efficient calculation of important quantities:

print(f"Mean trait value = {md['g'].mean():0.4f}.\nMean fitness = {md['w'].mean():0.4f}")
Mean trait value = 0.8998.
Mean fitness = 0.9880

Ancient samples#

When ancient/preserved samples are recorded during simulations, their meta data are stored in :attr:fwdpy11.DiploidPopulation.ancient_sample_metadata.

Haploid genomes#

Haploid genomes are instances of fwdpy11.HaploidGenome. These objects contain indexes to mutations. Let’s look at the effect sizes and origin times of mutations in the first non-empty genome:

def mut_info(pop, i):
    rv = False
    for m in pop.haploid_genomes[i].smutations:
        print(f"effect size = {pop.mutations[m].s:0.4f}, origin time = {pop.mutations[m].g}")
        rv = True
    return rv
    
for i in pop.diploids:
    if mut_info(pop, i.first) is True:
        break
    elif mut_info(pop, i.second) is True:
        break
effect size = 0.1634, origin time = 4843
effect size = 0.0078, origin time = 4421
effect size = 0.0644, origin time = 4592
effect size = 0.1151, origin time = 4788
effect size = 0.1521, origin time = 4823
effect size = 0.0508, origin time = 4740

Some details#

Neutral mutations are never added to haploid genomes.

Tables#

A population contains an instance of fwdpy11.TableCollection which is used to represent the genetic ancestry of the sample using the methods described in [KTAR18].

The table contents are described in the class documentation. All tables can be accessed either as Python objects or as numpy.recarray objects as we say above for diploid meta data.