Fwd: Re: [Tutor] migration simulation

Magnus Lycka magnus@thinkware.se
Sat Dec 14 20:39:01 2002


>From: jpollack@socrates.Berkeley.EDU
>To: Magnus Lycka <magnus@thinkware.se>
>Subject: Re: [Tutor] migration simulation

Hi Joshua, I suggest that we stay on the tutor list.

>Magnus:
>
>Thanks so much for the clear and lucid commentary.  I am indeed very new
>to python and, as you could tell, programming in general.  I'm doing my
>Phd in genetics, though, and it requires some powerful tools to deal with
>genomic information.  So I decided to try to learn a programming language
>and python was what it ended up being.

A wise choise I think. Have you had a look at:
http://www.thinkware.se/cgi-bin/thinki.cgi/PythonInScience ?

What you need might be written already... :)

>In answer to your earlier questions, I chose numbers to represent people
>because, when this is all done, each individual will be binary in nature,
>either they have a certain genetic marker, or didn't.  (I changed to 1-5
>in the practice simulation just so I could keep track easier...)
>So, at somepoint, there will be a bunch of subpopulations all filled with
>0's and 1's.  What I'm interested in is the stochasticity of migration and
>genetic drift and how quickly the overall levels of a gene or lack thereof
>change over time.  Is there a better method, do you think?

If the persons are instances of a class, you can give them attributes
that exactly match your needs. Due to Pythons dynamic nature, you can
make a very simple class:

class Person:
     pass

p1 = Person()
p1.population = 'sf'
p1.marker = 1

print p1.poulation
sf
print p1.marker
1

But be aware that if you do

l = [p1, p1, p1, p1]

you will have a four element list with THE SAME person four
times, not four identical persons. I.e.

l[2].population = 'la'

for p in l:
     print p.population
la
la
la
la

You don't need to be more OO than that. The rest of the
program can look more or less as it does today.

You would probably benefit from a slightly more advanced
class though.

class Person:
     def __init__(self, population, marker):
         self.population = population
         self.marker = marker
     def migrate(self, population):
         self.population = population
     def __str__(self):
         return str(self.marker)

p1 = Person('sf', 5)
print p1
5
print p1.population
sf
p1.migrate('la')
print p1.population
la

>Lastly, I'd thought about immediately migrating individuals, but I think
>the intermediary storage is a better idea.  Isn't it the case that, with
>immediate migration, an individual would stand a chance of migrating
>twice?  (i.e.- lands in a list in a subsequen forloop).

You are absolutely correct, but that's easy to avoid. Loop
over a copy of the original population instead of looping
over the dynamic list. But that requires that you copy all
groups before the first possible migration.

The simplest way to copy a list l is to write

copy_of_l = l[:]

This means that you make a slice from the first to the last element.

all = [sf, la, biloxi]
all_copy = [x[:] for x in all] # <= So called list comprehension
for pop_copy in all_copy:
     ...

Also, you still have the option to to have one list per
population. One list for people migrating from 'la', one for
people migrating from 'sf' and so on. When you pick people
from these lists, you can make sure you don't put them back
where they came from.

And you have the OO solution...

>That I don't
>want.  Eventually, the code will be part of a bigger for loop that will
>represent generational migration.
>
>All that said, I've been interested in classes and read a couple of things
>describing them, but I can't seem to get my head around OOP very well.  Do
>you have any suggestions?  Thanks for all your help.

I had been programming for many years before I approached OOP,
and it took quite a while before I understood what it was about.
Although, in those days, I had neither Python nor Tutor... ;)

Could you follow the code I wrote before?

Does anyone else have suggestions for good OOP introductions?

I guess OO isn't really such a big thing.

You store code and data that are related to each other together
in a class.

In this case you have Persons with certain characteristics, certain
attributes, such as what population the belong to, and what certain
genetic marker they have. They also have certain behaviour, such as
the ability to migrate and the ability to prosent themselves. These
data and capabilities of Persons should be collected in a Person
class.

You also have Populations which should have the ability to present
a summary of what they contain and to list the persons that belong
to each population and so on. It's really an open question here
whether to have a class describing one Population that you instanciate
one of per population, or a class that keep track of all populations
that you only instanciate one of. I choose the latter in my previous
mail.

Python has the 'class' construct to keep track of these kinds of
things. A class defines the things that are common for all instances
of a class.

For instance, let's imagine that you have a class Greeter that is
able to greet people in what ever way you like.

 >>> class Greeter:
...     def __init__(self, greeting='Hello'):
...             self.greeting = greeting
...     def to(self, person):
...             print self.greeting, person
...
 >>> hello = Greeter()
 >>> hello.to('John')
Hello John
 >>> hi = Greeter('Hi')
 >>> hi.to('Graham')
Hi Graham
 >>> hello.to('Terry')
Hello Terry
 >>> hi.to('Eric')
Hi Eric

An instance will be created when you call the class as if it
was a function. This call will return an instance object. All
functions in a class, called methods, take the instance object
(a new in the case of __init__ and the one that makes the
invokation for the methods) as its first parameter (typically
called self), and then whatever other parameters you say.
Constructs like "self.x = 5" in the methods means that you
attach an attribute to the instance object that will stay
there even after the method has finished (in contrast to
local variables in the method/function that will disappear
when the function they werw created in ends).

In other words...

 >>> class Greeter:
Let's define a class called "Greeter"
...     def __init__(self, greeting='Hello'):
When we call a class, it's .__init__() method is invoked
to control what happens on instance creation. __init()
takes a parameter that we call "greeting" on it's creation.
If "greeting" is not provided, use default value 'Hello'.

...             self.greeting = greeting
Then attach given or default value of "greeting" to the
instance attribute .greeting.

...     def to(self, person):
Then add a method called to, that takes a parameter "person"

...             print self.greeting, person
which prints the instance attribute .greeting followed by
the value of the variable person.

...
 >>> hello = Greeter()
Create an instance with the default .greeting 'Hello'.
Let's refer to it with a variable called hello.

Pseudocode explaining what "hello = Greeter()" means
could look like this.

hello = <new instance of class Greeter>
Greeter.__init__(hello)

 >>> hello.to('John')
Short-cut for "Greeter.to(hello, 'John')" or more
generally "hello's class.to(hello, 'John')". I.e.
call hello's .to()-method with parameter 'John'
 >>> hi = Greeter('Hi')
Create another Greeter instance with .greeting
attribute set to 'Hi'.

 >>> hi.to('Graham')
This will then give a different greeting when we call it's
.to() method. "Hi Graham".

 >>> hello.to('Terry')
The first variable, hello, still has the greeting 'Hello'!
There attributes are tied to instances, not shared in the
class.

There is much more to classes that this, but this is the
foundation. With this kind of constructs, you can create
"intelligent" data types. Thus you can make smart object
that can carry whatever attributes/values you like, and
also has built-in behaviour, so that it knows how to
behave.

For now, I guess it's the attributes that will be useful
for you, but organizing code in classes will make larger
programs more easy to write and maintain as you organize
things that have to do with each other in a more logical
(and local) way.

Some OO Heuristics:

A class should capture one and only one key abstraction.

Keep related data and behaviour in one place.

Users of a class must depend on its public interface, but
a class should not depend on it's users.

Distribute system intelligence horizontally as uniformly
as possible.


/Magnus

Just tasted Laphroaig 30 y.o, Laphroaig 10 y.o. cast strength
and Bowmore 30 y.o. :)


-- 
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