[Tutor] raw_input and converting string to digit

Jeff Shannon jeff@ccvcorp.com
Fri Feb 28 15:44:02 2003


(Apologies if this appears twice -- my mailer seems to be having 
problems.)

Mic Forster wrote:

 >Hi Guys,
 >
 >The purpose of this script is to obtain an expected
 >species-abundance curve given an observed curve. The
 >script works fine for this purpose but it is
 >cumbersome to implement. What I am trying to do is to
 >open this file, enter in a number that represents the
 >fundamental biodiversity number (theta) and another
 >that represents the community size (jM), let the
 >program do its stuff and then give the expected
 >abundance output.
 >
 >I am unsure of the correct syntax to convert a string
 >from raw_input into a digit. Here is what I have (any
 >additional comments on any aspect of this code is more
 >than welcome):
 >

Usually you would use the int() (or possibly long() or float(),
depending on the specific case) built-in function. Each of these
functions will convert a string into a number of the appropriate type.
They will throw an exception if the string doesn't represent a valid
number of that type.

 >>>>def addTheta():
 >>>>
 >>>>
 >	   theta.append(raw_input(Fundamental biodiversity
 >number is: ))
 >

This can simply be:

theta.append( int(raw_input('Fundamental biodiversity number is: ')) )

 >>>>class growList(list):
 >>>>
 >>>>
 >        '''A list that does as in Perl: Grows when
 >        we assign to new indices. I.e. x[1] = 5 will
 >        work if x was previously empty.'''
 >        [...]
 >

 >>>>def nsp():
 >>>>
 >>>>
 >     '''Number of species must be the same as the size
 >of the abundence list (I guess). Note that first
 >position is unused to match the 1-based algorithm
 >syntax.'''
 >        return len(abund) - 1
 >

In this, and in your following code, you spend a lot of work adjusting
by one in order to fit your 1-based algorithm. You'd be better off using
a 0-base -- you can then forget about all of that adjusting, and you're
much less likely to have errors. Your calculation can then be written
like this:

for j in range(1, jM):
x = rnd()
if x < (theta / float(theta + j):
abund.append(1)
else:
cumul[0] = abund[0] / float(j)
for i in range(1, len(abund)):
cumul[i] = cumul[i - 1] + abund[i] / float(j)
i = 0
while x >= cumul[i]:
i += 1
abund[i] += 1

This has the exact same values for everything except the indices,
there's no worrying about adding or subtracting one, etc, etc. Much
simpler and clearer.

Speaking of simpler and clearer, your last, innermost loop ( "while x >=
cumul[i]" ) could be rewritten like this:

for i in range(len(cumul)):
if x < cumul[i]:
abund[i] += 1
break

To my mind, this is a much clearer expression of the intent -- "step
through cumul until we find an item smaller than x, and then add 1 to
the matching element of abund".

You can also make a simple change to one of your loops in order to
remove the need for your special growlist class. You're only adding
elements onto the end of your cumul list (you already use append() for
the abund list), so you can simply special-case the final calculation,
or better yet use try/except:

for i in range(1, len(abund)):
value = cumul[i - 1] + abund[i] / float(j)
try:
cumul[i] = value
except IndexError:
cumul.append(value)

Now you can do away with your growList class, and simply initialize
abund and cumul like this:

abund = [1]
cumul = [0]

And you don't need zip() to print out your results:

for abundance in abund:
print abundance

Jeff Shannon
Technician/Programmer
Credit International