[Tutor] migration simulation

Magnus Lycka magnus@thinkware.se
Sat Dec 14 07:59:01 2002


At 20:02 2002-12-13 -0800, Joshua Pollack wrote:
>## Create subpopulations
>sf=[0]*5+[1]*5
>la=[2]*5+[3]*5
>biloxi=[4]*5+[5]*5

It's a bit strange that you use integers to represent individuals.
And later you confuse things a bit because of that it seems.

Why did you make that choise. What does the numbers represent?

>migrants=[]
>
>## Join subpops into single pop
>all=[sf,la,biloxi]
>print "pre-migrate:", all
>
>## Pull out migrants at a probability of 5%
>for i in all:
>     for j in i:
>         if random.random()<=.05:
>             migrants.append(i.pop(j))

Here you make several mistakes I think.

First of all, you don't use descriptive variable names,
and that is always bad. You should realize that i and j
are ususally used for index-values, but Python for loops
operate over elements in sequences. Let's rename i and j.

While we're at it, let's make the magic number explicit.
It's much, much better to place descriptions in names that
the programs use than in comments that the program ignores.

migrationRate = 0.05
for population in all:
     for person in population:
         if random.random()<=migrationRate:
             migrants.append(population.pop(person))

Now we see another mistake clearly. The list.pop() method
takes an integer that indicates the index of the element to
pop from the list. But that's not what we feed it. The number
we feed it indicates a person, not a position in the list,
right? This confusion would never have happened with another
type than integer to represent individuals.

migrationRate = 0.05
for population in all:
     for person in population:
         if random.random()<=migrationRate:
             migrants.append(population.pop(
                               population.index(person)))

Now you will pick out the integer you were working with
in the for loop, not the one located at the position that
the number of the current person indicated. (If you don't
see what I mean, pop up migrationRate to 0.95 and change
"biloxi=[4]*5+[5]*5" to "biloxi=[4]*5+[9]*5" as see what
happens with your old code.

We still have a problem though, your original problem. We
just mixed individuals of all populations in one big list,
and they don't have any passports.

One solution would be to have one list of migrants for each
population they come from (a list of lists, like "all"), and
to make sure that you don't put them back in the population
with the same index in all as the one of the list.

A second solution could be not to store them in any inter-
mediate storage at all, but to immediately pick out a new
population among all but the one they came from (I guess
you have to pick out a random population in a loop until
you find one that's not the same as original population
(which you should know at this point in time).

A third solution would be to make the persons a bit more
full features than just integers. But maybe it's a bit to
early in your learning to introduce classes and object-
oriented programming? (The code below doesn't do quite the
same as your code, but it might give some hints on a
different approach...

import random
migrateRate = 0.05

class Populations:
     def __init__(self):
         self.persons = []

     def add(self, person):
         self.persons.append(person)

     def write(self):
         self.persons.sort()
         thisPop = None
         popCnt = 0
         for person in self.persons:
             if person.population != thisPop:
                 if popCnt:
                     print popCnt
                 popCnt = 0
                 thisPop = person.population
                 print thisPop,
             print '.',
             popCnt += 1
         print popCnt

class Person:

     def __init__(self, population):
         self.population = population

     def migrate(self, population):
         self.population = population

     def __cmp__(self, other):
         return cmp(self.population, other.population)

popList = Populations()

popNames = ('sf', 'la', 'biloxi')
popSizes = (10, 10, 10)

for popName, popSize in zip(popNames, popSizes):
     for i in range(popSize):
         popList.add(Person(popName))
popList.write()

for person in popList.persons:
     if random.random() < migrateRate:
         while 1:
             newPop = random.choice(popNames)
             if newPop != person.population:
                 break # Found a good new population
         person.migrate(newPop)
popList.write()




-- 
Magnus Lycka, Thinkware AB
Alvans vag 99, SE-907 50 UMEA, SWEDEN
phone: int+46 70 582 80 65, fax: int+46 70 612 80 65
http://www.thinkware.se/  mailto:magnus@thinkware.se