Expressions and statements¶
Output a Python string (operator PyOutput
)¶
Operator PyOutput
is a simple operator that prints a Python string when
it is applied to a population. It is commonly used to print the progress of a
simulation (e.g. PyOutput
('start migration\\n', at=200
)) or
output separators to beautify outputs from PyEval
outputs (e.g.
PyOutput
('\\n', rep=-1
).
Execute Python statements (operator PyExec
)¶
Operator PyExec
executes Python statements in a population’s local
namespace when it is applied to that population. This operator is designed to
execute short Python statements but multiple statements separated by newline
characters are allowed.
Example PyExec uses two PyExec
operators to create and
use a variable traj
in each population’s local namespace. The first operator
initialize this variable as an empty list. During evolution, the frequency of
allele 1 at locus 0 is calcuated (operator Stat
) and appended to this
variable (operator PyExec
). The result is a trajectory of allele
frequencies during evolution.
Example: Execute Python statements during evolution
>>> import simuPOP as sim
>>> simu = sim.Simulator(sim.Population(100, loci=1),
... rep=2)
>>> simu.evolve(
... initOps=[
... sim.InitSex(),
... sim.InitGenotype(freq=[0.2, 0.8]),
... sim.PyExec('traj=[]')
... ],
... matingScheme=sim.RandomMating(),
... postOps=[
... sim.Stat(alleleFreq=0),
... sim.PyExec('traj.append(alleleFreq[0][1])'),
... ],
... gen=5
... )
(5, 5)
>>> # print Trajectory
>>> print(', '.join(['%.3f' % x for x in simu.dvars(0).traj]))
0.775, 0.790, 0.760, 0.750, 0.750
now exiting runScriptInteractively...
Evaluate and output Python expressions (operator PyEval
)¶
Operator PyEval
evaluate a given Python expression in a population’s
local namespace and output its return value. This operator has been widely used
(e.g. Example simple_example, ancestralPop, applicableGen and output) to output statistics of populations and report progress.
Two additional features of this operator may become handy from time to time.
First, an optional Python statements (parameter stmts) can be specified which
will be executed before the expression is evaluated. Second, the population
being applied can be exposed in its own namespace as a variable (parameter
exposePop). This makes it possible to access properties of a population other
than its variables. Example PyEval demonstrates both features.
In this example, two statements are executed to count the number of unique
parents in an offspring population and save them as variables numFather
and
numMother
. The operator outputs these two variables alone with a generation
number.
Example: Evaluate a expression and statements in a population’s local namespace.
>>> import simuPOP as sim
>>> pop = sim.Population(1000, loci=1,
... infoFields=['mother_idx', 'father_idx'])
>>> pop.evolve(
... initOps=sim.InitSex(),
... matingScheme=sim.RandomMating(ops=[
... sim.MendelianGenoTransmitter(),
... sim.ParentsTagger(),
... ]),
... postOps=[
... sim.Stat(alleleFreq=0),
... sim.PyEval(r'"gen %d, #father %d, #mother %d\n"' \
... ' % (gen, numFather, numMother)',
... stmts="numFather = len(set(pop.indInfo('father_idx')))\n"
... "numMother = len(set(pop.indInfo('mother_idx')))",
... exposePop='pop')
... ],
... gen=3
... )
gen 0, #father 439, #mother 433
gen 1, #father 433, #mother 432
gen 2, #father 449, #mother 420
3
now exiting runScriptInteractively...
Note that the function form of this operator (pyEval
) returns the result
of the expression rather than writting it to an output.
Expression and statement involving individual information fields (operator InfoEval
and InfoExec
) *¶
Operators PyEval
and PyExec
work at the population level,
using the local namespace of populations. Operator InfoEval
and
InfoExec
, on the contraray, work at the individual level, using
individual information fields (and population variables) as variables. In this
case, individual information fields are copied to the population namespace one
by one before expression or statements are executed for each individual.
Optionally, the individual object can be exposed to these namespace using a
user-specified name (parameter exposeInd). Individual information fields will
be updated if the value of these fields are changed.
Operator InfoEval
evaluates an expression and outputs its value.
Operator InfoExec
executes one or more statements and does not produce
any output. Operator InfoEval
is usually used to output individual
information fields and properties in batch mode. It is faster and sometimes
easier to use than corresponding for loop plus individual level operations. For
example
InfoEval
(r'''%.2f\\t'' % a'
) outputs the value of information field a for all individuals, separated by tabs.InfoEval
('ind.sexChar()', exposeInd='ind'
) outputs the sex of all individuals using an exposed individual objectind
.InfoEval
('a+b**2'
) outputs \(a+b^{2}\) for information fields \(a\) and \(b\) for all individuals.
Example InfoEval demonstrates the use of this operator.
Example: Evaluate expressions using individual information fields
>>> import simuPOP as sim
>>> import random
>>> pop = sim.Population(20, loci=1, infoFields='a')
>>> pop.setVirtualSplitter(sim.InfoSplitter('a', cutoff=[3]))
>>> sim.initGenotype(pop, freq=[0.2, 0.8])
>>> pop.setIndInfo([random.randint(2, 5) for x in range(20)], 'a')
>>> sim.infoEval(pop, 'a', subPops=[(0, 0)]);print(' ')
2.02.02.02.0
>>> sim.infoEval(pop, 'ind.allele(0, 0)', exposeInd='ind');print(' ')
11011111111100111111
>>> # use sim.population variables
>>> pop.dvars().b = 5
>>> sim.infoEval(pop, '"%d " % (a+b)');print(' ')
8 9 10 8 9 10 8 9 10 10 9 7 9 7 9 7 9 7 9 8
now exiting runScriptInteractively...
Operator InfoExec
is usually used to set individual information fields.
For example
InfoExec
('age += 1'
) increases the age of all individuals by one.InfoExec
('risk = 2 if packPerYear > 10 else 1.5'
) sets information fieldrisk
to2
ifpackPerYear
is greater than10
, and1.5
otherwise. Note that conditional expression is only available for Python version 2.5 or later.InfoExec
('a = b*c'
) sets the value of information fielda
to the product ofb
andc
.
Example InfoExec demonstrates the use of this operator, using
its function form infoExec
.
Example: Execute statements using individual information fields
>>> import simuPOP as sim
>>> pop = sim.Population(100, loci=1, infoFields=['a', 'b', 'c'])
>>> sim.initSex(pop)
>>> sim.initGenotype(pop, freq=[0.2, 0.8])
>>> sim.infoExec(pop, 'a=1')
>>> print(pop.indInfo('a')[:10])
(1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
>>> sim.infoExec(pop, 'b=ind.sex()', exposeInd='ind')
>>> print(pop.indInfo('b')[:10])
(2.0, 2.0, 1.0, 1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0)
>>> sim.infoExec(pop, 'c=a+b')
>>> print(pop.indInfo('c')[:10])
(3.0, 3.0, 2.0, 2.0, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0)
>>> pop.dvars().d = 5
>>> sim.infoExec(pop, 'c+=d')
>>> print(pop.indInfo('c')[:10])
(8.0, 8.0, 7.0, 7.0, 7.0, 7.0, 7.0, 8.0, 8.0, 8.0)
>>> # the operator can update population variable as well
>>> sim.infoExec(pop, 'd+=c*c')
>>> print(pop.dvars().d)
5835.0
now exiting runScriptInteractively...
Note that a statement can also be specified for operator InfoEval
,
which will be executed before an expression is evaluated.
Using functions in external modules in simuPOP expressions and statements¶
All simuPOP expressions and statements are evaluated in a population’s local
namespace, which is a dictionary with no access to external modules. If you
would like to use external modules (e.g. functions from the random
module),
you will have to import them to the namespace explicitly, using something like
exec('import random', pop.vars(), pop.vars())
before you evolve the population.
Example outputByInterval demonstrates the application
of this technique. This example imports the time
module in the population’s
local namespace and set init_time
and last_time
before evolution. During
evolution, anIfElse
operator is used to output the status of the
simulation for every 5 seconds using expression time.time() - last_time > 5
.
last_time
is reset using the PyExec
operator. The evolution will
last 20 seconds and be terminated by the Terminator with expression
time.time() - init_time > 20.
Example: Write the status of an evolutionary process every 10 seconds
>>> import simuPOP as sim
>>> import time
>>> pop = sim.Population(1000, loci=10)
>>> pop.dvars().init_time = time.time()
>>> pop.dvars().last_time = time.time()
>>> exec('import time', pop.vars(), pop.vars())
>>> pop.evolve(
... initOps=sim.InitSex(),
... matingScheme=sim.RandomMating(),
... postOps=[
... sim.IfElse('time.time() - last_time > 5', [
... sim.PyEval(r'"Gen: %d\n" % gen'),
... sim.PyExec('last_time = time.time()')
... ]),
... sim.TerminateIf('time.time() - init_time > 20')
... ]
... )
Gen: 5043
Gen: 9971
Gen: 14997
19925
>>>
now exiting runScriptInteractively...