This notebook was put together by Morgan Fouesneau for UW's [Astro 599](http://www.astro.washington.edu/users/vanderplas/Astr599/) course. Source and license info is on [GitHub](https://github.com/jakevdp/2013_fall_ASTR599/).
-- M. Fouesneau [ morgan.fouesneau@astro.washington.edu ]
Astro599 ( http://www.astro.washington.edu/users/vanderplas/Astr599/schedule )
Prezi presentation is available at: http://bit.ly/18FylbC
In [1]:
# make sure pylab runs inline when using the notebook
%pylab inline
import numpy as np
import pylab as plt
In [2]:
def ezrc(fontSize=22., lineWidth=2., labelSize=None, tickmajorsize=10, tickminorsize=5):
"""
slides - Define params to make pretty fig for slides
"""
from pylab import rc, rcParams
if labelSize is None:
labelSize = fontSize + 5
rc('figure', figsize=(9, 7))
#rc('figure', figsize=(8, 6))
rc('lines', linewidth=lineWidth)
rcParams['grid.linewidth'] = lineWidth
rc('font', size=fontSize, family='serif', weight='small')
rc('axes', linewidth=lineWidth, labelsize=labelSize)
rc('legend', borderpad=0.1, markerscale=1., fancybox=False)
#rc('text', usetex=True)
rc('image', aspect='auto')
#rc('ps', useafm=True, fonttype=3)
rcParams['xtick.major.size'] = tickmajorsize
rcParams['xtick.minor.size'] = tickminorsize
rcParams['ytick.major.size'] = tickmajorsize
rcParams['ytick.minor.size'] = tickminorsize
ezrc(20) # making figures cleaner for slides
Text is object
In [3]:
s = 'some text'
In [4]:
s.upper()
Out[4]:
Modules are objects
In [5]:
import numpy as np
np
Out[5]:
In [6]:
type(np)
Out[6]:
In [7]:
np.__name__
Out[7]:
In [8]:
np.__class__
Out[8]:
In [9]:
print np.__dict__.keys() # we'll see __dict__ later
Matplotlib of course
In [10]:
import pylab as plt
plt.plot(range(10))
Out[10]:
[<matplotlib.lines.Line2D at 0x30c3e90>]
plot returns an instance of a Line2D object
In [11]:
import pylab as plt
r = []
r.append( plt.plot(range(10)) )
r.append( plt.xlabel('measured') )
r.append( plt.ylabel('calculated') )
r.append( plt.title('Measured vs. calculated') )
r.append( plt.grid(True) )
print r
In [12]:
import pylab as plt
# ------- make a figure ----------------------------
plt.plot(range(10))
plt.grid(True)
# ------- access objects and change them ------------
ax = plt.gca() # gca == get current axis
line = ax.lines[0]
line.set_marker('o')
line.set_markersize(20)
line.set_linewidth(2)
plt.setp(line, color='g') # set property
#change the figure style
plt.tick_params(which='both', width=2)
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
for k, sk in ax.spines.items():
if k in ('top', 'right'):
sk.set_color('None')
else:
sk.set_position(('outward', 20))
# add labels
plt.xlabel('measured')
plt.ylabel('calculated')
plt.title('Measured vs. calculated')
Out[12]:
The simplest form of class definition looks like this:
In [13]:
class DummyClass:
pass
Class objects support two kinds of operations: attribute references and methods.
Attribute references use the standard syntax used for all attribute references in Python: obj.name
.
So, a class definition looked like this:
In [14]:
class DummyClass:
"""A simple example class"""
i = 12345
def f(self):
return 'hello world'
then DummyClass.i
and DummyClass.f
are valid attribute references, returning an integer and a function object, respectively.
When a class defines an __init__()
method, class instantiation automatically invokes __init__()
for the newly-created class instance. So in this example, a new, initialized instance can be obtained by:
In [15]:
class Vector(object):
def __init__(self):
pass
Note: Often, the first argument of a method is called self
.
This is nothing more than a convention: the name self has absolutely no special meaning to Python.
Note, however, that by not following the convention your code may be less readable to other Python programmers, and it is also conceivable that a class browser program might be written that relies upon such a convention.
In [16]:
class Vector(object):
def __init__(self, x,y,z):
""" constructor """
pass
def sum(self, p):
""" addition: p1+p2 """
pass
def scalarProd(self,p):
""" A.B = sum(a[k].b[k]) """
pass
A solution:
In [ ]:
# your solution here
In [17]:
x = Vector(1, 0, 0)
y = Vector(0, 1, 0)
z = Vector(0, 0, 1)
print "x.y = ", x.scalarProd(y)
print x
object.__repr__(self)
Called by the repr()
built-in function and by string conversions (reverse quotes) to compute the "official" string representation of an object.
object.__str__(self)
Called by the str()
built-in function and by the print statement to compute the "informal" string representation of an object.
In [ ]:
class Vector(object):
# your previous solution
def __str__(self):
# your solution here
In [ ]:
x = Vector(1, 0, 0)
y = Vector(0, 1, 0)
z = Vector(0, 0, 1)
print "x.y = ", x.scalarProd(y)
print x
What about checking that $x^2 + y^2 + 2\cdot x \cdot y = (x + y) ^ 2$
In [ ]:
x.scalarProd(x) + y.scalarProd(y)
x.scalarProd(x) + y.scalarProd(y) + 2 * x.scalarProd(y) == x.sum(y).scalarProd(x.sum(y))
In [ ]:
x ** 2 + y ** 2 + 2 * x * y == (x + y) ** 2
Implement common operators __add__
, __sub__
, __mul__
, ...
see http://docs.python.org/2/reference/datamodel.html
In [ ]:
# put the new version of your class here
In [ ]:
x = Vector(1, 0, 0)
y = Vector(0, 1, 0)
z = Vector(0, 0, 1)
xy = x * y
print "x.y = ", xy
print x
x * x + y * y + 2 * x * y == (x + y) ** 2
In [ ]:
#code here
This section is a very quick introduction to a very pythonic coding process: decorations
A decorator is just a callable object that takes a function as an argument and returns a replacement function. We’ll start simply and work our way up to useful decorators. In other words: a function that writes a function of a function
Look carefully through our decorator example below. We defined a function named outer
that has a single parameter some_func
. Inside outer
we define an nested function named inner
.
Since Python 2.4, you can wrap a function in a decorator by pre-pending the function definition with a decorator name and the @
symbol. In the code samples below above we decorated our function using this syntax, however it’s important to recognize that this is no different than simply replacing the original variable with the return from the wrapper function- Python just adds some syntactic sugar to make what is going on very explicit.
In [18]:
def outer(some_func):
def inner():
print "before some_func"
ret = some_func() # 1
return ret + 1
return inner
@outer
def foo():
return 1
# transparently equivalent to
# foo = outer(foo)
foo()
Out[18]:
Examples of 2 decorators:
In [19]:
import collections
import functools
from functools import wraps
import time, math, sys
class timeit(object):
""" Time a block of your code.
Decorator and context manager
"""
def __init__(self, f=None, verbose=True, text=None):
self.f = f
if not self.f is None:
if type(self.f) != str:
functools.update_wrapper(self, f)
self.text = self.__name__
else:
self.text = f
else:
self.text = text or ''
self.verbose = verbose
def __enter__(self):
print "Timing %s" % (self.text)
self.start = time.time()
def __exit__(self, exc_type, exc_val, exc_tb):
self.stop = time.time()
print self.time
def __pretty_print(self, t):
units = [u"s", u"ms",u'us',"ns"]
scaling = [1, 1e3, 1e6, 1e9]
if t > 0.0 and t < 1000.0:
order = min(-int(math.floor(math.log10(t)) // 3), 3)
elif best >= 1000.0:
order = 0
else:
order = 3
return "%s Execution time: %.3g %s" % (self.text, t * scaling[order], units[order])
@property
def time(self):
return self.__pretty_print(self.stop-self.start)
def __call__(self, *args, **kwargs):
self.start = time.time()
r = self.f(*args, **kwargs)
self.stop = time.time()
if self.verbose:
print self.time
return r
In [20]:
class memoize(dict):
'''Decorator that caches a function's return
value each time it is called. If called
later with the same arguments, the cached
value is returned (not reevaluated).
'''
def __init__(self, func):
self.func = func
functools.update_wrapper(self, func)
def __getitem__(self, *key):
return dict.__getitem__(self, key)
def __missing__(self, key):
ret = self[key] = self.func(*key)
return ret
__call__ = __getitem__
def __repr__(self):
'''Return the function's docstring.'''
return self.func.__doc__
In [21]:
@timeit
@memoize
def f(*args, **kwargs):
import time
time.sleep(5)
return (args, kwargs)
In [22]:
f(10)
f(10)
Out[22]:
In [ ]:
class IMF(object):
mass = [0.1, 120.]
class Salpeter(IMF):
pass
In [ ]:
f = Salpeter()
print f.mass
Execution of a derived class definition proceeds the same as for a base class. When the class object is constructed, the base class is remembered. This is used for resolving attribute references: if a requested attribute is not found in the class, the search proceeds to look in the base class. This rule is applied recursively if the base class itself is derived from some other class.
There’s nothing special about instantiation of derived classes: Salpeter()
creates a new instance of the class. Method references are resolved as follows: the corresponding class attribute is searched, ascending up the chain of parent classes if necessary, and the method reference is valid if this yields a function object.
Derived classes may override methods of their base classes.
An overriding method in a derived class may in fact want to extend rather than simply replace the base class method of the same name. There is a simple way to call the base class method directly: just call IMF.methodname(self, *args, **kwargs)
. It is common to call the parent constructor in the derived class constructor:
def Salpeter(IMF):
def __init__(self, *args, **kwargs):
IMF.__init__(self, *args, **kwargs)
Note that the above example is equivalent to doing nothing special and thus can be omitted (it is the implicit definition from inheritance)
due Monday, October 14th
Similarly to last week assignment
Create a new notebook or a copy of the current one, and call it HW2.ipynb
,
complete this part, embed resulting figures in the notebook. Comment your code: each function/method must have at least one line of description.
jakevdp/ASTR599_homework
. (see Astro599 website for help)The requirements are:
Below is a template of IMF class. Feel free to adapt.
In [ ]:
class IMF(object):
"""
IMF object class
let you define an IMF as multiple power-laws.
attributes:
norm: norm of the function
"""
def __init__(self, *args, **kwargs):
"""
"""
pass
def get_enclosed_mass(self, Mmin, Mmax):
"""Get the enclosed mass over a given mass range.
"""
pass
def get_enclosed_Nstar(self, Mmin, Mmax):
"""Get the enclosed dN over a given mass range
"""
pass
def get_avg_mass(self, Mmin, Mmax):
""" get the avg mass over a given range """
pass
def getValue(self, m):
""" returns the value of the normalized IMF at a given mass m:
note: m can be an iterable
"""
pass
def random(self, N, massMin=None, massMax=None):
""" Draw samples from this distribution
Samples are distributed over the interval [massMin, massMax]
Interval is truncated to the IMF range definition if it extents beyond it.
(taken as is otherwise)
"""
pass
def __call__(self, m=None):
return self.getValue(m)
Plot at least a couple of mass functions on the same figure
In [ ]:
#your code here
Draw a random sample of N masses from one mass function and show that the sample follows the desired distribution
In [ ]:
#code here