├── .gitignore ├── Wk06 ├── 02-template │ ├── templates │ │ └── index.html │ └── site.py ├── 06-storing-input │ ├── test.db │ ├── templates │ │ └── index.html │ └── site.py ├── 01-framework │ └── site.py ├── 03-html │ ├── templates │ │ └── index.html │ └── site.py ├── 04-uri-attributes │ ├── templates │ │ ├── index.html │ │ └── custom.html │ └── site.py └── 05-user-input │ ├── templates │ ├── custom.html │ └── index.html │ └── site.py ├── Wk07 ├── clustering.png ├── regression.png ├── classification.png ├── feature-selection.png ├── classification-data.png ├── density-estimation.png ├── workspace.py └── model_and_algorithms.py ├── Wk10 ├── clustering.png └── plot_cluster_comparison_001.png ├── Wk05 ├── Wk05-wsgi.py ├── workspace.py ├── ingredient_recipe_interface.py └── Wk05-OOP-Public-interface-Solution.ipynb ├── Wk02 ├── tsp_solver.py ├── tsp_solver_pep8.py ├── tsp_solver_doc.py ├── workspace.py ├── simulated_annealing_optimizer.py └── genetic_algorithm_optimizer.py ├── LICENSE ├── Wk04 ├── Wk04_matplotlib_gui.py ├── Wk04-GUI.ipynb └── Wk04-OOP-Inheritance-abstraction.ipynb ├── Wk03 ├── lpthw_ex43.py ├── cookbook.py └── Wk03-Paradigms.ipynb ├── README.md └── Wk01 ├── Wk01-Overview.ipynb └── .ipynb_checkpoints └── Wk01-Overview-checkpoint.ipynb /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | -------------------------------------------------------------------------------- /Wk06/02-template/templates/index.html: -------------------------------------------------------------------------------- 1 | Hello, World -------------------------------------------------------------------------------- /Wk07/clustering.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brienna/BIOF509/HEAD/Wk07/clustering.png -------------------------------------------------------------------------------- /Wk07/regression.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brienna/BIOF509/HEAD/Wk07/regression.png -------------------------------------------------------------------------------- /Wk10/clustering.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brienna/BIOF509/HEAD/Wk10/clustering.png -------------------------------------------------------------------------------- /Wk07/classification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brienna/BIOF509/HEAD/Wk07/classification.png -------------------------------------------------------------------------------- /Wk07/feature-selection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brienna/BIOF509/HEAD/Wk07/feature-selection.png -------------------------------------------------------------------------------- /Wk07/classification-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brienna/BIOF509/HEAD/Wk07/classification-data.png -------------------------------------------------------------------------------- /Wk07/density-estimation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brienna/BIOF509/HEAD/Wk07/density-estimation.png -------------------------------------------------------------------------------- /Wk06/06-storing-input/test.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brienna/BIOF509/HEAD/Wk06/06-storing-input/test.db -------------------------------------------------------------------------------- /Wk10/plot_cluster_comparison_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brienna/BIOF509/HEAD/Wk10/plot_cluster_comparison_001.png -------------------------------------------------------------------------------- /Wk06/01-framework/site.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | app = Flask(__name__) 4 | 5 | @app.route('/') 6 | def index(): 7 | return 'Hello, World' 8 | 9 | if __name__ == '__main__': 10 | app.run() 11 | -------------------------------------------------------------------------------- /Wk06/03-html/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello 4 | 7 | 8 | 9 | 10 |

Hello, World

11 | 12 | 13 | -------------------------------------------------------------------------------- /Wk06/04-uri-attributes/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello 4 | 7 | 8 | 9 | 10 |

Hello, World

11 | 12 | 13 | -------------------------------------------------------------------------------- /Wk06/05-user-input/templates/custom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello 4 | 7 | 8 | 9 | 10 |

Hello, World

11 | {{ custom }} 12 | 13 | 14 | -------------------------------------------------------------------------------- /Wk06/02-template/site.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template 2 | 3 | app = Flask(__name__) 4 | 5 | @app.route('/') 6 | def index(): 7 | return render_template('index.html') 8 | 9 | if __name__ == '__main__': 10 | app.run(debug=True) 11 | -------------------------------------------------------------------------------- /Wk06/04-uri-attributes/templates/custom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello 4 | 7 | 8 | 9 | 10 |

Hello, World

11 | {{ custom }} 12 | 13 | 14 | -------------------------------------------------------------------------------- /Wk06/03-html/site.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template 2 | 3 | app = Flask(__name__) 4 | 5 | @app.route('/') 6 | def index(): 7 | return render_template('index.html') 8 | 9 | if __name__ == '__main__': 10 | app.run(debug=True) 11 | -------------------------------------------------------------------------------- /Wk06/04-uri-attributes/site.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template 2 | 3 | app = Flask(__name__) 4 | 5 | @app.route('/') 6 | def index(): 7 | return render_template('index.html') 8 | 9 | @app.route('/custom//') 10 | def custom(custom): 11 | return render_template('custom.html', custom=custom) 12 | 13 | if __name__ == '__main__': 14 | app.run(debug=True) 15 | -------------------------------------------------------------------------------- /Wk06/05-user-input/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello 4 | 7 | 8 | 9 | 10 |

Hello, {{ name }}

11 | 12 |
13 | {{ form.hidden_tag() }} 14 | {{ form.name.label }} {{ form.name(size=20) }} 15 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /Wk05/Wk05-wsgi.py: -------------------------------------------------------------------------------- 1 | from wsgiref.simple_server import make_server 2 | 3 | 4 | def simple_app(environ, start_response): 5 | """A simple WSGI application""" 6 | status = '200 OK' 7 | headers = [('Content-type', 'text/plain; charset=utf-8')] 8 | 9 | start_response(status, headers) 10 | 11 | return ["Hello World\n".encode('utf-8')] 12 | 13 | if __name__ == '__main__': 14 | httpd = make_server('', 8000, simple_app) 15 | print("Serving on port 8000...") 16 | httpd.serve_forever() 17 | -------------------------------------------------------------------------------- /Wk02/tsp_solver.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | 3 | def distance(coords): 4 | distance = 0 5 | for p1, p2 in zip(coords[:-1], coords[1:]): 6 | distance += ((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2) ** 0.5 7 | return distance 8 | 9 | def find_best_route(coords): 10 | best_distance = distance(coords) 11 | best = coords 12 | for option in itertools.permutations(coords, len(coords)): 13 | option_distance = distance(option) 14 | if option_distance < best_distance: 15 | best = option 16 | best_distance = option_distance 17 | return (best_distance, best) 18 | -------------------------------------------------------------------------------- /Wk02/tsp_solver_pep8.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | 3 | 4 | def distance(coords): 5 | distance = 0 6 | for p1, p2 in zip(coords[:-1], coords[1:]): 7 | distance += ((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2) ** 0.5 8 | return distance 9 | 10 | 11 | def find_best_route(coords): 12 | best_distance = distance(coords) 13 | best = coords 14 | for option in itertools.permutations(coords, len(coords)): 15 | option_distance = distance(option) 16 | if option_distance < best_distance: 17 | best = option 18 | best_distance = option_distance 19 | return (best_distance, best) 20 | -------------------------------------------------------------------------------- /Wk06/06-storing-input/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello 4 | 7 | 8 | 9 | 10 |

Hello, {{ first_name }}

11 | 12 |
13 | {{ form.hidden_tag() }} 14 | {{ form.first_name.label }} {{ form.first_name(size=20) }} 15 | {{ form.last_name.label }} {{ form.last_name(size=20) }} 16 | 17 |
18 | 19 |

Previous Visitors

20 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Wk06/05-user-input/site.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template 2 | 3 | app = Flask(__name__) 4 | 5 | app.config['SECRET_KEY'] = 'important to keep unknown in production' 6 | 7 | from flask_wtf import Form 8 | from wtforms import StringField 9 | from wtforms.validators import DataRequired 10 | 11 | 12 | 13 | 14 | class MyForm(Form): 15 | name = StringField('name', validators=[DataRequired()]) 16 | 17 | 18 | @app.route('/', methods=('GET', 'POST')) 19 | def index(): 20 | form = MyForm() 21 | if form.validate_on_submit(): 22 | name = form.name.data 23 | else: 24 | name = 'stranger' 25 | return render_template('index.html', form=form, name=name) 26 | 27 | @app.route('/custom//') 28 | def custom(custom): 29 | return render_template('custom.html', custom=custom) 30 | 31 | if __name__ == '__main__': 32 | app.run(debug=True) 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Jonathan Street 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Wk02/tsp_solver_doc.py: -------------------------------------------------------------------------------- 1 | """Module to find the shortest path connecting a set of points 2 | 3 | find_best_route accepts a set of coordinates and will return the shortest 4 | route.""" 5 | import itertools 6 | 7 | 8 | def distance(coords): 9 | """Calculates the distance of a path between multiple points 10 | 11 | Arguments: 12 | coords -- List of coordinates, e.g. [(0,0), (1,1)] 13 | 14 | Returns: Total distance as a float 15 | """ 16 | distance = 0 17 | for p1, p2 in zip(coords[:-1], coords[1:]): 18 | distance += ((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2) ** 0.5 19 | return distance 20 | 21 | 22 | def find_best_route(coords): 23 | """Find the shortest path between a set of points 24 | 25 | Arguments: 26 | coords -- List of coordinates, e.g. [(0,0), (1,1)] 27 | 28 | Returns: Tuple of shortest distance and the route 29 | """ 30 | best_distance = distance(coords) 31 | best = coords 32 | for option in itertools.permutations(coords, len(coords)): 33 | option_distance = distance(option) 34 | if option_distance < best_distance: 35 | best = option 36 | best_distance = option_distance 37 | return (best_distance, best) 38 | -------------------------------------------------------------------------------- /Wk04/Wk04_matplotlib_gui.py: -------------------------------------------------------------------------------- 1 | """ 2 | Do a mouseclick somewhere, move the mouse to some destination, release 3 | the button. This class gives click- and release-events and also draws 4 | a line or a box from the click-point to the actual mouseposition 5 | (within the same axes) until the button is released. Within the 6 | method 'self.ignore()' it is checked wether the button from eventpress 7 | and eventrelease are the same. 8 | 9 | """ 10 | from matplotlib.widgets import RectangleSelector 11 | import matplotlib.pyplot as plt 12 | import matplotlib.cbook as cbook 13 | 14 | 15 | def line_select_callback(eclick, erelease): 16 | 'eclick and erelease are the press and release events' 17 | x1, y1 = eclick.xdata, eclick.ydata 18 | x2, y2 = erelease.xdata, erelease.ydata 19 | print ("(%3.2f, %3.2f) --> (%3.2f, %3.2f)" % (x1, y1, x2, y2)) 20 | print (" The button you used were: %s %s" % (eclick.button, erelease.button)) 21 | 22 | 23 | def toggle_selector(event): 24 | print (' Key pressed.') 25 | if event.key in ['Q', 'q'] and toggle_selector.RS.active: 26 | print (' RectangleSelector deactivated.') 27 | toggle_selector.RS.set_active(False) 28 | if event.key in ['A', 'a'] and not toggle_selector.RS.active: 29 | print (' RectangleSelector activated.') 30 | toggle_selector.RS.set_active(True) 31 | 32 | 33 | 34 | image_file = cbook.get_sample_data('grace_hopper.png') 35 | image = plt.imread(image_file) 36 | fig, current_ax = plt.subplots() 37 | plt.imshow(image) 38 | toggle_selector.RS = RectangleSelector(current_ax, 39 | line_select_callback, 40 | drawtype='box', useblit=True, 41 | button=[1,3], # don't use middle button 42 | minspanx=5, minspany=5, 43 | spancoords='pixels') 44 | plt.connect('key_press_event', toggle_selector) 45 | plt.show() 46 | -------------------------------------------------------------------------------- /Wk05/workspace.py: -------------------------------------------------------------------------------- 1 | from ingredient_recipe_interface import Ingredient, Recipe 2 | 3 | 4 | bread = Recipe('Bread', [(820, Ingredient('Flour', 0.77, 0.10, 0.01)), 5 | (30, Ingredient('Oil', 0, 0, 1)), 6 | (36, Ingredient('Sugar', 1, 0, 0)), 7 | (7, Ingredient('Yeast', 0.3125, 0.5, 0.0625)), 8 | (560, Ingredient('Water', 0, 0, 0))]) 9 | print(bread.ingredients) 10 | # Should be roughly [(820, Ingredient(Flour, 0.77, 0.1, 0.01)), (30, Ingredient(Oil, 0, 0, 1)), 11 | # (36, Ingredient(Sugar, 1, 0, 0)), (7, Ingredient(Yeast, 0.3125, 0.5, 0.0625)), (560, Ingredient(Water, 0, 0, 0))] 12 | 13 | print(bread.nutrition) 14 | #Should be roughly {'carbs': 669.5875, 'protein': 85.5, 'fat': 38.6375} the order is not important 15 | 16 | #Points to note: 17 | # - The different call to Ingredient, you can use isinstance or type to change the 18 | # behaviour depending on the arguments supplied 19 | # - Cholesterol as an extra nutrient, your implementation should accept any nutrient 20 | # - Use of Recipe (bread) as an ingredient 21 | basic_french_toast = Recipe('Basic French Toast', [(300, Ingredient('Egg', {'carbs': 0.0077, 'protein': 0.1258, 22 | 'fat': 0.0994, 'cholesterol': 0.00423})), 23 | (0.25, bread)]) 24 | print(basic_french_toast.ingredients) 25 | # Should be roughly: 26 | # [(300, Ingredient(Egg, 0.0077, 0.1258, 0.0994)), (0.25, Recipe(Bread, [(820, Ingredient(Flour, 0.77, 0.1, 0.01)), 27 | # (30, Ingredient(Oil, 0, 0, 1)), (36, Ingredient(Sugar, 1, 0, 0)), (7, Ingredient(Yeast, 0.3125, 0.5, 0.0625)), 28 | # (560, Ingredient(Water, 0, 0, 0))]))] 29 | # Note the formatting for the Recipe object, a __repr__ method will be needed 30 | 31 | print(basic_french_toast.nutrition) 32 | # Should be roughly {'protein': 59.115, 'carbs': 169.706875, 'cholesterol': 1.2690000000000001, 'fat': 39.479375000000005} 33 | # The order is not important -------------------------------------------------------------------------------- /Wk07/workspace.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | from model_and_algorithms import Model, RCT, EpsilonGreedy, EpsilonDecrease 3 | 4 | 5 | # plot RCT algorithm 6 | model = Model(RCT, {'n_arms': 2, 'epsilon':0.05}, weights=[0.1, 0.2]) 7 | model.repeat_simulation() 8 | 9 | fig, axes = plt.subplots(1,3, figsize=(18,6)) 10 | plt.suptitle("Performance of RCT and EpsilonGreedy algorithms", fontsize=20) 11 | 12 | model.plot_arm_frequency(ax=axes[0]) 13 | model.plot_reward(ax=axes[1]) 14 | model.plot_cumulative_reward(ax=axes[2]) 15 | 16 | 17 | # plot EpsilonGreedy algorithm on same plots as RCT algorithm 18 | model = Model(EpsilonGreedy, {'n_arms': 2, 'epsilon':0.05}, weights=[0.1, 0.2]) 19 | model.repeat_simulation() 20 | 21 | model.plot_arm_frequency(ax=axes[0], colour='red') 22 | model.plot_reward(ax=axes[1], colour='red') 23 | model.plot_cumulative_reward(ax=axes[2], colour='red') 24 | 25 | 26 | 27 | 28 | # plot different Epsilon values 29 | fig, axes = plt.subplots(1, 3, figsize=(18,6)) 30 | plt.suptitle("Performance of EpsilonGreedy algorithm with different epsilon values", fontsize=20) 31 | 32 | colors = ['red', 'orange', 'yellow', 'blue', 'purple', 'black'] 33 | epsilon_values = [1, 0.9, 0.6, 0.4, 0.2, 0.01] 34 | for color, value in zip(colors, epsilon_values): 35 | model = Model(EpsilonGreedy, {'n_arms': 2, 'epsilon':value}, weights=[0.1, 0.2]) 36 | model.repeat_simulation() 37 | model.plot_arm_frequency(ax=axes[0], colour=color) 38 | model.plot_reward(ax=axes[1], colour=color) 39 | model.plot_cumulative_reward(ax=axes[2], colour=color) 40 | 41 | 42 | 43 | 44 | # plot EpsilonDecrease algorithm 45 | fig, axes = plt.subplots(1, 3, figsize=(18,6)) 46 | plt.suptitle("Performance of EpsilonGreedy algorithm, reducing epsilon value over time", fontsize=20) 47 | 48 | model = Model(EpsilonDecrease, {'n_arms': 2, 'epsilon':1}, weights=[0.1, 0.2]) 49 | model.repeat_simulation() 50 | 51 | model.plot_arm_frequency(ax=axes[0]) 52 | model.plot_reward(ax=axes[1]) 53 | model.plot_cumulative_reward(ax=axes[2]) 54 | 55 | plt.show() 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /Wk02/workspace.py: -------------------------------------------------------------------------------- 1 | 2 | import timeit 3 | import matplotlib.pyplot as plt 4 | from simulated_annealing_optimizer import distance, new_path, new_path_modified, simulated_annealing_optimizer 5 | from genetic_algorithm_optimizer import new_path, distance, genetic_algorithm_optimizer, recombine, select_best 6 | 7 | 8 | coords = [(0,0), (10,5), (10,10), (5,10), (3,3), (3,7), (12,3), (10,11)] 9 | 10 | ################################### 11 | # simulated annealing algorithm # 12 | ################################### 13 | best_path, best_cost, history = simulated_annealing_optimizer(coords, distance, new_path_modified, 1000, 0.01, 1000) 14 | print('simulated annealing algorithm best cost: ', best_cost) 15 | 16 | ## plot cost for each path tested with simulated annealing algorithm 17 | plt.plot([i['current_cost'] for i in history]) 18 | plt.show() 19 | 20 | # plot coordinates of best path calculated with simulated annealing algorithm 21 | plt.plot([i[0] for i in best_path], [i[1] for i in best_path]) 22 | plt.show() 23 | 24 | 25 | # test to determine if new_path_modified improves performance of simulated annealing algorithm 26 | # there is not a significant difference, new_path_modified may even be worse 27 | def wrapper(func, *args, **kwargs): 28 | def wrapped(): 29 | return func(*args, **kwargs) 30 | return wrapped 31 | 32 | new_path_modified_test = wrapper(simulated_annealing_optimizer, coords, distance, new_path_modified, 1000, 0.01, 1000) 33 | new_path_test = wrapper(simulated_annealing_optimizer, coords, distance, new_path, 1000, 0.01, 1000) 34 | 35 | time_npm = timeit.timeit(new_path_modified_test, number = 100) 36 | time_np = timeit.timeit(new_path_test, number = 100) 37 | print('new_path_modified time: ', time_npm) 38 | print('new_path time: ', time_np) 39 | 40 | 41 | ####################### 42 | # genetic algorithm # 43 | ####################### 44 | best_path, best_cost, history = genetic_algorithm_optimizer(coords, distance, new_path, 500, 100) 45 | print('genetic algorithm best cost: ', best_cost) 46 | 47 | # plot cost for each path tested with genetic algorithm 48 | plt.plot([i['current_cost'] for i in history]) 49 | plt.show() 50 | 51 | # plot coordinates of best path calculated with genetic algorithm 52 | plt.plot([i[0] for i in best_path], [i[1] for i in best_path]) 53 | plt.show() 54 | 55 | -------------------------------------------------------------------------------- /Wk03/lpthw_ex43.py: -------------------------------------------------------------------------------- 1 | # Learn Python The Hard Way 2 | # Exercise 43: Basic Object-Oriented Analysis and Design 3 | # http://learnpythonthehardway.org/book/ex43.html 4 | 5 | # Aliens have invaded a space ship and our hero has to go through a maze 6 | # of rooms defeating them so he can escape into an escape pod to the planet 7 | # below. The game will be more like a Zork or Adventure type game with text 8 | # outputs and funny ways to die. The game will involve an engine that runs 9 | # a map full of rooms or scenes. Each room will print its own description 10 | # when the player enters it and then tell the engine what room to run next 11 | # out of the map. 12 | 13 | # Scenes: 14 | # Death: This is when the player dies and should be something funny. 15 | 16 | # Central Corridor: This is the starting point and has a Gothon already 17 | # standing there they have to defeat with a joke before continuing. 18 | 19 | # Laser Weapon Armory: This is where the hero gets a neutron bomb to blow 20 | # up the ship before getting to the escape pod. It has a keypad the hero 21 | # has to guess the number for. 22 | 23 | # The Bridge: Another battle scene with a Gothon where the hero places 24 | # the bomb. 25 | 26 | # Escape Pod: Where the hero escapes but only after guessing the right 27 | # escape pod. 28 | 29 | 30 | class Map(object): # inherits from the class 'object' 31 | 32 | def __init__(self, start_scene): # necessary for instantiation below 33 | pass 34 | 35 | def next_scene(self, scene_name): 36 | pass 37 | 38 | def opening_scene(self): 39 | pass 40 | 41 | 42 | class Engine(object): # inherits from the class 'object' 43 | 44 | def __init__(self, scene_map): # necessary for instantiation below 45 | 46 | def play(self): 47 | pass 48 | 49 | 50 | class Scene(object): # inherits from the class 'object' 51 | 52 | def enter(self): 53 | pass 54 | 55 | 56 | class Death(Scene): # inherits from the class 'Scene' 57 | 58 | def enter(self): # override parent method 59 | pass 60 | 61 | 62 | class CentralCorridor(Scene): # inherits from the class 'Scene' 63 | 64 | def enter(self): # override parent method 65 | pass 66 | 67 | 68 | class LaserWeaponArmory(Scene): # inherits from the class 'Scene' 69 | 70 | def enter(self): # override parent method 71 | pass 72 | 73 | 74 | class TheBridge(Scene): # inherits from the class 'Scene' 75 | 76 | def enter(self): # override parent method 77 | pass 78 | 79 | 80 | class EscapePod(Scene): # inherits from the class 'Scene' 81 | 82 | def enter(self): # override parent method 83 | pass 84 | 85 | 86 | a_map = Map('central_corridor') # instantiate Map object 87 | a_game = Engine(a_map) # instantiate Engine object, passing Map instance 88 | a_game.play() 89 | 90 | -------------------------------------------------------------------------------- /Wk06/06-storing-input/site.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template 2 | 3 | app = Flask(__name__) 4 | 5 | app.config['SECRET_KEY'] = 'important to keep unknown in production' 6 | 7 | from flask_wtf import Form 8 | from wtforms import StringField 9 | from wtforms.validators import DataRequired 10 | 11 | 12 | 13 | 14 | class MyForm(Form): 15 | """A class definition for a form object. 16 | 17 | Attributes: 18 | first_name (object) 19 | last_name (object) 20 | """ 21 | first_name = StringField('First name:', validators=[DataRequired()]) 22 | last_name = StringField('Last name:', validators=[DataRequired()]) 23 | # Fields are responsible for rendering and data conversion 24 | # They delegate to optional validators for data validation 25 | # StringField is the base for most of the more complicated fields, 26 | # and it represents 27 | # DataRequired checks that the data attribute on the field is a ‘true’ value 28 | 29 | 30 | from flask_sqlalchemy import SQLAlchemy 31 | 32 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db' 33 | db = SQLAlchemy(app) # create database 34 | db.create_all() # create all tables (will not recreate tables already present in database) 35 | 36 | 37 | class Visitor(db.Model): 38 | """A class definition for a visitor MetaData object. 39 | 40 | Attributes (initalized in __init__): 41 | first_name (string) 42 | last_name (string) 43 | 44 | """ 45 | # create columns for database table (not yet set) 46 | id = db.Column(db.Integer, primary_key=True) 47 | first_name = db.Column(db.String(80)) 48 | last_name = db.Column(db.String(80)) 49 | 50 | 51 | def __init__(self, first_name, last_name): 52 | self.first_name = first_name 53 | self.last_name = last_name 54 | 55 | 56 | def __repr__(self): 57 | return ''.format(self.first_name, self.last_name) 58 | 59 | 60 | @app.route('/', methods=('GET', 'POST')) 61 | def index(): 62 | """Process form for each visitor and return rendered HTML.""" 63 | 64 | form = MyForm() 65 | previous_visitors = Visitor.query.order_by('id DESC').all() 66 | 67 | # check if form submission is a valid POST request 68 | if form.validate_on_submit(): 69 | first_name = form.first_name.data # retrieve first name from form 70 | last_name = form.last_name.data # retrieve last name from form 71 | db.session.add(Visitor(first_name, last_name)) # set columns in database table 72 | db.session.commit() # write changes to the database 73 | else: 74 | first_name = 'stranger' 75 | last_name = None 76 | 77 | return render_template('index.html', form=form, first_name=first_name, last_name=last_name, previous_visitors=previous_visitors) 78 | 79 | 80 | if __name__ == '__main__': 81 | app.run(debug=True) 82 | -------------------------------------------------------------------------------- /Wk05/ingredient_recipe_interface.py: -------------------------------------------------------------------------------- 1 | class Ingredient(object): 2 | """The ingredient object that contains nutritional information""" 3 | 4 | def __init__(self, name, *args): 5 | self.name = name 6 | 7 | values = [] 8 | nutrients = ['carbs', 'protein', 'fat', 'cholesterol'] 9 | 10 | for arg in args: 11 | # if argument is a dict, assign it as instance attribute 12 | if isinstance(arg, dict): 13 | self.nutrition = arg 14 | # if argument isn't a dict, add argument to list 15 | else: 16 | values.append(arg) 17 | 18 | # if non-dict arguments were added to list, set them as instance attributes 19 | if len(values) > 0: 20 | self.nutrition = {} 21 | for nutrient, value in zip(nutrients, values): 22 | self.nutrition[nutrient] = value 23 | 24 | 25 | def __repr__(self): 26 | # if instance contains 'cholesterol' attribute, include it in str representation 27 | return 'Ingredient({0}, {1})'.format(self.name, self.nutrition) 28 | 29 | 30 | def get_nutrition(self): 31 | """Returns the nutritional information for the ingredient""" 32 | return (self.nutrition) 33 | 34 | 35 | class Recipe(object): 36 | """The Recipe object containing the ingredients""" 37 | 38 | def __init__(self, name, ingredients,): 39 | self.name = name 40 | self.ingredients = ingredients 41 | 42 | 43 | def __repr__(self): 44 | return 'Recipe({0}, {1})'.format(self.name, self.ingredients) 45 | 46 | 47 | def get_nutrition(self): 48 | """Returns the nutritional information for the recipe""" 49 | nutrition = [0, 0, 0] 50 | carbs = 0 51 | protein = 0 52 | fat = 0 53 | cholesterol = 0 54 | nutrition_cholesterol = 0 55 | for amount, ingredient in self.ingredients: 56 | # if ingredient is a Recipe instance 57 | if isinstance(ingredient, Recipe): 58 | ingredient = ingredient.ingredients # update ingredient to recipe's ingredients 59 | for amt, ing in ingredient: 60 | # print('ingredient within recipe: ', ing) 61 | carbs += amt * ing.carbs 62 | protein += amt * ing.protein 63 | fat += amt * ing.fat 64 | if hasattr(ing, 'cholesterol'): 65 | cholesterol += amt * ing.cholesterol 66 | nutrition[0] += amount * carbs 67 | nutrition[1] += amount * protein 68 | nutrition[2] += amount * fat 69 | nutrition_cholesterol += amount * cholesterol 70 | # if ingredient is an Ingredient instance 71 | else: 72 | # print('ingredient: ', ingredient) 73 | nutrition[0] += amount * ingredient.carbs 74 | nutrition[1] += amount * ingredient.protein 75 | nutrition[2] += amount * ingredient.fat 76 | if hasattr(ingredient, 'cholesterol'): 77 | nutrition_cholesterol += amount * ingredient.cholesterol 78 | 79 | # if cholesterol nutrition info has been supplied, append to list 80 | if nutrition_cholesterol > 0: 81 | nutrition.append(nutrition_cholesterol) 82 | return nutrition 83 | 84 | 85 | @property 86 | def nutrition(self): 87 | info = self.get_nutrition() 88 | nutrients = {} 89 | nutrients['carbs'] = info[0] 90 | nutrients['protein'] = info[1] 91 | nutrients['fat'] = info[2] 92 | # if info consists of more than carbs, protein, fat, add to nutrients dict 93 | if len(info) > 3: 94 | nutrients['cholesterol'] = info[3] 95 | return nutrients 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /Wk03/cookbook.py: -------------------------------------------------------------------------------- 1 | 2 | class Cookbook(object): 3 | """A class definition for a cookbook. The following attributes are supported: 4 | 5 | Attributes: 6 | name: A string representing the Cookbook's name 7 | recipes: A list of recipe objects""" 8 | 9 | def __init__(self, name): 10 | """Initalize a Cookbook object with name set to the value supplied, and initialize recipes array""" 11 | self.name = name 12 | self.recipes = [] 13 | 14 | def open(self, recipe_name): 15 | """Return a recipe. 16 | 17 | Arguments received: 18 | recipe_name -- string representing the recipe name 19 | """ 20 | for i in range(len(self.recipes)): 21 | if self.recipes[i].name == recipe_name: 22 | return self.recipes[i] 23 | 24 | def add(self, recipe): 25 | '''Append recipe object to Cookbook instance's recipes array 26 | 27 | Arguments received: 28 | recipe -- the recipe object to append 29 | ''' 30 | self.recipes.append(recipe) 31 | 32 | 33 | class Recipe(): 34 | """A class definition for a recipe. The following attributes are supported: 35 | 36 | Attributes: 37 | name: a string representing the recipe's name 38 | ingredients: a dictionary of ingredients the recipe needs 39 | equipment: a list of equipment the recipe needs 40 | instructions: a string representing the instructions 41 | servings: an integer representing the number of people the recipe can serve 42 | nutrition_info: a dictionary of nutrition info for each ingredient 43 | cooking_time: a float representing cooking time in minutes 44 | """ 45 | 46 | def __init__(self, name, ingredients, equipment, instructions, servings, cooking_time): 47 | """Initialize a Recipe object with name, ingredients, equipment, instructions, 48 | servings, nutrition_info, prep_time, cooking_time set to the values supplied""" 49 | self.name = name 50 | self.ingredients = ingredients 51 | self.equipment = equipment 52 | self.instructions = instructions 53 | self.servings = servings 54 | # self.nutrition_info, loops thru ingredients dict to retrieve nutrition info from each ingredient 55 | self.cooking_time = cooking_time 56 | 57 | def __str__(self): 58 | """Return a basic string representation of the Recipe instance""" 59 | return "Recipe: {0}.\nServes {1}.\nIngredients: {2}.\nInstructions: {3}.\nCooking time: {4} min.".format(self.name, self.servings, self.ingredients, self.instructions, self.cooking_time) 60 | 61 | 62 | class Ingredient(object): 63 | '''A class definition for an ingredient. The following attributes are supported: 64 | 65 | Attributes: 66 | name: a string representing the ingredient's name 67 | quantity: a string representing the quantity of ingredient needed 68 | nutrition_info: a string representing nutrition info of ingredient 69 | ''' 70 | 71 | def __init__(self, name, quantity, nutrition_info): 72 | '''Initialize a Ingredient object with name, quantity, nutrition_info set to values supplied''' 73 | self.name = name 74 | self.quantity = quantity 75 | self.nutrition_info = nutrition_info 76 | 77 | def __str__(self): 78 | '''Return a basic string representation of the Ingredient instance''' 79 | pass 80 | 81 | def scale(self, old_servings, new_servings): 82 | '''Scales quantity of ingredient according to new/old serving sizes 83 | 84 | Arguments accepted: 85 | old_servings -- an integer representing the number of people the recipe can currently serve 86 | new_servings -- 'an integer representing the number of people the recipe needs to serve''' 87 | pass 88 | 89 | def convert(self): 90 | '''Converts quantity of ingredient from metric to imperial, or vice versa''' 91 | pass 92 | 93 | 94 | # create banana bread recipe 95 | flour = Ingredient('flour', '2 cups', 'flour nutrition info') 96 | baking_soda = Ingredient('baking soda', '1 tsp', 'baking soda nutrition info') 97 | salt = Ingredient('salt', '1/4 tsp', 'salt nutrition info') 98 | butter = Ingredient('butter', '1/2 cup', 'butter nutrition info') 99 | brown_sugar = Ingredient('brown sugar', '3/4 cup', 'brown sugar nutrition info') 100 | bananas = Ingredient('bananas', '2 1/3 cups', 'banana nutrition info') 101 | ingredients = [flour, baking_soda, salt, butter, brown_sugar, bananas] 102 | equipment = ['9x5 inch loaf pan', 'oven', 'bowl', 'wooden spoon', 'measuring cups/spoons'] 103 | bananaBread = Recipe('banana bread', ingredients, equipment, 'instructions', 12, 80.0) 104 | 105 | a_cookbook = Cookbook('My Delicious Noms') # instantiate Cookbook 106 | a_cookbook.add(bananaBread) # add banana bread recipe to the Cookbook instance 107 | recipe = a_cookbook.open('banana bread') # open the banana bread recipe 108 | print(recipe) 109 | 110 | -------------------------------------------------------------------------------- /Wk07/model_and_algorithms.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | class Model(object): 4 | def __init__(self, algo, algo_kwargs, weights=[0.1, 0.1], size=100, repeats=200): 5 | self.algo = algo 6 | self.algo_kwargs = algo_kwargs 7 | self.weights = weights 8 | self.size = size 9 | self.repeats = repeats 10 | 11 | def run_simulation(self): 12 | """Run a single simulation, recording the performance""" 13 | algo = self.algo(**self.algo_kwargs) # instantiate algorithm object 14 | arm_choice_record = [] 15 | reward_record = [] 16 | for i in range(self.size): 17 | arm = algo.choose_arm() 18 | arm_choice_record.append(arm) 19 | reward = np.random.random() < self.weights[arm] 20 | reward_record.append(reward) 21 | algo.update(arm, reward) 22 | return arm_choice_record, reward_record 23 | 24 | def repeat_simulation(self): 25 | """Run multiple simulations, recording the performance of each""" 26 | arm_choice = [] 27 | reward = [] 28 | for i in range(self.repeats): 29 | arm_choice_record, reward_record = self.run_simulation() 30 | arm_choice.append(arm_choice_record) 31 | reward.append(reward_record) 32 | self.arm_choice = np.array(arm_choice) 33 | self.reward = np.array(reward) 34 | 35 | def plot_arm_frequency(self, ax, colour='black'): 36 | """Plot the frequency with which the second arm is chosen 37 | NOTE: Currently only works for two arms""" 38 | ax.plot(self.arm_choice.mean(axis=0), 'k.', color=colour) 39 | ax.set_title('Frequency of arm choice') 40 | ax.set_xlabel('Trial') 41 | ax.set_ylabel('Frequency') 42 | return ax 43 | 44 | def plot_reward(self, ax, colour='black'): 45 | """Plot the average reward for each trial across all simulations""" 46 | ax.plot(self.reward.mean(axis=0), 'k.', color=colour) 47 | ax.set_title('Reward') 48 | ax.set_xlabel('Trial') 49 | ax.set_ylabel('Reward') 50 | return ax 51 | 52 | def plot_cumulative_reward(self, ax, colour='black'): 53 | """Plot the cumulative reward across all simulations""" 54 | ax.plot(np.cumsum(self.reward, axis=1).mean(axis=0), 'k.', color=colour) 55 | ax.set_title('Cumulative Reward') 56 | ax.set_xlabel('Trial') 57 | ax.set_ylabel('Cumulative Reward') 58 | return ax 59 | 60 | 61 | class RCT(object): 62 | def __init__(self, n_arms, epsilon): 63 | self._epsilon = epsilon # self.epsilon is assigned via @property 64 | self.counts = [0] * n_arms 65 | self.values = [0] * n_arms 66 | self.n_arms = n_arms 67 | 68 | def choose_arm(self): 69 | """Choose an arm""" 70 | if np.random.random() > self.epsilon: 71 | # exploit (use best arm) 72 | weights = np.array(self.values) 73 | weights = weights == weights.max() 74 | weights = weights / weights.sum() 75 | return np.random.choice(np.arange(self.n_arms), p=weights) 76 | else: 77 | # explore (test all arms) 78 | return np.random.randint(self.n_arms) 79 | 80 | 81 | def update(self, arm, reward): 82 | """Update an arm with the reward""" 83 | self.counts[arm] = self.counts[arm] + 1 84 | n = self.counts[arm] 85 | value = self.values[arm] 86 | # Running product 87 | self.values[arm] = ((n - 1) / n) * self.values[arm] + (1/n) * reward 88 | 89 | @property 90 | def epsilon(self): 91 | if sum(self.counts) < 100: 92 | return 1 93 | else: 94 | return 0 95 | 96 | 97 | class EpsilonGreedy(RCT): 98 | 99 | @property 100 | def epsilon(self): 101 | return self._epsilon 102 | 103 | 104 | # When we have very little information on the relative performance of the two 105 | # arms a high exploration rate quickly provides us with additional information. 106 | # However, after several hundred trials we are relatively confident in the 107 | # performance of each arm and a high exploration rate is detrimental as we will 108 | # be choosing an arm we know to be inferior at a high rate. A better approach 109 | # would be to reduce the exploration rate as we acquire more information. 110 | class EpsilonDecrease(EpsilonGreedy): 111 | 112 | @property 113 | def epsilon(self): 114 | """Gradually reduce the value of epsilon over time""" 115 | total = sum(self.counts) 116 | return float(self._epsilon) / (total + float(self._epsilon)) 117 | 118 | -------------------------------------------------------------------------------- /Wk02/simulated_annealing_optimizer.py: -------------------------------------------------------------------------------- 1 | """Module to calculate best path between multiple points, using simulated annealing algorithm 2 | 3 | Functions: 4 | distance -- cost function that calculates distance as the cost of a path 5 | new_path -- path altering function that creates a new path 6 | new_path_modified -- same as new_path but slightly modified 7 | simulated_annealing_optimizer -- objective function that implements the simulated annealing algorithm 8 | """ 9 | 10 | import random 11 | import numpy as np 12 | 13 | 14 | def distance(coords): 15 | """Calculate the distance of a path between multiple points 16 | 17 | Arguments received: 18 | coords — list of coordinates, e.g. [(0,0), (10, 5), (10, 10)], representing a path 19 | 20 | Arguments returned: 21 | distance -- total distance as a float 22 | """ 23 | distance = 0 24 | for p1, p2 in zip(coords[:-1], coords[1:]): 25 | distance += ((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2) ** 0.5 26 | return distance 27 | 28 | 29 | def new_path(existing_path): 30 | """Switch two random consecutive points on a path 31 | 32 | Arguments received: 33 | existing_path -- list of coordinates representing a path 34 | 35 | Arguments returned: 36 | path -- list of coordinates representing the mutated path 37 | """ 38 | path = existing_path[:] 39 | point = random.randint(0, len(path)-2) # randomly choose a point between 1st and 2nd-to-last points on path 40 | path[point+1], path[point] = path[point], path[point+1] # switch this point with the next point 41 | return path 42 | 43 | # Our simulated annealing function generated a reasonable solution but there were signs that the approach was 44 | # limited by our function for creating new paths. Create a new function to generate paths in a different way. 45 | # Does this improve the performance of the simulated annealing function? Answer in workspace.py, along with timer. 46 | 47 | 48 | def new_path_modified(existing_path): 49 | """Switch two random points on a path 50 | 51 | Arguments received: 52 | existing_path -- list of coordinates representing a path 53 | 54 | Arguments returned: 55 | path -- list of coordinates representing the mutated path 56 | """ 57 | path = existing_path[:] 58 | point = random.randint(0, len(path)-1) # randomly choose any point on path 59 | point2 = random.randint(0, len(path)-1) # randomly choose any other point on path 60 | while point2 == point: 61 | point2 = random.randint(0, len(path)-1) 62 | temp = path[point] # temporary placeholder for 1st point 63 | path[point], path[point2] = path[point2], temp # switch 1st point with 2nd point 64 | return path 65 | 66 | 67 | def simulated_annealing_optimizer(starting_path, cost_func, new_path_func, start_temp, min_temp, steps): 68 | """Calculate the best path between multiple points using a simulated annealing algorithm 69 | 70 | The simulated annealing algorithm begins with a given path and calculates its cost. 71 | Each iteration generates a new path with two random consecutive points switched. 72 | If the new path has a lower cost, it is better and will replace the current path. 73 | If it has a higher cost, it is a worse path and may replace the current path, as 74 | decided by comparing a random number with an acceptance probability that depends on 75 | the temperature. The temperature decreases a bit in each iteration, giving worse paths 76 | a lesser chance of replacing the current path. Allowing worse paths at the beginning 77 | helps to avoid converging to a local minimum rather than the global minimum. 78 | 79 | Arugments received: 80 | starting_path -- list of coordinates representing a path 81 | cost_func -- function to calculate cost of a path 82 | new_path_func -- function to generate a new path with two random consecutive points switched 83 | start_temp -- maximum temperature 84 | min_temp -- minimum temperature 85 | steps -- number of iterations 86 | 87 | Arguments returned: 88 | current_path -- list of coordinates representing the best path 89 | current_cost -- cost of the best path 90 | history -- an array of objects, each object containing information about each tested path 91 | """ 92 | current_path = starting_path[:] 93 | current_cost = cost_func(current_path) 94 | temp_factor = -np.log(start_temp / min_temp) 95 | history = [] 96 | for s in range(0, steps): 97 | temp = start_temp * np.exp(temp_factor * s / steps) 98 | new_path = new_path_func(current_path) 99 | new_cost = cost_func(new_path) 100 | if (new_cost < current_cost) or (random.random() <= np.exp(-(new_cost - current_cost)/temp)): 101 | current_path = new_path 102 | current_cost = new_cost 103 | record = {'step': s, 'temperature': temp, 'current_cost': current_cost, } 104 | history.append(record) 105 | return (current_path, current_cost, history) 106 | -------------------------------------------------------------------------------- /Wk02/genetic_algorithm_optimizer.py: -------------------------------------------------------------------------------- 1 | """Module to calculate best path between multiple points, using genetic algorithm 2 | 3 | Functions: 4 | new_path -- path altering function that creates a new path 5 | distance -- cost function that calculates distance as the cost of a path 6 | select_best -- function that selects the best paths in a population 7 | recombine -- path altering function that returns a child path recombined from two parent paths 8 | genetic_algorithm_optimizer -- objective function that implements the genetic algorithm 9 | """ 10 | 11 | import random 12 | 13 | 14 | def new_path(existing_path): 15 | """Switch two random consecutive points on a path 16 | 17 | Arguments received: 18 | existing_path -- list of coordinates, e.g. [(0, 0), (10, 5), (10, 10)], representing a path 19 | 20 | Arguments returned: 21 | path -- list of coordinates representing the mutated path 22 | """ 23 | path = existing_path[:] 24 | point = random.randint(0, len(path)-2) # randomly choose a point between 1st and 2nd-to-last points on path 25 | path[point+1], path[point] = path[point], path[point+1] # switch this point with the next point 26 | return path 27 | 28 | 29 | def distance(coords): 30 | """Calculate the distance of a path between multiple points 31 | 32 | Arguments received: 33 | coords — list of coordinates representing a path 34 | 35 | Arguments returned: 36 | distance -- total distance as a float 37 | """ 38 | distance = 0 39 | for p1, p2 in zip(coords[:-1], coords[1:]): 40 | distance += ((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2) ** 0.5 41 | return distance 42 | 43 | 44 | def select_best(population, cost_func, num_to_keep): 45 | """Select a given number of paths with the lowest cost (the best paths) 46 | 47 | Arguments received: 48 | population -- an array of lists of coordinates representing paths 49 | cost_func -- function to calculate cost of a path 50 | num_to_keep -- number of paths to select 51 | 52 | Arguments returned: 53 | [i[0] for i in scored_population[:num_to_keep]] -- an array of lists of coordinates representing the best paths 54 | """ 55 | scored_population = [(i, cost_func(i)) for i in population] # create a list of tuples: (path, cost) 56 | scored_population.sort(key=lambda x: x[1]) # sort list by cost, lowest to highest 57 | return [i[0] for i in scored_population[:num_to_keep]] # return num_to_keep paths with the lowest cost 58 | 59 | 60 | def recombine(population): 61 | """Cross over two parent paths and return the resulting child path 62 | 63 | Arguments received: 64 | population -- an array of lists of coordinates representing paths 65 | 66 | Arguments returned: 67 | child -- list of coordinates representing a recombined child path 68 | """ 69 | # Randomly choose two parents 70 | options = list(range(len(population))) # from 1 to 125 71 | random.shuffle(options) 72 | partner1 = options[0] 73 | partner2 = options[1] 74 | # Choose a split point, take the first parent's order to that split point, 75 | # then the second parent's order for all remaining points 76 | split_point = random.randint(0, len(population[0])-1) 77 | child = population[partner1][:split_point] 78 | for point in population[partner2]: 79 | if point not in child: 80 | child.append(point) 81 | return child 82 | 83 | 84 | # Our genetic algorithm function currently only uses recombination. As we saw from the simulated 85 | # annealing approach mutation is also a powerful tool in locating the optimal solution. 86 | # Add mutation to the genetic algorithm function using the new_path function we created. 87 | 88 | def genetic_algorithm_optimizer(starting_path, cost_func, new_path_func, pop_size, generations): 89 | """Calculate the best path between multiple points using a genetic algorithm 90 | 91 | The genetic algorithm begins with a given path, which it shuffles to create a starting population of a given 92 | size. Once the population is generated, the cost of each path is evaluated. The top 25 percent then are sent 93 | through recombination, then mutation -- to hopefully generate 'better' paths -- to form a new population. 94 | 95 | Arugments received: 96 | starting_path -- list of coordinates representing a path 97 | cost_func -- function to calculate cost of a path 98 | new_path_func -- function to generate a new path with two random consecutive points switched 99 | pop_size -- population size, or amount of paths in one generation 100 | generations -- number of iterations 101 | 102 | Arguments returned: 103 | population[0] -- list of coordinates representing the best path 104 | cost_func(population[0]) -- cost of the best path 105 | history -- an array of objects, each object containing information about each tested path 106 | """ 107 | # Create a starting population of 500 paths by randomly shuffling the points 108 | population = [] 109 | for i in range(pop_size): 110 | new_path = starting_path[:] 111 | random.shuffle(new_path) 112 | population.append(new_path) 113 | history = [] 114 | # Take the top 25% of routes and recombine to create new routes, repeating for generations 115 | for i in range(generations): 116 | pop_best = select_best(population, cost_func, int(pop_size / 4)) 117 | new_population = [] 118 | mutated_population = [] 119 | for i in range(pop_size): 120 | new_population.append(recombine(pop_best)) 121 | if (random.random() <= 1/len(new_population[i])): # mutation probability, 1/path length 122 | mutated_population.append(new_path_func(new_population[i])) # mutate 123 | else: 124 | mutated_population.append(new_population[i]) # don't mutate 125 | population = mutated_population 126 | record = {'generation': i, 'current_cost': cost_func(population[0]), } 127 | history.append(record) 128 | return (population[0], cost_func(population[0]), history) 129 | -------------------------------------------------------------------------------- /Wk05/Wk05-OOP-Public-interface-Solution.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "##Assignments\n", 8 | "\n", 9 | "Modify the Ingredient and Recipe classes so that the following code works.\n", 10 | "\n" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": 46, 16 | "metadata": { 17 | "collapsed": true 18 | }, 19 | "outputs": [], 20 | "source": [ 21 | "class Ingredient(object):\n", 22 | " \"\"\"The ingredient object that contains nutritional information\"\"\"\n", 23 | " \n", 24 | " def __init__(self, name, data, protein=0, fat=0):\n", 25 | " if type(data) == dict:\n", 26 | " self.nutrition_store = data\n", 27 | " else:\n", 28 | " self.nutrition_store = {'carbs': data, 'protein':protein, 'fat': fat}\n", 29 | " self.name = name\n", 30 | " \n", 31 | " \n", 32 | " def __repr__(self):\n", 33 | " return 'Ingredient({0}, {1})'.format(self.name, self.nutrition_store)\n", 34 | " \n", 35 | " @property\n", 36 | " def nutrition(self):\n", 37 | " \"\"\"Returns the nutritional information for the ingredient\"\"\"\n", 38 | " return self.nutrition_store \n", 39 | "\n", 40 | "class Recipe(object):\n", 41 | " \"\"\"The Recipe object containing the ingredients\"\"\"\n", 42 | " \n", 43 | " def __init__(self, name, ingredients):\n", 44 | " self.name = name\n", 45 | " self.ingredients = ingredients\n", 46 | " \n", 47 | " def __repr__(self):\n", 48 | " temp_str = []\n", 49 | " for amount, ingredient in self.ingredients:\n", 50 | " temp_str.append('({0}, {1})'.format(amount, ingredient))\n", 51 | " return 'Recipe({0}, [{1}])'.format(self.name, ', '.join(temp_str))\n", 52 | " \n", 53 | " \n", 54 | " @property \n", 55 | " def nutrition(self):\n", 56 | " \"\"\"Returns the nutritional information for the recipe\"\"\"\n", 57 | " nutrition = {}\n", 58 | " for amount, ingredient in self.ingredients:\n", 59 | " for key in ingredient.nutrition:\n", 60 | " nutrition[key] = nutrition.get(key, 0) + amount * ingredient.nutrition[key]\n", 61 | " return nutrition" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": 47, 67 | "metadata": { 68 | "collapsed": false 69 | }, 70 | "outputs": [ 71 | { 72 | "name": "stdout", 73 | "output_type": "stream", 74 | "text": [ 75 | "[(820, Ingredient(Flour, {'carbs': 0.77, 'fat': 0.01, 'protein': 0.1})), (30, Ingredient(Oil, {'carbs': 0, 'fat': 1, 'protein': 0})), (36, Ingredient(Sugar, {'carbs': 1, 'fat': 0, 'protein': 0})), (7, Ingredient(Yeast, {'carbs': 0.3125, 'fat': 0.0625, 'protein': 0.5})), (560, Ingredient(Water, {'carbs': 0, 'fat': 0, 'protein': 0}))]\n" 76 | ] 77 | } 78 | ], 79 | "source": [ 80 | "bread = Recipe('Bread', [(820, Ingredient('Flour', 0.77, 0.10, 0.01)), \n", 81 | " (30, Ingredient('Oil', 0, 0, 1)), \n", 82 | " (36, Ingredient('Sugar', 1, 0, 0)), \n", 83 | " (7, Ingredient('Yeast', 0.3125, 0.5, 0.0625)),\n", 84 | " (560, Ingredient('Water', 0, 0, 0))])\n", 85 | "print(bread.ingredients)\n", 86 | "# Should be roughly [(820, Ingredient(Flour, 0.77, 0.1, 0.01)), (30, Ingredient(Oil, 0, 0, 1)), \n", 87 | "# (36, Ingredient(Sugar, 1, 0, 0)), (7, Ingredient(Yeast, 0.3125, 0.5, 0.0625)), (560, Ingredient(Water, 0, 0, 0))]" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 48, 93 | "metadata": { 94 | "collapsed": false 95 | }, 96 | "outputs": [ 97 | { 98 | "name": "stdout", 99 | "output_type": "stream", 100 | "text": [ 101 | "{'carbs': 669.5875, 'fat': 38.6375, 'protein': 85.5}\n" 102 | ] 103 | } 104 | ], 105 | "source": [ 106 | "print(bread.nutrition)\n", 107 | "#Should be roughly {'carbs': 669.5875, 'protein': 85.5, 'fat': 38.6375} the order is not important" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": 49, 113 | "metadata": { 114 | "collapsed": false 115 | }, 116 | "outputs": [ 117 | { 118 | "name": "stdout", 119 | "output_type": "stream", 120 | "text": [ 121 | "[(300, Ingredient(Egg, {'carbs': 0.0077, 'fat': 0.0994, 'protein': 0.1258, 'cholesterol': 0.00423})), (0.25, Recipe(Bread, [(820, Ingredient(Flour, {'carbs': 0.77, 'fat': 0.01, 'protein': 0.1})), (30, Ingredient(Oil, {'carbs': 0, 'fat': 1, 'protein': 0})), (36, Ingredient(Sugar, {'carbs': 1, 'fat': 0, 'protein': 0})), (7, Ingredient(Yeast, {'carbs': 0.3125, 'fat': 0.0625, 'protein': 0.5})), (560, Ingredient(Water, {'carbs': 0, 'fat': 0, 'protein': 0}))]))]\n" 122 | ] 123 | } 124 | ], 125 | "source": [ 126 | "#Points to note:\n", 127 | "# - The different call to Ingredient, you can use isinstance or type to change the \n", 128 | "# behaviour depending on the arguments supplied\n", 129 | "# - Cholesterol as an extra nutrient, your implementation should accept any nutrient\n", 130 | "# - Use of Recipe (bread) as an ingredient\n", 131 | "basic_french_toast = Recipe('Basic French Toast', [(300, Ingredient('Egg', {'carbs': 0.0077, 'protein': 0.1258, \n", 132 | " 'fat': 0.0994, 'cholesterol': 0.00423})), \n", 133 | " (0.25, bread)])\n", 134 | "print(basic_french_toast.ingredients)\n", 135 | "# Should be roughly:\n", 136 | "# [(300, Ingredient(Egg, 0.0077, 0.1258, 0.0994)), (0.25, Recipe(Bread, [(820, Ingredient(Flour, 0.77, 0.1, 0.01)), \n", 137 | "# (30, Ingredient(Oil, 0, 0, 1)), (36, Ingredient(Sugar, 1, 0, 0)), (7, Ingredient(Yeast, 0.3125, 0.5, 0.0625)), \n", 138 | "# (560, Ingredient(Water, 0, 0, 0))]))]\n", 139 | "# Note the formatting for the Recipe object, a __repr__ method will be needed" 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": 50, 145 | "metadata": { 146 | "collapsed": false 147 | }, 148 | "outputs": [ 149 | { 150 | "name": "stdout", 151 | "output_type": "stream", 152 | "text": [ 153 | "{'carbs': 169.706875, 'fat': 39.479375000000005, 'protein': 59.115, 'cholesterol': 1.2690000000000001}\n" 154 | ] 155 | } 156 | ], 157 | "source": [ 158 | "print(basic_french_toast.nutrition)\n", 159 | "# Should be roughly {'protein': 59.115, 'carbs': 169.706875, 'cholesterol': 1.2690000000000001, 'fat': 39.479375000000005}\n", 160 | "# The order is not important" 161 | ] 162 | } 163 | ], 164 | "metadata": { 165 | "kernelspec": { 166 | "display_name": "Python 3", 167 | "language": "python", 168 | "name": "python3" 169 | }, 170 | "language_info": { 171 | "codemirror_mode": { 172 | "name": "ipython", 173 | "version": 3 174 | }, 175 | "file_extension": ".py", 176 | "mimetype": "text/x-python", 177 | "name": "python", 178 | "nbconvert_exporter": "python", 179 | "pygments_lexer": "ipython3", 180 | "version": "3.5.1" 181 | } 182 | }, 183 | "nbformat": 4, 184 | "nbformat_minor": 0 185 | } 186 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Syllabus 2 | ======== 3 | 4 | **BIOF509 - Machine Learning and Object-Oriented Programming with Python** 5 | 6 | 7 | **Spring 2016** 8 | 9 | 10 | Instructors: 11 | 12 | * Jonathan Street (jonathan.street@nih.gov) 13 | * \R. Burke Squires (richard.squires@nih.gov) 14 | 15 | First class: 4th February 2016 16 | 17 | Final class: 12th May 2016 18 | 19 | *This document is subject to revision. Last revised 2nd Feb 2016.* 20 | 21 | Course Description 22 | ------------------ 23 | 24 | Learning Objectives 25 | ------------------- 26 | 27 | By the end of this course you should be able to: 28 | 29 | 1. Create working python programs using the basic features of the python language together with numpy, pandas, and biopython (A brief refresh) 30 | 2. Demonstrate the tools commonly used in professional settings to aid development 31 | 3. Compare different programming paradigms including procedural, functional and object oriented. 32 | 4. Define what an object is in the context of programming 33 | 5. Identify the features of an object definition 34 | 6. Contrast attributes, properties and methods 35 | 7. Review special methods 36 | 8. Design a public interface for a class 37 | 9. Utilize inheritance and abstraction 38 | 10. Choose when and how to raise and handle exceptions appropriately 39 | 11. Describe the common types of machine learning tasks 40 | 12. Implement a simple linear regression model utilizing numpy 41 | 13. List the advantages and disadvantages of different machine learning algorithms 42 | 14. Apply machine learning algorithms for both regression and classification 43 | 15. Convert a data set into a form suitable for use by machine learning algorithms 44 | 16. Apply dimensionality reduction to a data set for visualization and further processing 45 | 17. Identify subpopulations using clustering algorithms 46 | 18. Choose appropriate model parameters 47 | 19. Evaluate the results of a machine learning model 48 | 20. Integrate a machine learning model in a workflow 49 | 50 | 51 | Logistics 52 | --------- 53 | 54 | This is a 15 week course starting on the 4th February 2016, and finishing on 12th May 2016. Classes will take place between 5:30pm and 7:30pm each Thursday in building 10, room B1C207 within the FAES Academic Center. 55 | 56 | Attendance in class is strongly recommended; however, we realize other commitments will occasionally prevent attendance. Class materials will generally be sent to all learners via email. 57 | 58 | Most classes will have hands-on tutorials and assignments. Both practice and graded assignments will generally be provided. Graded assignments should be submitted prior to the following class. So that you can follow along during class bringing a laptop to each class is strongly encouraged. 59 | 60 | Important dates: 61 | 62 | * 04 Mar 2016 - Last day to drop/withdraw 63 | * 08 Apr 2016 - Last day to change status (credit or audit) 64 | 65 | Required Materials 66 | ------------------ 67 | 68 | **Each student is encouraged to bring their own laptop to each class.** For the course, we will use Python 3. Any python installation should work, but you must be able to install packages. The Anaconda Scientific Python Distribution from Continuum Analytics will likely be the easiest approach to configuring python if you do not already have python installed. The Anaconda installer will automatically install many of the packages we will use during the course. 69 | 70 | Recommended Books 71 | ----------------- 72 | 73 | **There is no required textbook for this course.** 74 | 75 | We will link to relevant online resources throughout the course. 76 | 77 | If you would like a refresher on the basics, the following resources may be useful: 78 | 79 | * `Learn python the hard way (ebook freely available from the author) `_ by Zed A. Shaw. A video course is also `available `_. 80 | * `Think python (ebook freely available from the author) `_ by Allen B. Downey. 81 | 82 | The following books cover some of the same material we will cover during the course. These books are not required, and presented solely as an alternative starting point covering the course objectives. 83 | 84 | * `The Elements of statistical learning (ebook freely available from the authors) `_ by Trevor Hastie, Robert Tibshirani, and Jerome Firedman. 85 | * `Python Machine Learning `_ by Sebastian Rashka. 86 | * `Python 3 Object Oriented Programming `_ by Dusty Phillips 87 | 88 | Assignments and Grading 89 | ----------------------- 90 | 91 | The emphasis of the course is on learning and mastering the skills covered. It is our hope that everyone will be able to complete the assignments and project. If some of the material appears unclear please ask for clarification. 92 | 93 | Grading assignments will follow the following rubric: 94 | 95 | * Program runs, produces correct result, contains useful comments, meaningful variable names, follows coding conventions: A+ 96 | * Program runs, produces correct result: A 97 | * Program runs, produces something close to the correct result: B 98 | * Program runs, does not produce correct result: C 99 | * Program does not run: Incomplete (I) 100 | 101 | Grading the final project will follow the following rubric: 102 | 103 | 104 | * Project description / Specification 105 | 106 | - Goals unclear, difficulty demonstrating functionality (1-3) 107 | - Goals for the project and functionality are discussed but difficult to follow (4-6) 108 | - Goals for the project and functionality are discussed (7-9) 109 | - Goals for the project and functionality are logically presented and clearly communicated (10-12) 110 | 111 | 112 | * Documentation 113 | 114 | - Only comments embedded in the code (1-3) 115 | - Objects and methods have docstrings (4-6) 116 | - Objects and methods have docstrings, additional standalone documentation (7-9) 117 | - Objects and methods have docstrings, extensive standalone documentation with example usage (10-12) 118 | 119 | 120 | * Readability 121 | 122 | - The code is poorly organized and very difficult to read (1-3) 123 | - The code is readable, but challenging to understand (4-6) 124 | - The code is fairly easy to read (7-9) 125 | - The code is well organized and very easy to read (10-12) 126 | 127 | 128 | * Reusability 129 | 130 | - The code is not organized for reusability (1-3) 131 | - Some parts of the code could be reused (4-6) 132 | - Most of the code could be reused (7-9) 133 | - Each part of the code, and the whole, could be reused (10-12) 134 | 135 | 136 | * Performance 137 | 138 | - Program does not run (1-6) 139 | - Program runs, but does not produce correct output (7-12) 140 | - Program runs, produces correct output under most conditions (13-18) 141 | - Program runs, produces correct output with robust error checking (19-24) 142 | 143 | 144 | Course Materials 145 | ---------------- 146 | 147 | Course materials are available from 148 | `the github repository `_. 149 | 150 | 151 | Schedule 152 | -------- 153 | 154 | **Week 1 (04 Feb 2016):** Course overview, troubleshooting software installation, and a refresher on the basics of python 155 | 156 | **Week 2 (11 Feb 2016):** The tools used to aid developemt. Recording experiments performed. Our first machine learning model. 157 | 158 | **Week 3 (18 Feb 2016):** Different programming paradigms, including object-oriented programming. The class definition - Attributes, properties, methods, and special methods. 159 | 160 | **Week 4 (25 Feb 2016):** Inheritance and abstraction. Graphical User Interfaces (GUIs) 161 | 162 | **Week 5 (03 Mar 2016):** Crafting the public interface. Choosing when and how to raise and handle exceptions. 163 | 164 | **Week 6 (10 Mar 2016):** WSGI as an example interface. Creating web applications. 165 | 166 | **Week 7 (17 Mar 2016):** Common machine learning tasks. 167 | 168 | **Week 8 (24 Mar 2016):** Linear regression with numpy. Overview of scikit-learn. 169 | 170 | **Week 9 (31 Mar 2016):** Dataset preprocessing. 171 | 172 | **Week 10 (07 Apr 2016):** Dimensionality reduction and clustering 173 | 174 | **Week 11 (14 Apr 2016):** Regression and classification. 175 | 176 | **Week 12 (21 Apr 2016):** The machine learning workflow. 177 | 178 | **Week 13 (28 Apr 2016):** Advanced ML tasks. 179 | 180 | **Week 14 (05 May 2016):** Project presentations. 181 | 182 | **Week 15 (12 May 2016):** Project presentations. -------------------------------------------------------------------------------- /Wk04/Wk04-GUI.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Graphical User Interfaces\n", 8 | "\n", 9 | "Object oriented programming and particularly inheritance is commonly used for creating GUIs. There are [a large number](https://wiki.python.org/moin/GuiProgramming) of different frameworks supporting building GUIs. The following are particularly relevant:\n", 10 | "\n", 11 | "* [TkInter](https://docs.python.org/3.5/library/tkinter.html) - This is the official/default GUI framework\n", 12 | "* [guidata](https://pythonhosted.org/guidata/) - A GUI framework for dataset display and editing\n", 13 | "* [VTK](http://www.vtk.org/) - A GUI framework for data visualization\n", 14 | "* [pyqtgraph](http://www.pyqtgraph.org/documentation/index.html) - A GUI framework for data visualization, easily installed with `conda install pyqtgraph`\n", 15 | "* [matplotlib](http://matplotlib.org/) - As well as creating plots matplotlib can support interaction" 16 | ] 17 | }, 18 | { 19 | "cell_type": "markdown", 20 | "metadata": {}, 21 | "source": [ 22 | "## TkInter\n", 23 | "\n", 24 | "TkInter is widely used with plenty of documentation available but may prove somewhat limited for more data intensive applications.\n", 25 | "\n", 26 | "* [Documentation from the standard library](https://docs.python.org/3/library/tk.html)\n", 27 | "* [Further documentation from python.org](https://docs.python.org/3.5/library/tkinter.html)\n", 28 | "* [TkDocs](http://www.tkdocs.com/index.html)\n", 29 | "\n", 30 | "Let's look at a simple example from the documentation" 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": 37, 36 | "metadata": { 37 | "collapsed": true 38 | }, 39 | "outputs": [], 40 | "source": [ 41 | "import tkinter as tk\n", 42 | "\n", 43 | "class Application(tk.Frame):\n", 44 | " def __init__(self, master=None):\n", 45 | " tk.Frame.__init__(self, master)\n", 46 | " self.pack()\n", 47 | " self.createWidgets()\n", 48 | "\n", 49 | " def createWidgets(self):\n", 50 | " self.hi_there = tk.Button(self)\n", 51 | " self.hi_there[\"text\"] = \"Hello World\\n(click me)\"\n", 52 | " self.hi_there[\"command\"] = self.say_hi\n", 53 | " self.hi_there.pack(side=\"top\")\n", 54 | "\n", 55 | " self.QUIT = tk.Button(self, text=\"QUIT\", fg=\"red\",\n", 56 | " command=root.destroy)\n", 57 | " self.QUIT.pack(side=\"bottom\")\n", 58 | "\n", 59 | " def say_hi(self):\n", 60 | " print(\"hi there, everyone!\")\n", 61 | "\n", 62 | "root = tk.Tk()\n", 63 | "app = Application(master=root)\n", 64 | "app.mainloop()" 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": {}, 70 | "source": [ 71 | "Although this works, it is visually unappealing. We can improve on this using styles and themes." 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": 48, 77 | "metadata": { 78 | "collapsed": false 79 | }, 80 | "outputs": [], 81 | "source": [ 82 | "import tkinter as tk\n", 83 | "from tkinter import ttk\n", 84 | "\n", 85 | "\n", 86 | "class Application(ttk.Frame):\n", 87 | " def __init__(self, master=None):\n", 88 | " super().__init__(master, padding=\"3 3 12 12\")\n", 89 | " self.grid(column=0, row=0, )\n", 90 | " self.createWidgets()\n", 91 | " self.master.title('Test')\n", 92 | "\n", 93 | " def createWidgets(self):\n", 94 | " self.hi_there = ttk.Button(self)\n", 95 | " self.hi_there[\"text\"] = \"Hello World\\n(click me)\"\n", 96 | " self.hi_there[\"command\"] = self.say_hi\n", 97 | "\n", 98 | " self.QUIT = ttk.Button(self, text=\"QUIT\", style='Alert.TButton', command=root.destroy)\n", 99 | "\n", 100 | " for child in self.winfo_children(): \n", 101 | " child.grid_configure(padx=10, pady=10)\n", 102 | "\n", 103 | " def say_hi(self):\n", 104 | " print(\"hi there, everyone!\")\n", 105 | "\n", 106 | " \n", 107 | "\n", 108 | "root = tk.Tk()\n", 109 | "app = Application(master=root)\n", 110 | "s = ttk.Style()\n", 111 | "s.configure('TButton', font='helvetica 24')\n", 112 | "s.configure('Alert.TButton', foreground='red')\n", 113 | "root.mainloop()" 114 | ] 115 | }, 116 | { 117 | "cell_type": "markdown", 118 | "metadata": {}, 119 | "source": [ 120 | "As our applications get more complicated we must give greater thought to the layout. The following example comes from the [TkDocs site](http://www.tkdocs.com/tutorial/firstexample.html)." 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": 28, 126 | "metadata": { 127 | "collapsed": false 128 | }, 129 | "outputs": [], 130 | "source": [ 131 | "from tkinter import *\n", 132 | "from tkinter import ttk\n", 133 | "\n", 134 | "def calculate(*args):\n", 135 | " try:\n", 136 | " value = float(feet.get())\n", 137 | " meters.set((0.3048 * value * 10000.0 + 0.5)/10000.0)\n", 138 | " except ValueError:\n", 139 | " pass\n", 140 | " \n", 141 | "root = Tk()\n", 142 | "root.title(\"Feet to Meters\")\n", 143 | "\n", 144 | "mainframe = ttk.Frame(root, padding=\"3 3 12 12\")\n", 145 | "mainframe.grid(column=0, row=0, sticky=(N, W, E, S))\n", 146 | "mainframe.columnconfigure(0, weight=1)\n", 147 | "mainframe.rowconfigure(0, weight=1)\n", 148 | "\n", 149 | "feet = StringVar()\n", 150 | "meters = StringVar()\n", 151 | "\n", 152 | "feet_entry = ttk.Entry(mainframe, width=7, textvariable=feet)\n", 153 | "feet_entry.grid(column=2, row=1, sticky=(W, E))\n", 154 | "\n", 155 | "ttk.Label(mainframe, textvariable=meters).grid(column=2, row=2, sticky=(W, E))\n", 156 | "ttk.Button(mainframe, text=\"Calculate\", command=calculate).grid(column=3, row=3, sticky=W)\n", 157 | "\n", 158 | "ttk.Label(mainframe, text=\"feet\").grid(column=3, row=1, sticky=W)\n", 159 | "ttk.Label(mainframe, text=\"is equivalent to\").grid(column=1, row=2, sticky=E)\n", 160 | "ttk.Label(mainframe, text=\"meters\").grid(column=3, row=2, sticky=W)\n", 161 | "\n", 162 | "for child in mainframe.winfo_children(): child.grid_configure(padx=5, pady=5)\n", 163 | "\n", 164 | "feet_entry.focus()\n", 165 | "root.bind('', calculate)\n", 166 | "\n", 167 | "root.mainloop()" 168 | ] 169 | }, 170 | { 171 | "cell_type": "markdown", 172 | "metadata": {}, 173 | "source": [ 174 | "## Matplotlib\n", 175 | "\n", 176 | "For simple programs, displaying data and taking basic input, often a command line application will be much faster to implement than a GUI. The times when I have moved away from the command line it has been to interact with image data and plots. Here, matplotlib often works very well. Either it can be embedded in a larger application or it can be used directly.\n", 177 | "\n", 178 | "There are a number of examples on the [matplotlib site](http://matplotlib.org/examples/index.html).\n", 179 | "\n", 180 | "Here is one stripped down example of one recent GUI I have used." 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": null, 186 | "metadata": { 187 | "collapsed": false 188 | }, 189 | "outputs": [], 190 | "source": [ 191 | "\"\"\"\n", 192 | "Do a mouseclick somewhere, move the mouse to some destination, release\n", 193 | "the button. This class gives click- and release-events and also draws\n", 194 | "a line or a box from the click-point to the actual mouseposition\n", 195 | "(within the same axes) until the button is released. Within the\n", 196 | "method 'self.ignore()' it is checked wether the button from eventpress\n", 197 | "and eventrelease are the same.\n", 198 | "\n", 199 | "\"\"\"\n", 200 | "from matplotlib.widgets import RectangleSelector\n", 201 | "import matplotlib.pyplot as plt\n", 202 | "import matplotlib.cbook as cbook\n", 203 | "\n", 204 | "\n", 205 | "def line_select_callback(eclick, erelease):\n", 206 | " 'eclick and erelease are the press and release events'\n", 207 | " x1, y1 = eclick.xdata, eclick.ydata\n", 208 | " x2, y2 = erelease.xdata, erelease.ydata\n", 209 | " print (\"(%3.2f, %3.2f) --> (%3.2f, %3.2f)\" % (x1, y1, x2, y2))\n", 210 | " print (\" The button you used were: %s %s\" % (eclick.button, erelease.button))\n", 211 | "\n", 212 | " \n", 213 | "def toggle_selector(event):\n", 214 | " print (' Key pressed.')\n", 215 | " if event.key in ['Q', 'q'] and toggle_selector.RS.active:\n", 216 | " print (' RectangleSelector deactivated.')\n", 217 | " toggle_selector.RS.set_active(False)\n", 218 | " if event.key in ['A', 'a'] and not toggle_selector.RS.active:\n", 219 | " print (' RectangleSelector activated.')\n", 220 | " toggle_selector.RS.set_active(True)\n", 221 | "\n", 222 | "\n", 223 | " \n", 224 | "image_file = cbook.get_sample_data('grace_hopper.png')\n", 225 | "image = plt.imread(image_file)\n", 226 | "fig, current_ax = plt.subplots()\n", 227 | "plt.imshow(image)\n", 228 | "toggle_selector.RS = RectangleSelector(current_ax, \n", 229 | " line_select_callback,\n", 230 | " drawtype='box', useblit=True,\n", 231 | " button=[1,3], # don't use middle button\n", 232 | " minspanx=5, minspany=5,\n", 233 | " spancoords='pixels')\n", 234 | "plt.connect('key_press_event', toggle_selector)\n", 235 | "plt.show()\n" 236 | ] 237 | }, 238 | { 239 | "cell_type": "code", 240 | "execution_count": null, 241 | "metadata": { 242 | "collapsed": true 243 | }, 244 | "outputs": [], 245 | "source": [] 246 | } 247 | ], 248 | "metadata": { 249 | "kernelspec": { 250 | "display_name": "Python 3", 251 | "language": "python", 252 | "name": "python3" 253 | }, 254 | "language_info": { 255 | "codemirror_mode": { 256 | "name": "ipython", 257 | "version": 3 258 | }, 259 | "file_extension": ".py", 260 | "mimetype": "text/x-python", 261 | "name": "python", 262 | "nbconvert_exporter": "python", 263 | "pygments_lexer": "ipython3", 264 | "version": "3.4.3" 265 | } 266 | }, 267 | "nbformat": 4, 268 | "nbformat_minor": 0 269 | } 270 | -------------------------------------------------------------------------------- /Wk04/Wk04-OOP-Inheritance-abstraction.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Week 4 - Inheritance and abstraction. Graphical User Interfaces (GUIs)\n", 8 | "\n", 9 | "\n", 10 | "## Learning Objectives\n", 11 | "\n", 12 | "* Describe inheritance in the context of object oriented programming\n", 13 | "* List situations in which inheritance is useful\n", 14 | "* Create an abstract class\n", 15 | "* Contrast control abstraction with data abstraction\n", 16 | "* Implement a simple graphic user interface" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "Last week we looked at several example projects and the classes we might use to implement them." 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "###Example 1: A Laboratory Inventory\n", 31 | "\n", 32 | "I would like to keep track of all the __items__ in the __laboratory__ so I can easily find them the next time I need them. Both __equipment__ and __consumables__ would be tracked. We have multiple __rooms__, and items can be on __shelves__, in __refrigerators__, in __freezers__, etc. Items can also be in __boxes__ containing other items in all these places.\n", 33 | "\n", 34 | "The words in __bold__ would all be good ideas to turn into classes. Now we know some of the classes we will need we can start to think about what each of these classes should do, what the methods will be. Let's consider the consumables class:\n", 35 | "\n", 36 | "For consumables we will need to manage their use so there should be an initial quantity and a quantity remaining that is updated every time we use some. We want to make sure that temperature sensitive consumables are always stored at the correct temperature, and that flammables are stored in a flammables cabinet etc.\n", 37 | "\n", 38 | "The consumable class will need a number of attributes:\n", 39 | "\n", 40 | "* Initial quantity\n", 41 | "* Current quantity\n", 42 | "* Storage temperature\n", 43 | "* Flammability\n", 44 | "\n", 45 | "The consumable class will need methods to:\n", 46 | "\n", 47 | "* Update the quantity remaining\n", 48 | "* Check for improper storage?\n", 49 | "\n", 50 | "The consumable class might interact with the shelf, refrigerator, freezer, and/or box classes.\n", 51 | "\n", 52 | "Reading back through our description of consumables there is reference to a flammables cabinet that was not mentioned in our initial description of the problem. This is an iterative design process so we should go back and add a flammables cabinet class. " 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "metadata": {}, 58 | "source": [ 59 | "If we expand our list to all the classes we plan to use we get the following:\n", 60 | "\n", 61 | "* __Items__\n", 62 | " * Attributes\n", 63 | " * Name\n", 64 | " * Description\n", 65 | " * Location\n", 66 | " * Methods\n", 67 | " * Update location\n", 68 | " * Interactions\n", 69 | " * Every other class except items and consumables\n", 70 | "* __Laboratory__\n", 71 | " * Attributes\n", 72 | " * ?\n", 73 | " * Methods\n", 74 | " * Search\n", 75 | " * Interactions\n", 76 | " * Every other class\n", 77 | "* __Equipment__\n", 78 | " * Attributes\n", 79 | " * Name\n", 80 | " * Description\n", 81 | " * Location\n", 82 | " * Methods\n", 83 | " * Update location\n", 84 | " * Interactions\n", 85 | " * Every other class except items and consumables\n", 86 | "* __Consumables__\n", 87 | " * Attributes\n", 88 | " * Name\n", 89 | " * Description\n", 90 | " * Location\n", 91 | " * Initial quantity\n", 92 | " * Current quantity\n", 93 | " * Storage temperature\n", 94 | " * Flammability\n", 95 | " * Methods\n", 96 | " * Update location\n", 97 | " * Update quantity remaining\n", 98 | " * Check for appropriate storage\n", 99 | " * Interactions\n", 100 | " * Every other class except equipment and items\n", 101 | "* __Rooms__\n", 102 | " * Attributes\n", 103 | " * Name\n", 104 | " * Description\n", 105 | " * Location\n", 106 | " * Storage locations within this location\n", 107 | " * Items stored here\n", 108 | " * Methods\n", 109 | " * Search\n", 110 | " * Interactions\n", 111 | " * Every other class\n", 112 | "* __Shelves__\n", 113 | " * Attributes\n", 114 | " * Name\n", 115 | " * Description\n", 116 | " * Location\n", 117 | " * Storage locations within this location\n", 118 | " * Items stored here\n", 119 | " * Methods\n", 120 | " * Search\n", 121 | " * Interactions\n", 122 | " * Every other class possible although refrigerator and freezer are unlikely\n", 123 | "* __Refrigerators__\n", 124 | " * Attributes\n", 125 | " * Name\n", 126 | " * Description\n", 127 | " * Location\n", 128 | " * Storage locations within this location\n", 129 | " * Items stored here\n", 130 | " * Temperature\n", 131 | " * Methods\n", 132 | " * Search\n", 133 | " * Interactions\n", 134 | " * Every other class possible although freezer and flammables cabinet unlikely\n", 135 | "* __Freezers__\n", 136 | " * Attributes\n", 137 | " * Name\n", 138 | " * Description\n", 139 | " * Location\n", 140 | " * Storage locations within this location\n", 141 | " * Items stored here\n", 142 | " * Temperature\n", 143 | " * Methods\n", 144 | " * Search\n", 145 | " * Interactions\n", 146 | " * Every other class possible although refrigerator and flammables cabinet unlikely\n", 147 | "* __Boxes__\n", 148 | " * Attributes\n", 149 | " * Name\n", 150 | " * Description\n", 151 | " * Location\n", 152 | " * Storage locations within this location\n", 153 | " * Items stored here\n", 154 | " * Methods\n", 155 | " * Search\n", 156 | " * Interactions\n", 157 | " * Every other class\n", 158 | "* __Flammables Cabinet__\n", 159 | " * Attributes\n", 160 | " * Name\n", 161 | " * Description\n", 162 | " * Location\n", 163 | " * Storage locations within this location\n", 164 | " * Items stored here\n", 165 | " * Methods\n", 166 | " * Search\n", 167 | " * Interactions\n", 168 | " * Every other class possible although refrigerator and freezer unlikely" 169 | ] 170 | }, 171 | { 172 | "cell_type": "markdown", 173 | "metadata": {}, 174 | "source": [ 175 | "Although this is a long list careful examination reveals that there is a lot of repetition.\n", 176 | "\n", 177 | "Items and equipment are identical and consumables is similar, adding several extra attributes and methods.\n", 178 | "\n", 179 | "Rooms, shelves, refrigerators, freezers, boxes and flammables cabinet are all similar, only differing in the occasional attribute.\n", 180 | "\n", 181 | "Our three main groups are:\n", 182 | "* Laboratory\n", 183 | "* Items (Items, equipment, and consumables)\n", 184 | "* Locations (Rooms, shelves, refrigerators, freezers, boxes and flammables cabinet)\n", 185 | "\n", 186 | "So much duplication is problematic, it is diffcult to maintain and subject to greater risk of bugs. \n", 187 | "\n", 188 | "There is a better way - we can create a generic class with the shared functionality and then __inherit__ from it when we create the other classes.\n", 189 | "\n", 190 | "For example an Item class would contain the basic attributes and methods. The Equipment class could then inherit from this class without modification. The Consumable class would also inherit from Item and only add the extra attributes and methods uniquely need by the Consumable class." 191 | ] 192 | }, 193 | { 194 | "cell_type": "code", 195 | "execution_count": null, 196 | "metadata": { 197 | "collapsed": true 198 | }, 199 | "outputs": [], 200 | "source": [ 201 | "class Item(object):\n", 202 | " \n", 203 | " \n", 204 | " def __init__(self, name, description, location):\n", 205 | " self.name = name\n", 206 | " self.description = description\n", 207 | " self.location = location\n", 208 | " \n", 209 | " \n", 210 | " def update_location(self, new_location):\n", 211 | " pass\n", 212 | "\n", 213 | "class Equipment(Item):\n", 214 | " pass\n", 215 | "\n", 216 | "class Consumable(Item):\n", 217 | " \n", 218 | " \n", 219 | " def __init__(self, name, description, location, initial_quantity, current_quantity, storage_temp, flammability):\n", 220 | " self.name = name\n", 221 | " self.description = description\n", 222 | " self.location = location\n", 223 | " self.initial_quantity = initial_quantity\n", 224 | " self.current_quantity = current_quantity\n", 225 | " self.flammability = flammability\n", 226 | " \n", 227 | " \n", 228 | " def update_quantity_remaining(self, amount):\n", 229 | " pass" 230 | ] 231 | }, 232 | { 233 | "cell_type": "markdown", 234 | "metadata": {}, 235 | "source": [ 236 | "There is one other situation we should consider. Occasionally we will want a class of a particular type to always implement a particular method even though we are unable to implement that method in our parent class. We need some way of raising an error when the parent class is inherited and the method is not implemented.\n", 237 | "\n", 238 | "As a simple example consider a class representing length. We might create classes for meters, miles, feet, etc. Keeping the original units when performing operations (adding, subtracting, etc) would prevent rounding errors but each class would need custom logic.\n", 239 | "\n", 240 | "Returning to our laboratory inventory system one way we can implement this is below:" 241 | ] 242 | }, 243 | { 244 | "cell_type": "code", 245 | "execution_count": 2, 246 | "metadata": { 247 | "collapsed": false 248 | }, 249 | "outputs": [], 250 | "source": [ 251 | "class Item(object):\n", 252 | " def safely_stored(self):\n", 253 | " raise NotImplementedError('override in subclass')\n", 254 | " \n", 255 | "class Consumable(Item):\n", 256 | " def safely_stored(self):\n", 257 | " return True" 258 | ] 259 | }, 260 | { 261 | "cell_type": "code", 262 | "execution_count": 3, 263 | "metadata": { 264 | "collapsed": true 265 | }, 266 | "outputs": [], 267 | "source": [ 268 | "a = Item()" 269 | ] 270 | }, 271 | { 272 | "cell_type": "code", 273 | "execution_count": 4, 274 | "metadata": { 275 | "collapsed": false 276 | }, 277 | "outputs": [ 278 | { 279 | "ename": "NotImplementedError", 280 | "evalue": "override in subclass", 281 | "output_type": "error", 282 | "traceback": [ 283 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 284 | "\u001b[1;31mNotImplementedError\u001b[0m Traceback (most recent call last)", 285 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0ma\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msafely_stored\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", 286 | "\u001b[1;32m\u001b[0m in \u001b[0;36msafely_stored\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[1;32mclass\u001b[0m \u001b[0mItem\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mobject\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0msafely_stored\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 3\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mNotImplementedError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'override in subclass'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 4\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 5\u001b[0m \u001b[1;32mclass\u001b[0m \u001b[0mConsumable\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mItem\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", 287 | "\u001b[1;31mNotImplementedError\u001b[0m: override in subclass" 288 | ] 289 | } 290 | ], 291 | "source": [ 292 | "a.safely_stored()" 293 | ] 294 | }, 295 | { 296 | "cell_type": "code", 297 | "execution_count": 5, 298 | "metadata": { 299 | "collapsed": true 300 | }, 301 | "outputs": [], 302 | "source": [ 303 | "b = Consumable()" 304 | ] 305 | }, 306 | { 307 | "cell_type": "code", 308 | "execution_count": 6, 309 | "metadata": { 310 | "collapsed": false 311 | }, 312 | "outputs": [ 313 | { 314 | "data": { 315 | "text/plain": [ 316 | "True" 317 | ] 318 | }, 319 | "execution_count": 6, 320 | "metadata": {}, 321 | "output_type": "execute_result" 322 | } 323 | ], 324 | "source": [ 325 | "b.safely_stored()" 326 | ] 327 | }, 328 | { 329 | "cell_type": "markdown", 330 | "metadata": {}, 331 | "source": [ 332 | "A disadvantage with this approach is we only see the error message when we call the method. The error is in the way we implemented the class so it would be more intuitive to get an error earlier, when we first create the object.\n", 333 | "\n", 334 | "This can be achieved using the [abstract method decorator](https://docs.python.org/3.5/library/abc.html)." 335 | ] 336 | }, 337 | { 338 | "cell_type": "code", 339 | "execution_count": 7, 340 | "metadata": { 341 | "collapsed": false 342 | }, 343 | "outputs": [], 344 | "source": [ 345 | "from abc import ABCMeta, abstractmethod\n", 346 | "\n", 347 | "class Item(metaclass=ABCMeta):\n", 348 | " @abstractmethod\n", 349 | " def safely_stored(self):\n", 350 | " pass\n", 351 | "\n", 352 | "class Consumable(Item):\n", 353 | " def safely_stored(self):\n", 354 | " return True" 355 | ] 356 | }, 357 | { 358 | "cell_type": "code", 359 | "execution_count": 8, 360 | "metadata": { 361 | "collapsed": false 362 | }, 363 | "outputs": [ 364 | { 365 | "ename": "TypeError", 366 | "evalue": "Can't instantiate abstract class Item with abstract methods safely_stored", 367 | "output_type": "error", 368 | "traceback": [ 369 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 370 | "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", 371 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0ma\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mItem\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", 372 | "\u001b[1;31mTypeError\u001b[0m: Can't instantiate abstract class Item with abstract methods safely_stored" 373 | ] 374 | } 375 | ], 376 | "source": [ 377 | "a = Item()" 378 | ] 379 | }, 380 | { 381 | "cell_type": "code", 382 | "execution_count": 9, 383 | "metadata": { 384 | "collapsed": false 385 | }, 386 | "outputs": [ 387 | { 388 | "data": { 389 | "text/plain": [ 390 | "True" 391 | ] 392 | }, 393 | "execution_count": 9, 394 | "metadata": {}, 395 | "output_type": "execute_result" 396 | } 397 | ], 398 | "source": [ 399 | "b = Consumable()\n", 400 | "b.safely_stored()" 401 | ] 402 | }, 403 | { 404 | "cell_type": "markdown", 405 | "metadata": { 406 | "collapsed": true 407 | }, 408 | "source": [ 409 | "Either of these approaches work well for adding new methods or completely changing the behaviour of a method. Often we only need to make a more subtle change. In this situation it can be useful to call a method from a parent class while only implementing our new functionality in the child class.\n", 410 | "\n", 411 | "\n", 412 | "There are two approaches for this." 413 | ] 414 | }, 415 | { 416 | "cell_type": "code", 417 | "execution_count": 1, 418 | "metadata": { 419 | "collapsed": false 420 | }, 421 | "outputs": [ 422 | { 423 | "name": "stdout", 424 | "output_type": "stream", 425 | "text": [ 426 | "a in class A\n", 427 | "a in class A\n", 428 | "b in class B\n" 429 | ] 430 | } 431 | ], 432 | "source": [ 433 | "class A(object):\n", 434 | " def a(self):\n", 435 | " print('a in class A')\n", 436 | "\n", 437 | "class B(A):\n", 438 | " def a(self):\n", 439 | " A.a(self)\n", 440 | " print('b in class B')\n", 441 | "\n", 442 | "a = A()\n", 443 | "a.a()\n", 444 | "\n", 445 | "b = B()\n", 446 | "b.a()" 447 | ] 448 | }, 449 | { 450 | "cell_type": "code", 451 | "execution_count": 2, 452 | "metadata": { 453 | "collapsed": false 454 | }, 455 | "outputs": [ 456 | { 457 | "name": "stdout", 458 | "output_type": "stream", 459 | "text": [ 460 | "a in class A\n", 461 | "a in class A\n", 462 | "b in class B\n" 463 | ] 464 | } 465 | ], 466 | "source": [ 467 | "class A(object):\n", 468 | " def a(self):\n", 469 | " print('a in class A')\n", 470 | "\n", 471 | "class B(A):\n", 472 | " def a(self):\n", 473 | " super().a()\n", 474 | " print('b in class B')\n", 475 | "\n", 476 | "a = A()\n", 477 | "a.a()\n", 478 | "\n", 479 | "b = B()\n", 480 | "b.a()" 481 | ] 482 | }, 483 | { 484 | "cell_type": "markdown", 485 | "metadata": {}, 486 | "source": [ 487 | "Using super() is usually the best approach, the reasons for this are covered in detail in [this blog post](https://rhettinger.wordpress.com/2011/05/26/super-considered-super/)" 488 | ] 489 | }, 490 | { 491 | "cell_type": "markdown", 492 | "metadata": {}, 493 | "source": [ 494 | "## Multiple Inheritance\n", 495 | "\n", 496 | "We are not limited to inheriting from a single class. It is possible to merge functionality from multiple different classes simply by inheriting from them.\n", 497 | "\n", 498 | "When inheriting from multiple classes that contain a method or attribute with the same name there is a particular order in which the names are resolved." 499 | ] 500 | }, 501 | { 502 | "cell_type": "code", 503 | "execution_count": 18, 504 | "metadata": { 505 | "collapsed": false 506 | }, 507 | "outputs": [ 508 | { 509 | "name": "stdout", 510 | "output_type": "stream", 511 | "text": [ 512 | "A-a\n", 513 | "A2-a\n", 514 | "A-a\n" 515 | ] 516 | } 517 | ], 518 | "source": [ 519 | "class A(object):\n", 520 | " def a(self):\n", 521 | " print('A-a')\n", 522 | "\n", 523 | "class A2(object):\n", 524 | " def a(self):\n", 525 | " print('A2-a')\n", 526 | "\n", 527 | "class B(A, A2):\n", 528 | " pass\n", 529 | "\n", 530 | "a = A()\n", 531 | "a.a()\n", 532 | "\n", 533 | "a2 = A2()\n", 534 | "a2.a()\n", 535 | "\n", 536 | "b = B()\n", 537 | "b.a()" 538 | ] 539 | }, 540 | { 541 | "cell_type": "code", 542 | "execution_count": 21, 543 | "metadata": { 544 | "collapsed": false 545 | }, 546 | "outputs": [ 547 | { 548 | "name": "stdout", 549 | "output_type": "stream", 550 | "text": [ 551 | "A-a\n", 552 | "A2-a\n", 553 | "A-a\n" 554 | ] 555 | } 556 | ], 557 | "source": [ 558 | "class A(object):\n", 559 | " def a(self):\n", 560 | " print('A-a')\n", 561 | "\n", 562 | "class A2(object):\n", 563 | " def a(self):\n", 564 | " print('A2-a')\n", 565 | "\n", 566 | "class B(A):\n", 567 | " pass\n", 568 | "\n", 569 | "class C(B, A2):\n", 570 | " pass\n", 571 | "\n", 572 | "a = A()\n", 573 | "a.a()\n", 574 | "\n", 575 | "a2 = A2()\n", 576 | "a2.a()\n", 577 | "\n", 578 | "c = C()\n", 579 | "c.a()" 580 | ] 581 | }, 582 | { 583 | "cell_type": "markdown", 584 | "metadata": {}, 585 | "source": [ 586 | "A simple rule-of-thumb is that search is depth first. [The details](https://en.wikipedia.org/wiki/C3_linearization) are a little more complicated." 587 | ] 588 | }, 589 | { 590 | "cell_type": "markdown", 591 | "metadata": {}, 592 | "source": [ 593 | "## isinstance\n", 594 | "\n", 595 | "Often we need to check whether a particular variable is an instance of a particular class. For example, returning to our laboratory inventory system we would want to check that we only add instances of `Item` or its subclasses to our storage locations." 596 | ] 597 | }, 598 | { 599 | "cell_type": "code", 600 | "execution_count": 11, 601 | "metadata": { 602 | "collapsed": false 603 | }, 604 | "outputs": [ 605 | { 606 | "data": { 607 | "text/plain": [ 608 | "True" 609 | ] 610 | }, 611 | "execution_count": 11, 612 | "metadata": {}, 613 | "output_type": "execute_result" 614 | } 615 | ], 616 | "source": [ 617 | "isinstance(a, Item)" 618 | ] 619 | }, 620 | { 621 | "cell_type": "code", 622 | "execution_count": 12, 623 | "metadata": { 624 | "collapsed": false 625 | }, 626 | "outputs": [ 627 | { 628 | "data": { 629 | "text/plain": [ 630 | "True" 631 | ] 632 | }, 633 | "execution_count": 12, 634 | "metadata": {}, 635 | "output_type": "execute_result" 636 | } 637 | ], 638 | "source": [ 639 | "isinstance(b, Consumable)" 640 | ] 641 | }, 642 | { 643 | "cell_type": "code", 644 | "execution_count": 13, 645 | "metadata": { 646 | "collapsed": false 647 | }, 648 | "outputs": [ 649 | { 650 | "data": { 651 | "text/plain": [ 652 | "True" 653 | ] 654 | }, 655 | "execution_count": 13, 656 | "metadata": {}, 657 | "output_type": "execute_result" 658 | } 659 | ], 660 | "source": [ 661 | "isinstance(b, Item)" 662 | ] 663 | }, 664 | { 665 | "cell_type": "code", 666 | "execution_count": 14, 667 | "metadata": { 668 | "collapsed": false 669 | }, 670 | "outputs": [ 671 | { 672 | "data": { 673 | "text/plain": [ 674 | "False" 675 | ] 676 | }, 677 | "execution_count": 14, 678 | "metadata": {}, 679 | "output_type": "execute_result" 680 | } 681 | ], 682 | "source": [ 683 | "isinstance(a, Consumable)" 684 | ] 685 | }, 686 | { 687 | "cell_type": "markdown", 688 | "metadata": {}, 689 | "source": [ 690 | "##Duck typing\n", 691 | "\n", 692 | "A popular alternative in python is duck typing, an approach named after the idea that,\n", 693 | "\n", 694 | "`If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.`\n", 695 | "\n", 696 | "What this means for programming is that instead of checking for a particular class, instead the methods and attributes that are actually needed are checked for." 697 | ] 698 | }, 699 | { 700 | "cell_type": "markdown", 701 | "metadata": {}, 702 | "source": [ 703 | "## Graphical User Interfaces\n", 704 | "\n", 705 | "Object oriented programming and particularly inheritance is commonly used for creating GUIs. There are [a large number](https://wiki.python.org/moin/GuiProgramming) of different frameworks supporting building GUIs. The following are particularly relevant:\n", 706 | "\n", 707 | "* [TkInter](https://docs.python.org/3.5/library/tkinter.html) - This is the official/default GUI framework\n", 708 | "* [guidata](https://pythonhosted.org/guidata/) - A GUI framework for dataset display and editing\n", 709 | "* [VTK](http://www.vtk.org/) - A GUI framework for data visualization\n", 710 | "* [pyqtgraph](http://www.pyqtgraph.org/documentation/index.html) - A GUI framework for data visualization, easily installed with `conda install pyqtgraph`\n", 711 | "* [matplotlib](http://matplotlib.org/) - As well as creating plots matplotlib can support interaction" 712 | ] 713 | }, 714 | { 715 | "cell_type": "code", 716 | "execution_count": null, 717 | "metadata": { 718 | "collapsed": true 719 | }, 720 | "outputs": [], 721 | "source": [] 722 | } 723 | ], 724 | "metadata": { 725 | "kernelspec": { 726 | "display_name": "Python 3", 727 | "language": "python", 728 | "name": "python3" 729 | }, 730 | "language_info": { 731 | "codemirror_mode": { 732 | "name": "ipython", 733 | "version": 3 734 | }, 735 | "file_extension": ".py", 736 | "mimetype": "text/x-python", 737 | "name": "python", 738 | "nbconvert_exporter": "python", 739 | "pygments_lexer": "ipython3", 740 | "version": "3.4.3" 741 | } 742 | }, 743 | "nbformat": 4, 744 | "nbformat_minor": 0 745 | } 746 | -------------------------------------------------------------------------------- /Wk03/Wk03-Paradigms.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Week 3 - Programming Paradigms\n", 8 | "\n", 9 | "## Learning Objectives\n", 10 | "\n", 11 | "* List popular programming paradigms\n", 12 | "* Demonstrate object oriented programming\n", 13 | "* Compare procedural programming and object oriented programming\n", 14 | "* Apply object oriented programming to solve sample problems" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": { 20 | "collapsed": false 21 | }, 22 | "source": [ 23 | "Computer programs and the elements they contain can be built in a variety of different ways. Several different styles, or paradigms, exist with differing popularity and usefulness for different tasks.\n", 24 | "\n", 25 | "Some programming languages are designed to support a particular paradigm, while other languages support several different paradigms.\n", 26 | "\n", 27 | "Three of the most commonly used paradigms are:\n", 28 | "\n", 29 | "* Procedural\n", 30 | "* Object oriented\n", 31 | "* Functional\n", 32 | "\n", 33 | "Python supports each of these paradigms. " 34 | ] 35 | }, 36 | { 37 | "cell_type": "markdown", 38 | "metadata": { 39 | "collapsed": true 40 | }, 41 | "source": [ 42 | "## Procedural" 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "metadata": {}, 48 | "source": [ 49 | "You may not have realized it but the procedural programming paradigm is probably the approach you are currently taking with your programs.\n", 50 | "\n", 51 | "Programs and functions are simply a series of steps to be performed. \n", 52 | "\n", 53 | "For example:" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": 67, 59 | "metadata": { 60 | "collapsed": false 61 | }, 62 | "outputs": [ 63 | { 64 | "name": "stdout", 65 | "output_type": "stream", 66 | "text": [ 67 | "[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]\n" 68 | ] 69 | } 70 | ], 71 | "source": [ 72 | "primes = []\n", 73 | "i = 2\n", 74 | "while len(primes) < 25:\n", 75 | " for p in primes:\n", 76 | " if i % p == 0:\n", 77 | " break\n", 78 | " else:\n", 79 | " primes.append(i)\n", 80 | " i += 1\n", 81 | "\n", 82 | "print(primes)" 83 | ] 84 | }, 85 | { 86 | "cell_type": "markdown", 87 | "metadata": {}, 88 | "source": [ 89 | "## Functional" 90 | ] 91 | }, 92 | { 93 | "cell_type": "markdown", 94 | "metadata": { 95 | "collapsed": true 96 | }, 97 | "source": [ 98 | "Functional programming is based on the evaluation of mathematical functions. This is a more restricted form of function than you may have used previously - mutable data and changing state is avoided. This makes understanding how a program will behave more straightforward.\n", 99 | "\n", 100 | "Python does support functional programming although it is not as widely used as procedural and object oriented programming. Some languages better known for supporting functional programming include Lisp, Clojure, Erlang, and Haskell." 101 | ] 102 | }, 103 | { 104 | "cell_type": "markdown", 105 | "metadata": {}, 106 | "source": [ 107 | "### Functions - Mathematical vs subroutines\n", 108 | "\n", 109 | "In the general sense, functions can be thought of as simply wrappers around blocks of code. In this sense they can also be thought of as subroutines. Importantly they can be written to fetch data and change the program state independently of the function arguments.\n", 110 | "\n", 111 | "In functional programming the output of a function should depend solely on the function arguments." 112 | ] 113 | }, 114 | { 115 | "cell_type": "markdown", 116 | "metadata": {}, 117 | "source": [ 118 | "There is an extensive howto in the [python documentation](https://docs.python.org/3.5/howto/functional.html).\n", 119 | "\n", 120 | "[This presentation from PyCon US 2013](https://www.youtube.com/watch?v=Ta1bAMOMFOI) is also worth watching.\n", 121 | "\n", 122 | "[This presentation from PyGotham 2014](https://www.youtube.com/watch?v=yW0cK3IxlHc) covers decorators specifically." 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": 68, 128 | "metadata": { 129 | "collapsed": false 130 | }, 131 | "outputs": [ 132 | { 133 | "name": "stdout", 134 | "output_type": "stream", 135 | "text": [ 136 | "0\n", 137 | "1\n", 138 | "2\n", 139 | "3\n", 140 | "4\n", 141 | "Squared from list:\n", 142 | "[0, 1, 4, 9, 16]\n", 143 | "Squared from iterable:\n", 144 | " at 0x7fa208be2510>\n" 145 | ] 146 | } 147 | ], 148 | "source": [ 149 | "def square(val):\n", 150 | " print(val)\n", 151 | " return val ** 2\n", 152 | "\n", 153 | "squared_numbers = [square(i) for i in range(5)]\n", 154 | "print('Squared from list:')\n", 155 | "print(squared_numbers)\n", 156 | "\n", 157 | "squared_numbers = (square(i) for i in range(5))\n", 158 | "print('Squared from iterable:')\n", 159 | "print(squared_numbers)" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": 69, 165 | "metadata": { 166 | "collapsed": false 167 | }, 168 | "outputs": [ 169 | { 170 | "name": "stdout", 171 | "output_type": "stream", 172 | "text": [ 173 | "\n", 174 | "0\n", 175 | "1\n", 176 | "4\n", 177 | "9\n", 178 | "16\n", 179 | "This is only printed after all the numbers output have been consumed\n" 180 | ] 181 | } 182 | ], 183 | "source": [ 184 | "def squared_numbers(num):\n", 185 | " for i in range(num):\n", 186 | " yield i ** 2\n", 187 | " print('This is only printed after all the numbers output have been consumed')\n", 188 | "\n", 189 | "print(squared_numbers(5))\n", 190 | "for i in squared_numbers(5):\n", 191 | " print(i)" 192 | ] 193 | }, 194 | { 195 | "cell_type": "code", 196 | "execution_count": 70, 197 | "metadata": { 198 | "collapsed": false 199 | }, 200 | "outputs": [ 201 | { 202 | "data": { 203 | "text/plain": [ 204 | "10" 205 | ] 206 | }, 207 | "execution_count": 70, 208 | "metadata": {}, 209 | "output_type": "execute_result" 210 | } 211 | ], 212 | "source": [ 213 | "import functools\n", 214 | "\n", 215 | "def plus(val, n):\n", 216 | " return val + n\n", 217 | "\n", 218 | "\n", 219 | "f = functools.partial(plus, 5)\n", 220 | "f(5)" 221 | ] 222 | }, 223 | { 224 | "cell_type": "code", 225 | "execution_count": 71, 226 | "metadata": { 227 | "collapsed": false 228 | }, 229 | "outputs": [ 230 | { 231 | "name": "stdout", 232 | "output_type": "stream", 233 | "text": [ 234 | "before\n", 235 | "decorated\n", 236 | "after\n" 237 | ] 238 | } 239 | ], 240 | "source": [ 241 | "def decorator(inner):\n", 242 | " def inner_decorator():\n", 243 | " print('before')\n", 244 | " inner()\n", 245 | " print('after')\n", 246 | " return inner_decorator\n", 247 | "\n", 248 | "def decorated():\n", 249 | " print('decorated')\n", 250 | "\n", 251 | "f = decorator(decorated)\n", 252 | "f()" 253 | ] 254 | }, 255 | { 256 | "cell_type": "code", 257 | "execution_count": 72, 258 | "metadata": { 259 | "collapsed": false 260 | }, 261 | "outputs": [ 262 | { 263 | "name": "stdout", 264 | "output_type": "stream", 265 | "text": [ 266 | "before\n", 267 | "decorated\n", 268 | "after\n" 269 | ] 270 | } 271 | ], 272 | "source": [ 273 | "@decorator\n", 274 | "def decorated():\n", 275 | " print('decorated')\n", 276 | " \n", 277 | "decorated()" 278 | ] 279 | }, 280 | { 281 | "cell_type": "code", 282 | "execution_count": 73, 283 | "metadata": { 284 | "collapsed": false 285 | }, 286 | "outputs": [ 287 | { 288 | "name": "stdout", 289 | "output_type": "stream", 290 | "text": [ 291 | "1\n", 292 | "1.0271596908569336\n", 293 | "0.0001976490020751953\n", 294 | "2\n", 295 | "1.0024516582489014\n" 296 | ] 297 | } 298 | ], 299 | "source": [ 300 | "import time\n", 301 | "\n", 302 | "@functools.lru_cache()\n", 303 | "def slow_compute(n):\n", 304 | " time.sleep(1)\n", 305 | " print(n)\n", 306 | "\n", 307 | "start = time.time()\n", 308 | "slow_compute(1)\n", 309 | "print(time.time() - start)\n", 310 | "\n", 311 | "start = time.time()\n", 312 | "slow_compute(1)\n", 313 | "print(time.time() - start)\n", 314 | "\n", 315 | "start = time.time()\n", 316 | "slow_compute(2)\n", 317 | "print(time.time() - start)" 318 | ] 319 | }, 320 | { 321 | "cell_type": "markdown", 322 | "metadata": {}, 323 | "source": [ 324 | "## Object oriented" 325 | ] 326 | }, 327 | { 328 | "cell_type": "markdown", 329 | "metadata": {}, 330 | "source": [ 331 | "Object oriented programming is a paradigm that combines data with code into objects. The code can interact with and modify the data in an object. A program will be separated out into a number of different objects that interact with each other.\n", 332 | "\n", 333 | "Object oriented programming is a widely used paradigm and a variety of different languages support it including Python, C++, Java, PHP, Ruby, and many others.\n", 334 | "\n", 335 | "Each of these languages use slightly different syntax but the underlying design choices will be the same in each language. \n", 336 | "\n", 337 | "Objects __are__ things, their names often recognise this and are nouns. These might be physical things like a chair, or concepts like a number. \n", 338 | "\n", 339 | "While procedural programs make use of global information, object oriented design forgoes this global knowledge in favor of local knowledge. Objects contain information and can __do__ things. The information they contain are in attributes. The things they can do are in their methods (similar to functions, but attached to the object).\n", 340 | "\n", 341 | "Finally, to achieve the objective of the program objects must interact.\n", 342 | "\n", 343 | "We will look at the python syntax for creating objects later, first let's explore how objects might work in various scenarios. " 344 | ] 345 | }, 346 | { 347 | "cell_type": "markdown", 348 | "metadata": {}, 349 | "source": [ 350 | "##Designing Object Oriented Programs\n", 351 | "\n", 352 | "These are the simple building blocks for classes and objects. Just as with the other programming constructs available in python, although the language is relatively simple if used effectively they are very powerful.\n", 353 | "\n", 354 | "[Learn Python the Hard Way](http://learnpythonthehardway.org/book/ex43.html) has a very good description of how to design a program using the object oriented programming paradigm. The linked exercise particularly is worth reading.\n", 355 | "\n", 356 | "The best place to start is describing the problem. What are you trying to do? What are the items involved?" 357 | ] 358 | }, 359 | { 360 | "cell_type": "markdown", 361 | "metadata": { 362 | "collapsed": false 363 | }, 364 | "source": [ 365 | "###Example 1: A Laboratory Inventory\n", 366 | "\n", 367 | "I would like to keep track of all the __items__ in the __laboratory__ so I can easily find them the next time I need them. Both __equipment__ and __consumables__ would be tracked. We have multiple __rooms__, and items can be on __shelves__, in __refrigerators__, in __freezers__, etc. Items can also be in __boxes__ containing other items in all these places.\n", 368 | "\n", 369 | "The words in __bold__ would all be good ideas to turn into classes. Now we know some of the classes we will need we can start to think about what each of these classes should do, what the methods will be. Let's consider the consumables class:\n", 370 | "\n", 371 | "For consumables we will need to manage their use so there should be an initial quantity and a quantity remaining that is updated every time we use some. We want to make sure that temperature sensitive consumables are always stored at the correct temperature, and that flammables are stored in a flammables cabinet etc.\n", 372 | "\n", 373 | "The consumable class will need a number of attributes:\n", 374 | "\n", 375 | "* Initial quantity\n", 376 | "* Current quantity\n", 377 | "* Storage temperature\n", 378 | "* Flammability\n", 379 | "\n", 380 | "The consumable class will need methods to:\n", 381 | "\n", 382 | "* Update the quantity remaining\n", 383 | "* Check for improper storage?\n", 384 | "\n", 385 | "The consumable class might interact with the shelf, refrigerator, freezer, and/or box classes.\n", 386 | "\n", 387 | "Reading back through our description of consumables there is reference to a flammables cabinet that was not mentioned in our initial description of the problem. This is an iterative design process so we should go back and add a flammables cabinet class. " 388 | ] 389 | }, 390 | { 391 | "cell_type": "markdown", 392 | "metadata": {}, 393 | "source": [ 394 | "###Exercise: A Chart\n", 395 | "\n", 396 | "We have used matplotlib several times now to generate charts. If we were to create a charting library ourselves what are the objects we would use?\n", 397 | "\n", 398 | "I would like to plot some data on a chart. The data, as a series of points and lines, would be placed on a set of x-y axes that are numbered and labeled to accurately describe the data. There should be a grid so that values can be easily read from the chart.\n", 399 | "\n", 400 | "What are the classes you would use to create this plot?\n", 401 | "\n", 402 | "Pick one class and describe the methods it would have, and the other classes it might interact with." 403 | ] 404 | }, 405 | { 406 | "cell_type": "code", 407 | "execution_count": null, 408 | "metadata": { 409 | "collapsed": true 410 | }, 411 | "outputs": [], 412 | "source": [ 413 | "DISCUSSED IN CLASS\n", 414 | "\n", 415 | "Classes: \n", 416 | "data\n", 417 | "style\n", 418 | "source\n", 419 | "axes\n", 420 | "grid \n", 421 | "legend\n", 422 | "points\n", 423 | "lines\n", 424 | " - draw\n", 425 | " - position\n", 426 | " - color\n", 427 | " - size / thickness\n", 428 | " - symbol\n", 429 | " - slope / intercept\n", 430 | " - text representation\n", 431 | "figure\n", 432 | "plot\n", 433 | "title\n", 434 | "labels\n", 435 | "text" 436 | ] 437 | }, 438 | { 439 | "cell_type": "markdown", 440 | "metadata": {}, 441 | "source": [ 442 | "###Exercise 2: A Cookbook\n", 443 | "\n", 444 | "A system to manage different recipes, with their ingredients, equipment needed and instructions. Recipes should be scalable to different numbers of servings with the amount of ingredients adjusted appropriately and viewable in metric and imperial units. Nutritional information should be tracked.\n", 445 | "\n", 446 | "What are the classes you would use to create this system?\n", 447 | "\n", 448 | "Pick one class and describe the methods it would have, and the other classes it might interact with." 449 | ] 450 | }, 451 | { 452 | "cell_type": "code", 453 | "execution_count": null, 454 | "metadata": { 455 | "collapsed": true 456 | }, 457 | "outputs": [], 458 | "source": [ 459 | "DISCUSSED IN CLASS\n", 460 | "\n", 461 | "classes:\n", 462 | "appetizers\n", 463 | "main courses\n", 464 | "desserts\n", 465 | "recipe\n", 466 | " __Attributes/Methods__\n", 467 | " - preparation time\n", 468 | " - cooking time\n", 469 | " - serving portion\n", 470 | " - servings\n", 471 | " - name\n", 472 | " - nutritional information\n", 473 | " - ingredients\n", 474 | " - methods\n", 475 | " - scaling\n", 476 | " - equipment\n", 477 | " - special occasion \n", 478 | " - instructions\n", 479 | " __Interactions__\n", 480 | " - Cookbook\n", 481 | " - Ingredients\n", 482 | " - Instructions\n", 483 | " - Nutrition\n", 484 | " - Equipment\n", 485 | " - Scaling\n", 486 | " - Portions\n", 487 | "servings\n", 488 | "ingredients\n", 489 | " - quantity (mass/volume)\n", 490 | " - units\n", 491 | " - nutritional info\n", 492 | " - specific dietary info\n", 493 | " - cost\n", 494 | " - storage temperature\n", 495 | " - sustitute options\n", 496 | " - name\n", 497 | " - translations\n", 498 | " - alternative names\n", 499 | " - special handling instructions\n", 500 | " - sources\n", 501 | " * can interact with the following classes:\n", 502 | " * recipe\n", 503 | " * Instructions\n", 504 | " * Equipment\n", 505 | " * nutrition \n", 506 | " * scaling\n", 507 | " * portions \n", 508 | " * cookbook\n", 509 | " * units\n", 510 | "solid ingredients\n", 511 | "liquid ingredients\n", 512 | "equipment\n", 513 | "instructions\n", 514 | "nutritional info\n", 515 | "style\n", 516 | "units conversion\n", 517 | "scaling \n", 518 | "portions\n", 519 | "cookbook\n", 520 | "\n", 521 | "(recipe class can have an attribute, list of ingredients which are instances of another class themselves)\n", 522 | "(classes interact with each other often in an app like this)\n", 523 | "(instructions can be a class, and an instance of it can be saved as an attribute in Recipe class)" 524 | ] 525 | }, 526 | { 527 | "cell_type": "markdown", 528 | "metadata": { 529 | "collapsed": true 530 | }, 531 | "source": [ 532 | "[Building Skills in Object Oriented Design](http://www.itmaybeahack.com/homepage/books/oodesign.html) is a good resource to learn more about this process.\n", 533 | "\n", 534 | "##Syntax\n", 535 | "\n", 536 | "Now let's look at the syntax we use to work with objects in python.\n", 537 | "\n", 538 | "There is a tutorial in the [python documentation](https://docs.python.org/3.5/tutorial/classes.html)." 539 | ] 540 | }, 541 | { 542 | "cell_type": "markdown", 543 | "metadata": {}, 544 | "source": [ 545 | "Before we use an object in our program we must first define it. Just as we define a function with the *def* keyword, we use *class* to define a class. What is a class? Think of it as the template, or blueprint from which our objects will be made.\n", 546 | "\n", 547 | "Remember that in addition to code, objects can also contain data that can change so we may have many different instances of an object. Although each may contain different data they are all formed from the same class definition.\n", 548 | "\n", 549 | "As an example:" 550 | ] 551 | }, 552 | { 553 | "cell_type": "code", 554 | "execution_count": 74, 555 | "metadata": { 556 | "collapsed": false 557 | }, 558 | "outputs": [ 559 | { 560 | "name": "stdout", 561 | "output_type": "stream", 562 | "text": [ 563 | "<__main__.Person object at 0x7fa208c0b240> <__main__.Person object at 0x7fa208c0b128>\n" 564 | ] 565 | } 566 | ], 567 | "source": [ 568 | "class Person(object):\n", 569 | " \"\"\"A class definition for a person. The following attributes are supported:\n", 570 | " \n", 571 | " Attributes:\n", 572 | " name: A string representing the person's name.\n", 573 | " age: An integer representing the person's age.\"\"\"\n", 574 | "\n", 575 | " mammal = True\n", 576 | " \n", 577 | " \n", 578 | " def __init__(self, name, age):\n", 579 | " \"\"\"Return a Person object with name and age set to the values supplied\"\"\"\n", 580 | " self.name = name\n", 581 | " self.age = age\n", 582 | "\n", 583 | "\n", 584 | "person1 = Person('Alice', 25)\n", 585 | "person2 = Person('Bob', 30)\n", 586 | "\n", 587 | "print(person1, person2)\n" 588 | ] 589 | }, 590 | { 591 | "cell_type": "markdown", 592 | "metadata": {}, 593 | "source": [ 594 | "There is a lot happening above.\n", 595 | "\n", 596 | "__class Person(object):__ The *class* keyword begins the definition of our class. Here, we are naming the class *Person*. Next, *(object)* means that this class will inherit from the object class. This is not strictly necessary but is generally good practice. Inheritance will be discussed in greater depth next week. Finally, just as for a function definition we finish with a *colon*.\n", 597 | "\n", 598 | "__\"\"\"Documentation\"\"\"__ Next, a docstring provides important notes on usage.\n", 599 | "\n", 600 | "__mammal = True__ This is a class attribute. This is useful for defining data that our objects will need that is the same for all instances.\n", 601 | "\n", 602 | "**def __init__(self, name, age):** This is a method definition. The *def* keyword is used just as for functions. The first parameter here is *self* which refers to the object this method will be part of. The double underscores around the method name signify that this is a special method. In this case the \\_\\_init\\_\\_ method is called when the object is first instantiated.\n", 603 | "\n", 604 | "__self.name = name__ A common reason to define an \\_\\_init\\_\\_ method is to set instance attributes. In this class, name and age are set to the values supplied.\n", 605 | "\n", 606 | "\n", 607 | "That is all there is to this class definition. Next, we create two instances of this class. The values supplied will be passed to the \\_\\_init\\_\\_ method.\n", 608 | "\n", 609 | "Printing these objects don't provide a useful description of what they are. We can improve on this with another special method." 610 | ] 611 | }, 612 | { 613 | "cell_type": "code", 614 | "execution_count": 75, 615 | "metadata": { 616 | "collapsed": false 617 | }, 618 | "outputs": [ 619 | { 620 | "name": "stdout", 621 | "output_type": "stream", 622 | "text": [ 623 | "Alice who is 25 years old. Bob who is 30 years old.\n" 624 | ] 625 | } 626 | ], 627 | "source": [ 628 | "class Person(object):\n", 629 | " \"\"\"A class definition for a person. The following attributes are supported:\n", 630 | " \n", 631 | " Attributes:\n", 632 | " name: A string representing the person's name.\n", 633 | " age: An integer representing the person's age.\"\"\"\n", 634 | "\n", 635 | " mammal = True\n", 636 | " \n", 637 | " \n", 638 | " def __init__(self, name, age):\n", 639 | " \"\"\"Return a Person object with name and age set to the values supplied\"\"\"\n", 640 | " self.name = name\n", 641 | " self.age = age\n", 642 | " \n", 643 | " \n", 644 | " def __str__(self):\n", 645 | " return '{0} who is {1} years old.'.format(self.name, self.age)\n", 646 | "\n", 647 | "\n", 648 | "person1 = Person('Alice', 25)\n", 649 | "person2 = Person('Bob', 30)\n", 650 | "\n", 651 | "print(person1, person2)" 652 | ] 653 | }, 654 | { 655 | "cell_type": "markdown", 656 | "metadata": {}, 657 | "source": [ 658 | "[There are many more special methods](https://docs.python.org/3.5/reference/datamodel.html#special-method-names)." 659 | ] 660 | }, 661 | { 662 | "cell_type": "markdown", 663 | "metadata": {}, 664 | "source": [ 665 | "Before we go on a note of caution is needed for class attributes. If only using strings and numbers the behaviour will likely be much as you expect. However, if using a list, dictionary, or other similar type you may get a surprise." 666 | ] 667 | }, 668 | { 669 | "cell_type": "code", 670 | "execution_count": 76, 671 | "metadata": { 672 | "collapsed": false 673 | }, 674 | "outputs": [ 675 | { 676 | "name": "stdout", 677 | "output_type": "stream", 678 | "text": [ 679 | "['Charlie', 'Danielle'] ['Charlie', 'Danielle']\n" 680 | ] 681 | } 682 | ], 683 | "source": [ 684 | "class Person(object):\n", 685 | " \"\"\"A class definition for a person. The following attributes are supported:\n", 686 | " \n", 687 | " Attributes:\n", 688 | " name: A string representing the person's name.\n", 689 | " age: An integer representing the person's age.\"\"\"\n", 690 | "\n", 691 | " friends = []\n", 692 | " \n", 693 | " \n", 694 | " def __init__(self, name, age):\n", 695 | " \"\"\"Return a Person object with name and age set to the values supplied\"\"\"\n", 696 | " self.name = name\n", 697 | " self.age = age\n", 698 | " \n", 699 | " \n", 700 | " def __str__(self):\n", 701 | " return '{0} who is {1} years old'.format(self.name, self.age)\n", 702 | "\n", 703 | "\n", 704 | "person1 = Person('Alice', 25)\n", 705 | "person2 = Person('Bob', 30)\n", 706 | "\n", 707 | "person1.friends.append('Charlie')\n", 708 | "person2.friends.append('Danielle')\n", 709 | "\n", 710 | "print(person1.friends, person2.friends)" 711 | ] 712 | }, 713 | { 714 | "cell_type": "markdown", 715 | "metadata": {}, 716 | "source": [ 717 | "Both of our objects point to the same instance of the list type so adding a new friend to either object shows up in both.\n", 718 | "\n", 719 | "The solution to this is creating our *friends* attribute only at instantiation of the object. This can be done by creating it in the \\_\\_init\\_\\_ method." 720 | ] 721 | }, 722 | { 723 | "cell_type": "code", 724 | "execution_count": 77, 725 | "metadata": { 726 | "collapsed": false 727 | }, 728 | "outputs": [ 729 | { 730 | "name": "stdout", 731 | "output_type": "stream", 732 | "text": [ 733 | "['Charlie'] ['Danielle']\n" 734 | ] 735 | } 736 | ], 737 | "source": [ 738 | "class Person(object):\n", 739 | " \"\"\"A class definition for a person. The following attributes are supported:\n", 740 | " \n", 741 | " Attributes:\n", 742 | " name: A string representing the person's name.\n", 743 | " age: An integer representing the person's age.\"\"\"\n", 744 | "\n", 745 | " \n", 746 | " \n", 747 | " def __init__(self, name, age):\n", 748 | " \"\"\"Return a Person object with name and age set to the values supplied\"\"\"\n", 749 | " self.name = name\n", 750 | " self.age = age\n", 751 | " self.friends = []\n", 752 | " \n", 753 | " \n", 754 | " def __str__(self):\n", 755 | " return '{0} who is {1} years old'.format(self.name, self.age)\n", 756 | "\n", 757 | "\n", 758 | "person1 = Person('Alice', 25)\n", 759 | "person2 = Person('Bob', 30)\n", 760 | "\n", 761 | "person1.friends.append('Charlie')\n", 762 | "person2.friends.append('Danielle')\n", 763 | "\n", 764 | "print(person1.friends, person2.friends)" 765 | ] 766 | }, 767 | { 768 | "cell_type": "markdown", 769 | "metadata": { 770 | "collapsed": true 771 | }, 772 | "source": [ 773 | "Objects have their own namespace, although we have created variables called name, age, and friends they can only be accessed in the context of the object." 774 | ] 775 | }, 776 | { 777 | "cell_type": "code", 778 | "execution_count": 78, 779 | "metadata": { 780 | "collapsed": false 781 | }, 782 | "outputs": [ 783 | { 784 | "name": "stdout", 785 | "output_type": "stream", 786 | "text": [ 787 | "This works: ['Charlie']\n" 788 | ] 789 | }, 790 | { 791 | "ename": "NameError", 792 | "evalue": "name 'friends' is not defined", 793 | "output_type": "error", 794 | "traceback": [ 795 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 796 | "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", 797 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'This works:'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mperson1\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfriends\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'This does not work:'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mfriends\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", 798 | "\u001b[1;31mNameError\u001b[0m: name 'friends' is not defined" 799 | ] 800 | } 801 | ], 802 | "source": [ 803 | "print('This works:', person1.friends)\n", 804 | "print('This does not work:', friends)" 805 | ] 806 | }, 807 | { 808 | "cell_type": "markdown", 809 | "metadata": {}, 810 | "source": [ 811 | "We are not limited to special methods when creating classes. Standard functions, or in this context methods, are an integral part of object oriented programming. Their definition is identical to special methods and functions outside of classes." 812 | ] 813 | }, 814 | { 815 | "cell_type": "code", 816 | "execution_count": 79, 817 | "metadata": { 818 | "collapsed": false 819 | }, 820 | "outputs": [ 821 | { 822 | "name": "stdout", 823 | "output_type": "stream", 824 | "text": [ 825 | "['Charlie'] ['Danielle']\n" 826 | ] 827 | } 828 | ], 829 | "source": [ 830 | "class Person(object):\n", 831 | " \"\"\"A class definition for a person. The following attributes are supported:\n", 832 | " \n", 833 | " Attributes:\n", 834 | " name: A string representing the person's name.\n", 835 | " age: An integer representing the person's age.\"\"\"\n", 836 | "\n", 837 | " \n", 838 | " \n", 839 | " def __init__(self, name, age):\n", 840 | " \"\"\"Return a Person object with name and age set to the values supplied\"\"\"\n", 841 | " self.name = name\n", 842 | " self.age = age\n", 843 | " self.friends = []\n", 844 | " \n", 845 | " \n", 846 | " def __str__(self):\n", 847 | " \"\"\"Return a string representation of the object\"\"\"\n", 848 | " return '{0} who is {1} years old'.format(self.name, self.age)\n", 849 | " \n", 850 | " \n", 851 | " def add_friend(self, friend):\n", 852 | " \"\"\"Add a friend\"\"\"\n", 853 | " self.friends.append(friend)\n", 854 | "\n", 855 | " \n", 856 | "person1 = Person('Alice', 25)\n", 857 | "person2 = Person('Bob', 30)\n", 858 | "\n", 859 | "person1.add_friend('Charlie')\n", 860 | "person2.add_friend('Danielle')\n", 861 | "\n", 862 | "print(person1.friends, person2.friends)" 863 | ] 864 | }, 865 | { 866 | "cell_type": "markdown", 867 | "metadata": {}, 868 | "source": [ 869 | "###Private vs Public\n", 870 | "\n", 871 | "Some programming languages support hiding methods and attributes in an object. This can be useful to simplify the public interface someone using the class will see while still breaking up components into manageable blocks 'under-the-hood'. We will discuss designing the public interface in detail in week 5.\n", 872 | "\n", 873 | "Python does not support private variables beyond convention. Names prefixed with a underscore are assumed to be private. This means they may be changed without warning between different versions of the package. For public attributes/methods this is highly discouraged." 874 | ] 875 | }, 876 | { 877 | "cell_type": "markdown", 878 | "metadata": {}, 879 | "source": [ 880 | "### Glossary\n", 881 | "\n", 882 | "__Class__: Our definition, or template, for an object.\n", 883 | "\n", 884 | "__Object__: An instance of a class.\n", 885 | "\n", 886 | "__Method__: A function that belongs to an object\n", 887 | "\n", 888 | "__Attribute__: A characteristic of an object, these can be data attributes and methods." 889 | ] 890 | }, 891 | { 892 | "cell_type": "markdown", 893 | "metadata": {}, 894 | "source": [ 895 | "##Exercises\n", 896 | "\n", 897 | "Please send to me prior to class next week.\n", 898 | "\n" 899 | ] 900 | }, 901 | { 902 | "cell_type": "markdown", 903 | "metadata": {}, 904 | "source": [ 905 | "* Pick one of the examples above (the laboratory inventory manager, the chart or the cookbook) and write out the classes it requires with their methods. You don't need to complete the code in the methods, using the `pass` keyword can act as a placeholder, e.g." 906 | ] 907 | }, 908 | { 909 | "cell_type": "code", 910 | "execution_count": 80, 911 | "metadata": { 912 | "collapsed": true 913 | }, 914 | "outputs": [], 915 | "source": [ 916 | "class Test(object):\n", 917 | " \n", 918 | " \n", 919 | " def do_something(self):\n", 920 | " pass" 921 | ] 922 | }, 923 | { 924 | "cell_type": "markdown", 925 | "metadata": {}, 926 | "source": [ 927 | "* Add documentation to the classes and their methods" 928 | ] 929 | }, 930 | { 931 | "cell_type": "code", 932 | "execution_count": null, 933 | "metadata": { 934 | "collapsed": true 935 | }, 936 | "outputs": [], 937 | "source": [] 938 | } 939 | ], 940 | "metadata": { 941 | "kernelspec": { 942 | "display_name": "Python 3", 943 | "language": "python", 944 | "name": "python3" 945 | }, 946 | "language_info": { 947 | "codemirror_mode": { 948 | "name": "ipython", 949 | "version": 3 950 | }, 951 | "file_extension": ".py", 952 | "mimetype": "text/x-python", 953 | "name": "python", 954 | "nbconvert_exporter": "python", 955 | "pygments_lexer": "ipython3", 956 | "version": "3.5.1" 957 | } 958 | }, 959 | "nbformat": 4, 960 | "nbformat_minor": 0 961 | } 962 | -------------------------------------------------------------------------------- /Wk01/Wk01-Overview.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Week 1 - Getting Started" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 3, 13 | "metadata": { 14 | "collapsed": false 15 | }, 16 | "outputs": [ 17 | { 18 | "name": "stdout", 19 | "output_type": "stream", 20 | "text": [ 21 | "Numpy: 1.10.4\n" 22 | ] 23 | } 24 | ], 25 | "source": [ 26 | "import numpy as np\n", 27 | "\n", 28 | "print(\"Numpy:\", np.__version__)" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "#Python Summary\n", 36 | "\n", 37 | "##Further information\n", 38 | "\n", 39 | "More information is usually available with the `help` function. Using ? brings up the same information in ipython. \n", 40 | "\n", 41 | "Using the `dir` function lists all the options available from a variable." 42 | ] 43 | }, 44 | { 45 | "cell_type": "markdown", 46 | "metadata": {}, 47 | "source": [ 48 | "help(np)\n", 49 | "\n", 50 | "np?\n", 51 | "\n", 52 | "dir(np)" 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "metadata": {}, 58 | "source": [ 59 | "##Variables\n", 60 | "\n", 61 | "A variable is simply a name for something. One of the simplest tasks is printing the value of a variable.\n", 62 | "\n", 63 | "Printing can be customized using the format method on strings." 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": 13, 69 | "metadata": { 70 | "collapsed": false 71 | }, 72 | "outputs": [ 73 | { 74 | "name": "stdout", 75 | "output_type": "stream", 76 | "text": [ 77 | "We're in Bethesda zip code 20892 , 71.9 m above sea level\n", 78 | "We're in Bethesda zip code 20892, 71.9m above sea level\n", 79 | "We're in Bethesda zip code 20892, 71.9m above sea level\n", 80 | "We're in Bethesda zip code 20892, 7.19e+01m above sea level\n" 81 | ] 82 | } 83 | ], 84 | "source": [ 85 | "location = 'Bethesda'\n", 86 | "zip_code = 20892\n", 87 | "elevation = 71.9\n", 88 | "\n", 89 | "print(\"We're in\", location, \"zip code\", zip_code, \", \", elevation, \"m above sea level\")\n", 90 | "print(\"We're in \" + location + \" zip code \" + str(zip_code) + \", \" + str(elevation) + \"m above sea level\")\n", 91 | "print(\"We're in {0} zip code {1}, {2}m above sea level\".format(location, zip_code, elevation))\n", 92 | "print(\"We're in {0} zip code {1}, {2:.2e}m above sea level\".format(location, zip_code, elevation))" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": {}, 98 | "source": [ 99 | "##Types\n", 100 | "\n", 101 | "A number of different types are available as part of the standard library. The following links to the documentation provide a summary.\n", 102 | "\n", 103 | "* https://docs.python.org/3.5/library/stdtypes.html\n", 104 | "* https://docs.python.org/3.5/tutorial/datastructures.html\n", 105 | "\n", 106 | "Other types are available from other packages and can be created to support special situations.\n", 107 | "\n", 108 | "A variety of different methods are available depending on the type." 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": 1, 114 | "metadata": { 115 | "collapsed": false 116 | }, 117 | "outputs": [ 118 | { 119 | "name": "stdout", 120 | "output_type": "stream", 121 | "text": [ 122 | "List: [1, 2, 3, 4, 4] 5 True\n", 123 | "Tuple: (1, 2, 3, 4, 4) 5 True\n", 124 | "Set: {1, 2, 3, 4} 4 True\n", 125 | "Dictionary: {'a': 1, 3: 's', 2.5: 't', 'b': 2} 4 True\n" 126 | ] 127 | } 128 | ], 129 | "source": [ 130 | "# Sequences\n", 131 | "\n", 132 | "# Lists\n", 133 | "l = [1,2,3,4,4]\n", 134 | "print(\"List:\", l, len(l), 1 in l)\n", 135 | "\n", 136 | "# Tuples\n", 137 | "t = (1,2,3,4,4)\n", 138 | "print(\"Tuple:\", t, len(t), 1 in t)\n", 139 | "\n", 140 | "# Sets\n", 141 | "s = set([1,2,3,4,4])\n", 142 | "print(\"Set:\", s, len(s), 1 in s)\n", 143 | "\n", 144 | "# Dictionaries\n", 145 | "# Dictionaries map hashable values to arbitrary objects\n", 146 | "d = {'a': 1, 'b': 2, 3: 's', 2.5: 't'}\n", 147 | "print(\"Dictionary:\", d, len(d), 'a' in d)" 148 | ] 149 | }, 150 | { 151 | "cell_type": "markdown", 152 | "metadata": {}, 153 | "source": [ 154 | "##Conditionals\n", 155 | "\n", 156 | "https://docs.python.org/3.5/tutorial/controlflow.html" 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": 2, 162 | "metadata": { 163 | "collapsed": false 164 | }, 165 | "outputs": [ 166 | { 167 | "name": "stdout", 168 | "output_type": "stream", 169 | "text": [ 170 | "Should be printed 50% of the time\n" 171 | ] 172 | } 173 | ], 174 | "source": [ 175 | "import random\n", 176 | "\n", 177 | "if random.random() < 0.5:\n", 178 | " print(\"Should be printed 50% of the time\")\n", 179 | "elif random.random() < 0.5:\n", 180 | " print(\"Should be primted 25% of the time\")\n", 181 | "else:\n", 182 | " print(\"Should be printed 25% of the time\")\n" 183 | ] 184 | }, 185 | { 186 | "cell_type": "markdown", 187 | "metadata": {}, 188 | "source": [ 189 | "##Loops\n", 190 | "\n", 191 | "https://docs.python.org/3.5/tutorial/controlflow.html" 192 | ] 193 | }, 194 | { 195 | "cell_type": "code", 196 | "execution_count": 3, 197 | "metadata": { 198 | "collapsed": false 199 | }, 200 | "outputs": [ 201 | { 202 | "name": "stdout", 203 | "output_type": "stream", 204 | "text": [ 205 | "a\n", 206 | "b\n", 207 | "c\n", 208 | "d\n", 209 | "Else\n", 210 | "a\n", 211 | "c\n" 212 | ] 213 | } 214 | ], 215 | "source": [ 216 | "for i in ['a', 'b', 'c', 'd']:\n", 217 | " print(i)\n", 218 | "else:\n", 219 | " print('Else')\n", 220 | " \n", 221 | "for i in ['a', 'b', 'c', 'd']:\n", 222 | " if i == 'b':\n", 223 | " continue\n", 224 | " elif i == 'd':\n", 225 | " break\n", 226 | " print(i)\n", 227 | "else:\n", 228 | " print('Else')" 229 | ] 230 | }, 231 | { 232 | "cell_type": "markdown", 233 | "metadata": {}, 234 | "source": [ 235 | "##Functions\n", 236 | "\n", 237 | "https://docs.python.org/3.5/tutorial/controlflow.html" 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": 4, 243 | "metadata": { 244 | "collapsed": false 245 | }, 246 | "outputs": [ 247 | { 248 | "name": "stdout", 249 | "output_type": "stream", 250 | "text": [ 251 | "False True\n" 252 | ] 253 | } 254 | ], 255 | "source": [ 256 | "def is_even(n):\n", 257 | " return not n % 2\n", 258 | "\n", 259 | "print(is_even(1), is_even(2))" 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": 5, 265 | "metadata": { 266 | "collapsed": false 267 | }, 268 | "outputs": [ 269 | { 270 | "name": "stdout", 271 | "output_type": "stream", 272 | "text": [ 273 | "[1, 4, 9, 16, 25]\n" 274 | ] 275 | } 276 | ], 277 | "source": [ 278 | "def first_n_squared_numbers(n=5):\n", 279 | " return [i**2 for i in range(1,n+1)]\n", 280 | "\n", 281 | "print(first_n_squared_numbers())" 282 | ] 283 | }, 284 | { 285 | "cell_type": "code", 286 | "execution_count": 6, 287 | "metadata": { 288 | "collapsed": false 289 | }, 290 | "outputs": [ 291 | { 292 | "name": "stdout", 293 | "output_type": "stream", 294 | "text": [ 295 | "1 1 2 3 5 8\n" 296 | ] 297 | } 298 | ], 299 | "source": [ 300 | "def next_fibonacci(status=[]):\n", 301 | " if len(status) < 2:\n", 302 | " status.append(1)\n", 303 | " return 1\n", 304 | " status.append(status[-2] + status[-1])\n", 305 | " return status[-1]\n", 306 | "\n", 307 | "print(next_fibonacci(), next_fibonacci(), next_fibonacci(), next_fibonacci(), next_fibonacci(), next_fibonacci())" 308 | ] 309 | }, 310 | { 311 | "cell_type": "code", 312 | "execution_count": 7, 313 | "metadata": { 314 | "collapsed": false 315 | }, 316 | "outputs": [ 317 | { 318 | "name": "stdout", 319 | "output_type": "stream", 320 | "text": [ 321 | "1\n", 322 | "2\n", 323 | "3\n", 324 | "4\n", 325 | "c 3\n", 326 | "a 1\n", 327 | "b 2\n" 328 | ] 329 | } 330 | ], 331 | "source": [ 332 | "def accepts_anything(*args, **kwargs):\n", 333 | " for a in args:\n", 334 | " print(a)\n", 335 | " for k in kwargs:\n", 336 | " print(k, kwargs[k])\n", 337 | " \n", 338 | "accepts_anything(1,2,3,4, a=1, b=2, c=3)" 339 | ] 340 | }, 341 | { 342 | "cell_type": "code", 343 | "execution_count": 8, 344 | "metadata": { 345 | "collapsed": false 346 | }, 347 | "outputs": [ 348 | { 349 | "name": "stdout", 350 | "output_type": "stream", 351 | "text": [ 352 | "25\n", 353 | "[{'age': 30, 'name': 'Alice'}, {'age': 35, 'name': 'Bob'}, {'age': 35, 'name': 'Charlie'}, {'age': 25, 'name': 'Dennis'}]\n", 354 | "[{'age': 25, 'name': 'Dennis'}, {'age': 30, 'name': 'Alice'}, {'age': 35, 'name': 'Bob'}, {'age': 35, 'name': 'Charlie'}]\n" 355 | ] 356 | } 357 | ], 358 | "source": [ 359 | "# For quick and simple functions a lambda expression can be a useful approach. \n", 360 | "# Standard functions are always a valid alternative and often make code clearer.\n", 361 | "f = lambda x: x**2\n", 362 | "print(f(5))\n", 363 | "\n", 364 | "people = [{'name': 'Alice', 'age': 30}, \n", 365 | " {'name': 'Bob', 'age': 35}, \n", 366 | " {'name': 'Charlie', 'age': 35}, \n", 367 | " {'name': 'Dennis', 'age': 25}]\n", 368 | "print(people)\n", 369 | "people.sort(key=lambda x: x['age'])\n", 370 | "print(people)" 371 | ] 372 | }, 373 | { 374 | "cell_type": "markdown", 375 | "metadata": { 376 | "collapsed": true 377 | }, 378 | "source": [ 379 | "##Numpy\n", 380 | "\n", 381 | "http://docs.scipy.org/doc/numpy/reference/" 382 | ] 383 | }, 384 | { 385 | "cell_type": "code", 386 | "execution_count": 21, 387 | "metadata": { 388 | "collapsed": false 389 | }, 390 | "outputs": [ 391 | { 392 | "name": "stdout", 393 | "output_type": "stream", 394 | "text": [ 395 | "[[1 2 3]\n", 396 | " [4 5 6]\n", 397 | " [7 8 9]]\n", 398 | "[1 2 3]\n", 399 | "[[5 6]\n", 400 | " [8 9]]\n", 401 | "[[ 3 4 5]\n", 402 | " [ 6 7 8]\n", 403 | " [ 9 10 11]]\n", 404 | "[[ 4 6 8]\n", 405 | " [ 7 9 11]\n", 406 | " [10 12 14]]\n", 407 | "[[14 16 18]\n", 408 | " [27 29 31]\n", 409 | " [40 42 44]]\n" 410 | ] 411 | } 412 | ], 413 | "source": [ 414 | "a = np.array([[1,2,3], [4,5,6], [7,8,9]])\n", 415 | "print(a)\n", 416 | "print(a[1:,1:])\n", 417 | "a = a + 2\n", 418 | "print(a)\n", 419 | "a = a + np.array([1,2,3])\n", 420 | "print(a)\n", 421 | "a = a + np.array([[10],[20],[30]])\n", 422 | "print(a)" 423 | ] 424 | }, 425 | { 426 | "cell_type": "code", 427 | "execution_count": 59, 428 | "metadata": { 429 | "collapsed": false 430 | }, 431 | "outputs": [ 432 | { 433 | "name": "stdout", 434 | "output_type": "stream", 435 | "text": [ 436 | "29.0 [ 27. 29. 31.] [ 16. 29. 42.]\n" 437 | ] 438 | } 439 | ], 440 | "source": [ 441 | "print(a.mean(), a.mean(axis=0), a.mean(axis=1))" 442 | ] 443 | }, 444 | { 445 | "cell_type": "code", 446 | "execution_count": 65, 447 | "metadata": { 448 | "collapsed": true 449 | }, 450 | "outputs": [], 451 | "source": [ 452 | "import matplotlib.pyplot as plt\n", 453 | "\n", 454 | "%matplotlib inline" 455 | ] 456 | }, 457 | { 458 | "cell_type": "code", 459 | "execution_count": 68, 460 | "metadata": { 461 | "collapsed": false 462 | }, 463 | "outputs": [ 464 | { 465 | "data": { 466 | "image/png": "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\n", 467 | "text/plain": [ 468 | "" 469 | ] 470 | }, 471 | "metadata": {}, 472 | "output_type": "display_data" 473 | } 474 | ], 475 | "source": [ 476 | "x = np.linspace(0, 3*2*np.pi, 500)\n", 477 | "\n", 478 | "plt.plot(x, np.sin(x))\n", 479 | "plt.show()" 480 | ] 481 | }, 482 | { 483 | "cell_type": "markdown", 484 | "metadata": {}, 485 | "source": [ 486 | "#Exercises" 487 | ] 488 | }, 489 | { 490 | "cell_type": "code", 491 | "execution_count": 3, 492 | "metadata": { 493 | "collapsed": false 494 | }, 495 | "outputs": [ 496 | { 497 | "name": "stdout", 498 | "output_type": "stream", 499 | "text": [ 500 | "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG\n", 501 | "ThE qUiCk BrOwN fOx JuMpS oVeR tHe LaZy DoG\n", 502 | "god yzal eht revo spmuj xof nworb kciuq ehT\n", 503 | "ehT kciuq nworb xof spmuj revo eht yzal god\n", 504 | "1.2346E+09 1.2346E+9\n" 505 | ] 506 | } 507 | ], 508 | "source": [ 509 | "a = \"The quick brown fox jumps over the lazy dog\"\n", 510 | "b = 1234567890.0\n", 511 | "\n", 512 | "## Print the variable `a` in all uppercase\n", 513 | "print(a.upper())\n", 514 | "\n", 515 | "## Print the variable `a` with every other letter in uppercase\n", 516 | "def capEveryOtherLetter(str):\n", 517 | " ans = \"\" \n", 518 | " i = True # capitalize\n", 519 | " for char in str:\n", 520 | " if i == True:\n", 521 | " ans += char.upper() \n", 522 | " else:\n", 523 | " ans += char.lower()\n", 524 | " if char != ' ': # if character is not a space\n", 525 | " i = not i # toggle i between False/True\n", 526 | " return ans\n", 527 | "\n", 528 | "print(capEveryOtherLetter(a))\n", 529 | "\n", 530 | "## Print the variable `a` in reverse, i.e. god yzal ...\n", 531 | "def reverse(str):\n", 532 | " rev = \"\"\n", 533 | " for char in str:\n", 534 | " rev = char + rev\n", 535 | " return rev\n", 536 | " \n", 537 | "print(reverse(a))\n", 538 | "\n", 539 | "## Print the variable `a` with the words reversed, i.e. ehT kciuq ...\n", 540 | "def reverseWords(str):\n", 541 | " words = str.split()\n", 542 | " for i in range(len(words)):\n", 543 | " words[i] = reverse(words[i])\n", 544 | " rev = \" \".join(words)\n", 545 | " return rev\n", 546 | "\n", 547 | "print(reverseWords(a))\n", 548 | "\n", 549 | "## Print the variable `b` in scientific notation with 4 decimal places\n", 550 | "## In python, you have floats and decimals that can be rounded. \n", 551 | "## If you care about the accuracy of rounding, use decimal type. \n", 552 | "## If you use floats, you will have issues with accuracy.\n", 553 | "## Why does ans output E+09 and ans2 E+9? \n", 554 | "from decimal import Decimal\n", 555 | "ans = '%.4E' % Decimal(b)\n", 556 | "ans2 = \"{:.4E}\".format(Decimal(b))\n", 557 | "print(ans, ans2)" 558 | ] 559 | }, 560 | { 561 | "cell_type": "markdown", 562 | "metadata": {}, 563 | "source": [ 564 | "* Print the variable `a` in all uppercase\n", 565 | "* Print the variable `a` with every other letter in uppercase\n", 566 | "* Print the variable `a` in reverse, i.e. god yzal ...\n", 567 | "* Print the variable `a` with the words reversed, i.e. ehT kciuq ...\n", 568 | "* Print the variable `b` in scientific notation with 4 decimal places" 569 | ] 570 | }, 571 | { 572 | "cell_type": "code", 573 | "execution_count": null, 574 | "metadata": { 575 | "collapsed": true 576 | }, 577 | "outputs": [], 578 | "source": [] 579 | }, 580 | { 581 | "cell_type": "code", 582 | "execution_count": 1, 583 | "metadata": { 584 | "collapsed": false 585 | }, 586 | "outputs": [ 587 | { 588 | "name": "stdout", 589 | "output_type": "stream", 590 | "text": [ 591 | "{'name': 'Bob', 'age': 35},{'name': 'Alice', 'age': 30},{'name': 'Eve', 'age': 20},{'name': 'Gail', 'age': 30},{'name': 'Dennis', 'age': 25},{'name': 'Charlie', 'age': 35},{'name': 'Fred', 'age': 25}\n", 592 | "[{'name': 'Eve', 'age': 20}, {'name': 'Dennis', 'age': 25}, {'name': 'Fred', 'age': 25}, {'name': 'Alice', 'age': 30}, {'name': 'Gail', 'age': 30}, {'name': 'Bob', 'age': 35}, {'name': 'Charlie', 'age': 35}]\n", 593 | "[{'name': 'Eve', 'age': 20}, {'name': 'Dennis', 'age': 25}, {'name': 'Fred', 'age': 25}, {'name': 'Alice', 'age': 30}, {'name': 'Gail', 'age': 30}, {'name': 'Bob', 'age': 35}, {'name': 'Charlie', 'age': 35}]\n" 594 | ] 595 | } 596 | ], 597 | "source": [ 598 | "people = [{'name': 'Bob', 'age': 35}, \n", 599 | " {'name': 'Alice', 'age': 30}, \n", 600 | " {'name': 'Eve', 'age': 20},\n", 601 | " {'name': 'Gail', 'age': 30},\n", 602 | " {'name': 'Dennis', 'age': 25},\n", 603 | " {'name': 'Charlie', 'age': 35}, \n", 604 | " {'name': 'Fred', 'age': 25},]\n", 605 | "\n", 606 | "## Print the items in people as comma-separated values.\n", 607 | "## uses map with str conversion function, as join() expects str, not dict\n", 608 | "peopleCommaSeparated = \",\".join(map(str, people))\n", 609 | "print(peopleCommaSeparated)\n", 610 | "\n", 611 | "## Sort people so that they are ordered by age, and print.\n", 612 | "## sort() only works with lists, whereas sorted() accepts any iterable\n", 613 | "peopleSortedByAge = sorted(people, key = lambda person: person['age'])\n", 614 | "print(peopleSortedByAge)\n", 615 | "\n", 616 | "## Sort people so that they are ordered by age first, and then their names, \n", 617 | "## i.e., Bob and Charlie should be next to each other due to their ages \n", 618 | "## with Bob first due to his name.\n", 619 | "peopleSortedByAgeAndName = sorted(people, key = lambda person: (person['age'], person['name']))\n", 620 | "print(peopleSortedByAgeAndName)" 621 | ] 622 | }, 623 | { 624 | "cell_type": "markdown", 625 | "metadata": {}, 626 | "source": [ 627 | "* Print the items in `people` as comma seperated values\n", 628 | "* Sort `people` so that they are ordered by age, and print\n", 629 | "* Sort `people` so that they are ordered by age first, and then their names, i.e. Bob and Charlie should be next to each other due to their ages with Bob first due to his name." 630 | ] 631 | }, 632 | { 633 | "cell_type": "code", 634 | "execution_count": null, 635 | "metadata": { 636 | "collapsed": false 637 | }, 638 | "outputs": [], 639 | "source": [] 640 | }, 641 | { 642 | "cell_type": "markdown", 643 | "metadata": {}, 644 | "source": [ 645 | "* Write a function that returns the first n prime numbers\n", 646 | "* Given a list of coordinates calculate the distance using the (Euclidean distance)[https://en.wikipedia.org/wiki/Euclidean_distance]\n", 647 | "* Given a list of coordinates arrange them in such a way that the distance traveled is minimized (the itertools module may be useful).\n" 648 | ] 649 | }, 650 | { 651 | "cell_type": "code", 652 | "execution_count": null, 653 | "metadata": { 654 | "collapsed": true 655 | }, 656 | "outputs": [], 657 | "source": [ 658 | "coords = [(0,0), (10,5), (10,10), (5,10), (3,3), (3,7), (12,3), (10,11)]\n", 659 | "\n" 660 | ] 661 | }, 662 | { 663 | "cell_type": "markdown", 664 | "metadata": {}, 665 | "source": [ 666 | "* Print the standard deviation of each row in a numpy array\n", 667 | "* Print only the values greater than 90 in a numpy array\n", 668 | "* From a numpy array display the values in each row in a seperate plot (the subplots method may be useful)" 669 | ] 670 | }, 671 | { 672 | "cell_type": "code", 673 | "execution_count": 27, 674 | "metadata": { 675 | "collapsed": false 676 | }, 677 | "outputs": [ 678 | { 679 | "name": "stdout", 680 | "output_type": "stream", 681 | "text": [ 682 | "[[44 47 64 67 67 9 83 21 36 87 70 88 88 12 58 65 39 87 46 88]\n", 683 | " [81 37 25 77 72 9 20 80 69 79 47 64 82 99 88 49 29 19 19 14]\n", 684 | " [39 32 65 9 57 32 31 74 23 35 75 55 28 34 0 0 36 53 5 38]\n", 685 | " [17 79 4 42 58 31 1 65 41 57 35 11 46 82 91 0 14 99 53 12]\n", 686 | " [42 84 75 68 6 68 47 3 76 52 78 15 20 99 58 23 79 13 85 48]\n", 687 | " [49 69 41 35 64 95 69 94 0 50 36 34 48 93 3 98 42 77 21 73]\n", 688 | " [ 0 10 43 58 23 59 2 98 62 35 94 67 82 46 99 20 81 50 27 14]\n", 689 | " [41 58 65 36 10 86 43 11 2 51 80 32 54 0 38 19 46 42 56 60]\n", 690 | " [77 30 24 2 3 94 98 13 40 72 19 95 72 26 66 52 67 61 14 96]\n", 691 | " [ 4 67 11 86 77 75 56 16 24 29 21 25 80 60 61 83 33 32 70 85]] \n", 692 | "\n", 693 | "[ 25.08804496 28.47011591 21.7795202 30.07972739 28.97667165\n", 694 | " 28.50697283 31.07008207 23.401923 32.10681392 27.20087315] \n", 695 | "\n", 696 | "[99 91 99 99 95 94 93 98 98 94 99 94 98 95 96] \n", 697 | "\n" 698 | ] 699 | } 700 | ], 701 | "source": [ 702 | "import numpy as np\n", 703 | "np.random.seed(0)\n", 704 | "a = np.random.randint(0, 100, size=(10,20))\n", 705 | "print(a, \"\\n\")\n", 706 | "\n", 707 | "## Print the standard deviation (σ) of each row in a numpy array\n", 708 | "## in a 2D array, columns are axis = 0, and rows are axis = 1\n", 709 | "row_std = np.std(a, axis=1)\n", 710 | "print(row_std, \"\\n\")\n", 711 | "\n", 712 | "## Print only the values greater than 90 in a numpy array\n", 713 | "truncated = a[a > 90]\n", 714 | "print(truncated, \"\\n\")\n", 715 | "\n", 716 | "## From a numpy array, display the values in each row in a separate plot \n", 717 | "## (the subplots method) may be useful\n", 718 | "import matplotlib.pyplot as plt\n", 719 | "\n", 720 | "plt.plot(a[0])\n", 721 | "plt.subplot(211)\n", 722 | "plt.show()" 723 | ] 724 | }, 725 | { 726 | "cell_type": "code", 727 | "execution_count": null, 728 | "metadata": { 729 | "collapsed": true 730 | }, 731 | "outputs": [], 732 | "source": [] 733 | } 734 | ], 735 | "metadata": { 736 | "kernelspec": { 737 | "display_name": "Python 3", 738 | "language": "python", 739 | "name": "python3" 740 | }, 741 | "language_info": { 742 | "codemirror_mode": { 743 | "name": "ipython", 744 | "version": 3 745 | }, 746 | "file_extension": ".py", 747 | "mimetype": "text/x-python", 748 | "name": "python", 749 | "nbconvert_exporter": "python", 750 | "pygments_lexer": "ipython3", 751 | "version": "3.5.1" 752 | } 753 | }, 754 | "nbformat": 4, 755 | "nbformat_minor": 0 756 | } 757 | -------------------------------------------------------------------------------- /Wk01/.ipynb_checkpoints/Wk01-Overview-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Week 1 - Getting Started" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 3, 13 | "metadata": { 14 | "collapsed": false 15 | }, 16 | "outputs": [ 17 | { 18 | "name": "stdout", 19 | "output_type": "stream", 20 | "text": [ 21 | "Numpy: 1.10.4\n" 22 | ] 23 | } 24 | ], 25 | "source": [ 26 | "import numpy as np\n", 27 | "\n", 28 | "print(\"Numpy:\", np.__version__)" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "#Python Summary\n", 36 | "\n", 37 | "##Further information\n", 38 | "\n", 39 | "More information is usually available with the `help` function. Using ? brings up the same information in ipython. \n", 40 | "\n", 41 | "Using the `dir` function lists all the options available from a variable." 42 | ] 43 | }, 44 | { 45 | "cell_type": "markdown", 46 | "metadata": {}, 47 | "source": [ 48 | "help(np)\n", 49 | "\n", 50 | "np?\n", 51 | "\n", 52 | "dir(np)" 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "metadata": {}, 58 | "source": [ 59 | "##Variables\n", 60 | "\n", 61 | "A variable is simply a name for something. One of the simplest tasks is printing the value of a variable.\n", 62 | "\n", 63 | "Printing can be customized using the format method on strings." 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": 13, 69 | "metadata": { 70 | "collapsed": false 71 | }, 72 | "outputs": [ 73 | { 74 | "name": "stdout", 75 | "output_type": "stream", 76 | "text": [ 77 | "We're in Bethesda zip code 20892 , 71.9 m above sea level\n", 78 | "We're in Bethesda zip code 20892, 71.9m above sea level\n", 79 | "We're in Bethesda zip code 20892, 71.9m above sea level\n", 80 | "We're in Bethesda zip code 20892, 7.19e+01m above sea level\n" 81 | ] 82 | } 83 | ], 84 | "source": [ 85 | "location = 'Bethesda'\n", 86 | "zip_code = 20892\n", 87 | "elevation = 71.9\n", 88 | "\n", 89 | "print(\"We're in\", location, \"zip code\", zip_code, \", \", elevation, \"m above sea level\")\n", 90 | "print(\"We're in \" + location + \" zip code \" + str(zip_code) + \", \" + str(elevation) + \"m above sea level\")\n", 91 | "print(\"We're in {0} zip code {1}, {2}m above sea level\".format(location, zip_code, elevation))\n", 92 | "print(\"We're in {0} zip code {1}, {2:.2e}m above sea level\".format(location, zip_code, elevation))" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": {}, 98 | "source": [ 99 | "##Types\n", 100 | "\n", 101 | "A number of different types are available as part of the standard library. The following links to the documentation provide a summary.\n", 102 | "\n", 103 | "* https://docs.python.org/3.5/library/stdtypes.html\n", 104 | "* https://docs.python.org/3.5/tutorial/datastructures.html\n", 105 | "\n", 106 | "Other types are available from other packages and can be created to support special situations.\n", 107 | "\n", 108 | "A variety of different methods are available depending on the type." 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": 1, 114 | "metadata": { 115 | "collapsed": false 116 | }, 117 | "outputs": [ 118 | { 119 | "name": "stdout", 120 | "output_type": "stream", 121 | "text": [ 122 | "List: [1, 2, 3, 4, 4] 5 True\n", 123 | "Tuple: (1, 2, 3, 4, 4) 5 True\n", 124 | "Set: {1, 2, 3, 4} 4 True\n", 125 | "Dictionary: {'a': 1, 3: 's', 2.5: 't', 'b': 2} 4 True\n" 126 | ] 127 | } 128 | ], 129 | "source": [ 130 | "# Sequences\n", 131 | "\n", 132 | "# Lists\n", 133 | "l = [1,2,3,4,4]\n", 134 | "print(\"List:\", l, len(l), 1 in l)\n", 135 | "\n", 136 | "# Tuples\n", 137 | "t = (1,2,3,4,4)\n", 138 | "print(\"Tuple:\", t, len(t), 1 in t)\n", 139 | "\n", 140 | "# Sets\n", 141 | "s = set([1,2,3,4,4])\n", 142 | "print(\"Set:\", s, len(s), 1 in s)\n", 143 | "\n", 144 | "# Dictionaries\n", 145 | "# Dictionaries map hashable values to arbitrary objects\n", 146 | "d = {'a': 1, 'b': 2, 3: 's', 2.5: 't'}\n", 147 | "print(\"Dictionary:\", d, len(d), 'a' in d)" 148 | ] 149 | }, 150 | { 151 | "cell_type": "markdown", 152 | "metadata": {}, 153 | "source": [ 154 | "##Conditionals\n", 155 | "\n", 156 | "https://docs.python.org/3.5/tutorial/controlflow.html" 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": 2, 162 | "metadata": { 163 | "collapsed": false 164 | }, 165 | "outputs": [ 166 | { 167 | "name": "stdout", 168 | "output_type": "stream", 169 | "text": [ 170 | "Should be printed 50% of the time\n" 171 | ] 172 | } 173 | ], 174 | "source": [ 175 | "import random\n", 176 | "\n", 177 | "if random.random() < 0.5:\n", 178 | " print(\"Should be printed 50% of the time\")\n", 179 | "elif random.random() < 0.5:\n", 180 | " print(\"Should be primted 25% of the time\")\n", 181 | "else:\n", 182 | " print(\"Should be printed 25% of the time\")\n" 183 | ] 184 | }, 185 | { 186 | "cell_type": "markdown", 187 | "metadata": {}, 188 | "source": [ 189 | "##Loops\n", 190 | "\n", 191 | "https://docs.python.org/3.5/tutorial/controlflow.html" 192 | ] 193 | }, 194 | { 195 | "cell_type": "code", 196 | "execution_count": 3, 197 | "metadata": { 198 | "collapsed": false 199 | }, 200 | "outputs": [ 201 | { 202 | "name": "stdout", 203 | "output_type": "stream", 204 | "text": [ 205 | "a\n", 206 | "b\n", 207 | "c\n", 208 | "d\n", 209 | "Else\n", 210 | "a\n", 211 | "c\n" 212 | ] 213 | } 214 | ], 215 | "source": [ 216 | "for i in ['a', 'b', 'c', 'd']:\n", 217 | " print(i)\n", 218 | "else:\n", 219 | " print('Else')\n", 220 | " \n", 221 | "for i in ['a', 'b', 'c', 'd']:\n", 222 | " if i == 'b':\n", 223 | " continue\n", 224 | " elif i == 'd':\n", 225 | " break\n", 226 | " print(i)\n", 227 | "else:\n", 228 | " print('Else')" 229 | ] 230 | }, 231 | { 232 | "cell_type": "markdown", 233 | "metadata": {}, 234 | "source": [ 235 | "##Functions\n", 236 | "\n", 237 | "https://docs.python.org/3.5/tutorial/controlflow.html" 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": 4, 243 | "metadata": { 244 | "collapsed": false 245 | }, 246 | "outputs": [ 247 | { 248 | "name": "stdout", 249 | "output_type": "stream", 250 | "text": [ 251 | "False True\n" 252 | ] 253 | } 254 | ], 255 | "source": [ 256 | "def is_even(n):\n", 257 | " return not n % 2\n", 258 | "\n", 259 | "print(is_even(1), is_even(2))" 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": 5, 265 | "metadata": { 266 | "collapsed": false 267 | }, 268 | "outputs": [ 269 | { 270 | "name": "stdout", 271 | "output_type": "stream", 272 | "text": [ 273 | "[1, 4, 9, 16, 25]\n" 274 | ] 275 | } 276 | ], 277 | "source": [ 278 | "def first_n_squared_numbers(n=5):\n", 279 | " return [i**2 for i in range(1,n+1)]\n", 280 | "\n", 281 | "print(first_n_squared_numbers())" 282 | ] 283 | }, 284 | { 285 | "cell_type": "code", 286 | "execution_count": 6, 287 | "metadata": { 288 | "collapsed": false 289 | }, 290 | "outputs": [ 291 | { 292 | "name": "stdout", 293 | "output_type": "stream", 294 | "text": [ 295 | "1 1 2 3 5 8\n" 296 | ] 297 | } 298 | ], 299 | "source": [ 300 | "def next_fibonacci(status=[]):\n", 301 | " if len(status) < 2:\n", 302 | " status.append(1)\n", 303 | " return 1\n", 304 | " status.append(status[-2] + status[-1])\n", 305 | " return status[-1]\n", 306 | "\n", 307 | "print(next_fibonacci(), next_fibonacci(), next_fibonacci(), next_fibonacci(), next_fibonacci(), next_fibonacci())" 308 | ] 309 | }, 310 | { 311 | "cell_type": "code", 312 | "execution_count": 7, 313 | "metadata": { 314 | "collapsed": false 315 | }, 316 | "outputs": [ 317 | { 318 | "name": "stdout", 319 | "output_type": "stream", 320 | "text": [ 321 | "1\n", 322 | "2\n", 323 | "3\n", 324 | "4\n", 325 | "c 3\n", 326 | "a 1\n", 327 | "b 2\n" 328 | ] 329 | } 330 | ], 331 | "source": [ 332 | "def accepts_anything(*args, **kwargs):\n", 333 | " for a in args:\n", 334 | " print(a)\n", 335 | " for k in kwargs:\n", 336 | " print(k, kwargs[k])\n", 337 | " \n", 338 | "accepts_anything(1,2,3,4, a=1, b=2, c=3)" 339 | ] 340 | }, 341 | { 342 | "cell_type": "code", 343 | "execution_count": 8, 344 | "metadata": { 345 | "collapsed": false 346 | }, 347 | "outputs": [ 348 | { 349 | "name": "stdout", 350 | "output_type": "stream", 351 | "text": [ 352 | "25\n", 353 | "[{'age': 30, 'name': 'Alice'}, {'age': 35, 'name': 'Bob'}, {'age': 35, 'name': 'Charlie'}, {'age': 25, 'name': 'Dennis'}]\n", 354 | "[{'age': 25, 'name': 'Dennis'}, {'age': 30, 'name': 'Alice'}, {'age': 35, 'name': 'Bob'}, {'age': 35, 'name': 'Charlie'}]\n" 355 | ] 356 | } 357 | ], 358 | "source": [ 359 | "# For quick and simple functions a lambda expression can be a useful approach. \n", 360 | "# Standard functions are always a valid alternative and often make code clearer.\n", 361 | "f = lambda x: x**2\n", 362 | "print(f(5))\n", 363 | "\n", 364 | "people = [{'name': 'Alice', 'age': 30}, \n", 365 | " {'name': 'Bob', 'age': 35}, \n", 366 | " {'name': 'Charlie', 'age': 35}, \n", 367 | " {'name': 'Dennis', 'age': 25}]\n", 368 | "print(people)\n", 369 | "people.sort(key=lambda x: x['age'])\n", 370 | "print(people)" 371 | ] 372 | }, 373 | { 374 | "cell_type": "markdown", 375 | "metadata": { 376 | "collapsed": true 377 | }, 378 | "source": [ 379 | "##Numpy\n", 380 | "\n", 381 | "http://docs.scipy.org/doc/numpy/reference/" 382 | ] 383 | }, 384 | { 385 | "cell_type": "code", 386 | "execution_count": 21, 387 | "metadata": { 388 | "collapsed": false 389 | }, 390 | "outputs": [ 391 | { 392 | "name": "stdout", 393 | "output_type": "stream", 394 | "text": [ 395 | "[[1 2 3]\n", 396 | " [4 5 6]\n", 397 | " [7 8 9]]\n", 398 | "[1 2 3]\n", 399 | "[[5 6]\n", 400 | " [8 9]]\n", 401 | "[[ 3 4 5]\n", 402 | " [ 6 7 8]\n", 403 | " [ 9 10 11]]\n", 404 | "[[ 4 6 8]\n", 405 | " [ 7 9 11]\n", 406 | " [10 12 14]]\n", 407 | "[[14 16 18]\n", 408 | " [27 29 31]\n", 409 | " [40 42 44]]\n" 410 | ] 411 | } 412 | ], 413 | "source": [ 414 | "a = np.array([[1,2,3], [4,5,6], [7,8,9]])\n", 415 | "print(a)\n", 416 | "print(a[1:,1:])\n", 417 | "a = a + 2\n", 418 | "print(a)\n", 419 | "a = a + np.array([1,2,3])\n", 420 | "print(a)\n", 421 | "a = a + np.array([[10],[20],[30]])\n", 422 | "print(a)" 423 | ] 424 | }, 425 | { 426 | "cell_type": "code", 427 | "execution_count": 59, 428 | "metadata": { 429 | "collapsed": false 430 | }, 431 | "outputs": [ 432 | { 433 | "name": "stdout", 434 | "output_type": "stream", 435 | "text": [ 436 | "29.0 [ 27. 29. 31.] [ 16. 29. 42.]\n" 437 | ] 438 | } 439 | ], 440 | "source": [ 441 | "print(a.mean(), a.mean(axis=0), a.mean(axis=1))" 442 | ] 443 | }, 444 | { 445 | "cell_type": "code", 446 | "execution_count": 65, 447 | "metadata": { 448 | "collapsed": true 449 | }, 450 | "outputs": [], 451 | "source": [ 452 | "import matplotlib.pyplot as plt\n", 453 | "\n", 454 | "%matplotlib inline" 455 | ] 456 | }, 457 | { 458 | "cell_type": "code", 459 | "execution_count": 68, 460 | "metadata": { 461 | "collapsed": false 462 | }, 463 | "outputs": [ 464 | { 465 | "data": { 466 | "image/png": "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\n", 467 | "text/plain": [ 468 | "" 469 | ] 470 | }, 471 | "metadata": {}, 472 | "output_type": "display_data" 473 | } 474 | ], 475 | "source": [ 476 | "x = np.linspace(0, 3*2*np.pi, 500)\n", 477 | "\n", 478 | "plt.plot(x, np.sin(x))\n", 479 | "plt.show()" 480 | ] 481 | }, 482 | { 483 | "cell_type": "markdown", 484 | "metadata": {}, 485 | "source": [ 486 | "#Exercises" 487 | ] 488 | }, 489 | { 490 | "cell_type": "code", 491 | "execution_count": 3, 492 | "metadata": { 493 | "collapsed": false 494 | }, 495 | "outputs": [ 496 | { 497 | "name": "stdout", 498 | "output_type": "stream", 499 | "text": [ 500 | "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG\n", 501 | "ThE qUiCk BrOwN fOx JuMpS oVeR tHe LaZy DoG\n", 502 | "god yzal eht revo spmuj xof nworb kciuq ehT\n", 503 | "ehT kciuq nworb xof spmuj revo eht yzal god\n", 504 | "1.2346E+09 1.2346E+9\n" 505 | ] 506 | } 507 | ], 508 | "source": [ 509 | "a = \"The quick brown fox jumps over the lazy dog\"\n", 510 | "b = 1234567890.0\n", 511 | "\n", 512 | "## Print the variable `a` in all uppercase\n", 513 | "print(a.upper())\n", 514 | "\n", 515 | "## Print the variable `a` with every other letter in uppercase\n", 516 | "def capEveryOtherLetter(str):\n", 517 | " ans = \"\" \n", 518 | " i = True # capitalize\n", 519 | " for char in str:\n", 520 | " if i == True:\n", 521 | " ans += char.upper() \n", 522 | " else:\n", 523 | " ans += char.lower()\n", 524 | " if char != ' ': # if character is not a space\n", 525 | " i = not i # toggle i between False/True\n", 526 | " return ans\n", 527 | "\n", 528 | "print(capEveryOtherLetter(a))\n", 529 | "\n", 530 | "## Print the variable `a` in reverse, i.e. god yzal ...\n", 531 | "def reverse(str):\n", 532 | " rev = \"\"\n", 533 | " for char in str:\n", 534 | " rev = char + rev\n", 535 | " return rev\n", 536 | " \n", 537 | "print(reverse(a))\n", 538 | "\n", 539 | "## Print the variable `a` with the words reversed, i.e. ehT kciuq ...\n", 540 | "def reverseWords(str):\n", 541 | " words = str.split()\n", 542 | " for i in range(len(words)):\n", 543 | " words[i] = reverse(words[i])\n", 544 | " rev = \" \".join(words)\n", 545 | " return rev\n", 546 | "\n", 547 | "print(reverseWords(a))\n", 548 | "\n", 549 | "## Print the variable `b` in scientific notation with 4 decimal places\n", 550 | "## In python, you have floats and decimals that can be rounded. \n", 551 | "## If you care about the accuracy of rounding, use decimal type. \n", 552 | "## If you use floats, you will have issues with accuracy.\n", 553 | "## Why does ans output E+09 and ans2 E+9? \n", 554 | "from decimal import Decimal\n", 555 | "ans = '%.4E' % Decimal(b)\n", 556 | "ans2 = \"{:.4E}\".format(Decimal(b))\n", 557 | "print(ans, ans2)" 558 | ] 559 | }, 560 | { 561 | "cell_type": "markdown", 562 | "metadata": {}, 563 | "source": [ 564 | "* Print the variable `a` in all uppercase\n", 565 | "* Print the variable `a` with every other letter in uppercase\n", 566 | "* Print the variable `a` in reverse, i.e. god yzal ...\n", 567 | "* Print the variable `a` with the words reversed, i.e. ehT kciuq ...\n", 568 | "* Print the variable `b` in scientific notation with 4 decimal places" 569 | ] 570 | }, 571 | { 572 | "cell_type": "code", 573 | "execution_count": null, 574 | "metadata": { 575 | "collapsed": true 576 | }, 577 | "outputs": [], 578 | "source": [] 579 | }, 580 | { 581 | "cell_type": "code", 582 | "execution_count": 1, 583 | "metadata": { 584 | "collapsed": false 585 | }, 586 | "outputs": [ 587 | { 588 | "name": "stdout", 589 | "output_type": "stream", 590 | "text": [ 591 | "{'name': 'Bob', 'age': 35},{'name': 'Alice', 'age': 30},{'name': 'Eve', 'age': 20},{'name': 'Gail', 'age': 30},{'name': 'Dennis', 'age': 25},{'name': 'Charlie', 'age': 35},{'name': 'Fred', 'age': 25}\n", 592 | "[{'name': 'Eve', 'age': 20}, {'name': 'Dennis', 'age': 25}, {'name': 'Fred', 'age': 25}, {'name': 'Alice', 'age': 30}, {'name': 'Gail', 'age': 30}, {'name': 'Bob', 'age': 35}, {'name': 'Charlie', 'age': 35}]\n", 593 | "[{'name': 'Eve', 'age': 20}, {'name': 'Dennis', 'age': 25}, {'name': 'Fred', 'age': 25}, {'name': 'Alice', 'age': 30}, {'name': 'Gail', 'age': 30}, {'name': 'Bob', 'age': 35}, {'name': 'Charlie', 'age': 35}]\n" 594 | ] 595 | } 596 | ], 597 | "source": [ 598 | "people = [{'name': 'Bob', 'age': 35}, \n", 599 | " {'name': 'Alice', 'age': 30}, \n", 600 | " {'name': 'Eve', 'age': 20},\n", 601 | " {'name': 'Gail', 'age': 30},\n", 602 | " {'name': 'Dennis', 'age': 25},\n", 603 | " {'name': 'Charlie', 'age': 35}, \n", 604 | " {'name': 'Fred', 'age': 25},]\n", 605 | "\n", 606 | "## Print the items in people as comma-separated values.\n", 607 | "## uses map with str conversion function, as join() expects str, not dict\n", 608 | "peopleCommaSeparated = \",\".join(map(str, people))\n", 609 | "print(peopleCommaSeparated)\n", 610 | "\n", 611 | "## Sort people so that they are ordered by age, and print.\n", 612 | "## sort() only works with lists, whereas sorted() accepts any iterable\n", 613 | "peopleSortedByAge = sorted(people, key = lambda person: person['age'])\n", 614 | "print(peopleSortedByAge)\n", 615 | "\n", 616 | "## Sort people so that they are ordered by age first, and then their names, \n", 617 | "## i.e., Bob and Charlie should be next to each other due to their ages \n", 618 | "## with Bob first due to his name.\n", 619 | "peopleSortedByAgeAndName = sorted(people, key = lambda person: (person['age'], person['name']))\n", 620 | "print(peopleSortedByAgeAndName)" 621 | ] 622 | }, 623 | { 624 | "cell_type": "markdown", 625 | "metadata": {}, 626 | "source": [ 627 | "* Print the items in `people` as comma seperated values\n", 628 | "* Sort `people` so that they are ordered by age, and print\n", 629 | "* Sort `people` so that they are ordered by age first, and then their names, i.e. Bob and Charlie should be next to each other due to their ages with Bob first due to his name." 630 | ] 631 | }, 632 | { 633 | "cell_type": "code", 634 | "execution_count": null, 635 | "metadata": { 636 | "collapsed": false 637 | }, 638 | "outputs": [], 639 | "source": [] 640 | }, 641 | { 642 | "cell_type": "markdown", 643 | "metadata": {}, 644 | "source": [ 645 | "* Write a function that returns the first n prime numbers\n", 646 | "* Given a list of coordinates calculate the distance using the (Euclidean distance)[https://en.wikipedia.org/wiki/Euclidean_distance]\n", 647 | "* Given a list of coordinates arrange them in such a way that the distance traveled is minimized (the itertools module may be useful).\n" 648 | ] 649 | }, 650 | { 651 | "cell_type": "code", 652 | "execution_count": null, 653 | "metadata": { 654 | "collapsed": true 655 | }, 656 | "outputs": [], 657 | "source": [ 658 | "coords = [(0,0), (10,5), (10,10), (5,10), (3,3), (3,7), (12,3), (10,11)]\n", 659 | "\n" 660 | ] 661 | }, 662 | { 663 | "cell_type": "markdown", 664 | "metadata": {}, 665 | "source": [ 666 | "* Print the standard deviation of each row in a numpy array\n", 667 | "* Print only the values greater than 90 in a numpy array\n", 668 | "* From a numpy array display the values in each row in a seperate plot (the subplots method may be useful)" 669 | ] 670 | }, 671 | { 672 | "cell_type": "code", 673 | "execution_count": 27, 674 | "metadata": { 675 | "collapsed": false 676 | }, 677 | "outputs": [ 678 | { 679 | "name": "stdout", 680 | "output_type": "stream", 681 | "text": [ 682 | "[[44 47 64 67 67 9 83 21 36 87 70 88 88 12 58 65 39 87 46 88]\n", 683 | " [81 37 25 77 72 9 20 80 69 79 47 64 82 99 88 49 29 19 19 14]\n", 684 | " [39 32 65 9 57 32 31 74 23 35 75 55 28 34 0 0 36 53 5 38]\n", 685 | " [17 79 4 42 58 31 1 65 41 57 35 11 46 82 91 0 14 99 53 12]\n", 686 | " [42 84 75 68 6 68 47 3 76 52 78 15 20 99 58 23 79 13 85 48]\n", 687 | " [49 69 41 35 64 95 69 94 0 50 36 34 48 93 3 98 42 77 21 73]\n", 688 | " [ 0 10 43 58 23 59 2 98 62 35 94 67 82 46 99 20 81 50 27 14]\n", 689 | " [41 58 65 36 10 86 43 11 2 51 80 32 54 0 38 19 46 42 56 60]\n", 690 | " [77 30 24 2 3 94 98 13 40 72 19 95 72 26 66 52 67 61 14 96]\n", 691 | " [ 4 67 11 86 77 75 56 16 24 29 21 25 80 60 61 83 33 32 70 85]] \n", 692 | "\n", 693 | "[ 25.08804496 28.47011591 21.7795202 30.07972739 28.97667165\n", 694 | " 28.50697283 31.07008207 23.401923 32.10681392 27.20087315] \n", 695 | "\n", 696 | "[99 91 99 99 95 94 93 98 98 94 99 94 98 95 96] \n", 697 | "\n" 698 | ] 699 | } 700 | ], 701 | "source": [ 702 | "import numpy as np\n", 703 | "np.random.seed(0)\n", 704 | "a = np.random.randint(0, 100, size=(10,20))\n", 705 | "print(a, \"\\n\")\n", 706 | "\n", 707 | "## Print the standard deviation (σ) of each row in a numpy array\n", 708 | "## in a 2D array, columns are axis = 0, and rows are axis = 1\n", 709 | "row_std = np.std(a, axis=1)\n", 710 | "print(row_std, \"\\n\")\n", 711 | "\n", 712 | "## Print only the values greater than 90 in a numpy array\n", 713 | "truncated = a[a > 90]\n", 714 | "print(truncated, \"\\n\")\n", 715 | "\n", 716 | "## From a numpy array, display the values in each row in a separate plot \n", 717 | "## (the subplots method) may be useful\n", 718 | "import matplotlib.pyplot as plt\n", 719 | "\n", 720 | "plt.plot(a[0])\n", 721 | "plt.subplot(211)\n", 722 | "plt.show()" 723 | ] 724 | }, 725 | { 726 | "cell_type": "code", 727 | "execution_count": null, 728 | "metadata": { 729 | "collapsed": true 730 | }, 731 | "outputs": [], 732 | "source": [] 733 | } 734 | ], 735 | "metadata": { 736 | "kernelspec": { 737 | "display_name": "Python 3", 738 | "language": "python", 739 | "name": "python3" 740 | }, 741 | "language_info": { 742 | "codemirror_mode": { 743 | "name": "ipython", 744 | "version": 3 745 | }, 746 | "file_extension": ".py", 747 | "mimetype": "text/x-python", 748 | "name": "python", 749 | "nbconvert_exporter": "python", 750 | "pygments_lexer": "ipython3", 751 | "version": "3.5.1" 752 | } 753 | }, 754 | "nbformat": 4, 755 | "nbformat_minor": 0 756 | } 757 | --------------------------------------------------------------------------------