DS Data manipulation, analysis and visualisation in Python
December, 2019© 2016, Joris Van den Bossche and Stijn Van Hoey (mailto:jorisvandenbossche@gmail.com, mailto:stijnvanhoey@gmail.com). Licensed under CC BY 4.0 Creative Commons
This notebook is largely based on material of the Python Scientific Lecture Notes (https://scipy-lectures.github.io/), adapted with some exercises.
Function blocks must be indented as other control-flow blocks
In [1]:
def the_answer_to_the_universe():
print(42)
the_answer_to_the_universe()
Note: the syntax to define a function:
Functions can optionally return values
In [2]:
def calcAreaSquare(edge):
return edge**2
calcAreaSquare(2.3)
Out[2]:
Mandatory parameters (positional arguments)
In [3]:
def double_it(x):
return 2*x
double_it(3)
Out[3]:
In [4]:
#double_it()
Optional parameters (keyword or named arguments)
The order of the keyword arguments does not matter, but it is good practice to use the same ordering as the function's definition
Keyword arguments are a very convenient feature for defining functions with a variable number of arguments, especially when default values are to be used in most calls to the function.
In [5]:
def double_it (x=1):
return 2*x
print(double_it(3))
In [6]:
print(double_it())
In [7]:
def addition(int1=1, int2=1, int3=1):
return int1 + 2*int2 + 3*int3
print(addition(int1=1, int2=1, int3=1))
In [8]:
print(addition(int1=1, int3=1, int2=1)) # sequence of these named arguments do not matter
Using an immutable type in a keyword argument:
In [4]:
bigx = 10
def double_it(x=bigx):
return x * 2
bigx = 1e9
double_it()
Out[4]:
Using an mutable type in a keyword argument (and modifying it inside the function body)
In [5]:
def add_to_dict(args={'a': 1, 'b': 2}):
for i in args.keys():
args[i] += 1
print(args)
In [6]:
add_to_dict
add_to_dict()
add_to_dict()
add_to_dict()
In [7]:
#the {'a': 1, 'b': 2} was created in the memory on the moment that the definition was evaluated
In [8]:
def add_to_dict(args=None):
if not args:
args = {'a': 1, 'b': 2}
for i in args.keys():
args[i] += 1
print(args)
In [9]:
add_to_dict
add_to_dict()
add_to_dict()
Special forms of parameters:
In [10]:
def variable_args(*args, **kwargs):
print('args is', args)
print('kwargs is', kwargs)
variable_args('one', 'two', x=1, y=2, z=3)
Documentation about what the function does and its parameters. General convention:
In [11]:
def funcname(params):
"""Concise one-line sentence describing the function.
Extended summary which can contain multiple paragraphs.
"""
# function body
pass
funcname?
Functions are first-class objects, which means they can be:
In [12]:
va = variable_args
va('three', x=1, y=2)
Methods are functions attached to objects. You’ve seen these in our examples on lists, dictionaries, strings, etc...
Calling them can be done by dir(object):
In [13]:
dd = {'antea': 3, 'IMDC': 2, 'arcadis': 4, 'witteveen': 5, 'grontmij': 1, 'fluves': 6, 'marlinks': 7}
In [14]:
def check_for_key(checkdict, key):
"""
Function checks the presence of key in dictionary checkdict and returns an
exception if the key is already used in the dictionary
"""
if key in checkdict.keys():
raise Exception('Key already used in this dictionary')
In [15]:
check_for_key(dd, 'deme')
In [16]:
#check_for_key(dd, 'antea') # uncomment this line
Wondering what OO is? A very nice introduction is given here: http://py.processing.org/tutorials/objects/
Python supports object-oriented programming (OOP). The goals of OOP are:
Here is a small example: we create a Student class, which is an object gathering several custom functions (methods) and variables (attributes), we will be able to use:
In [17]:
class Employee(): #object
def __init__(self, name, wage=60.):
"""
Employee class to save the amount of hours worked and related earnings
"""
self.name = name
self.wage = wage
self.hours = 0.
def worked(self, hours):
"""add worked hours on a project
"""
try:
hours = float(hours)
except:
raise Exception("Hours not convertable to float!")
self.hours += hours
def calc_earnings(self):
"""
Calculate earnings
"""
return self.hours *self.wage
In [18]:
bert = Employee('bert')
In [19]:
bert.worked(10.)
bert.worked(20.)
bert.wage = 80.
In [20]:
bert.calc_earnings()
Out[20]:
In [21]:
dir(Employee)
Out[21]:
It is just the same as all the other objects we worked with!
In [22]:
class Employee(): #object
def __init__(self, name, wage=60.):
"""
Employee class to save the amount of hours worked and related earnings
"""
self.name = name
self.wage = wage
self.projects = {}
def new_project(self, projectname):
"""
"""
if projectname in self.projects:
raise Exception("project already exist for", self.name)
else:
self.projects[projectname] = 0.
def worked(self, hours, projectname):
"""add worked hours on a project
"""
try:
hours = float(hours)
except:
raise Exception("Hours not convertable to float!")
if not projectname in self.projects:
raise Exception("project non-existing for", self.name)
self.projects[projectname] += hours
def calc_earnings(self):
"""
Calculate earnings
"""
total_hours = 0
for val in self.projects.values():
total_hours += val
return total_hours *self.wage
def info(self):
"""
get info
"""
for proj, hour in self.projects.items():
print(hour, 'worked on project', proj)
In [23]:
bert = Employee('bert')
bert.new_project('vmm')
In [24]:
bert.worked(10., 'vmm')
In [25]:
bert.calc_earnings()
Out[25]:
In [26]:
bert.new_project('pwc')
In [27]:
bert.info()
In [28]:
bert.worked(3., 'pwc')