├── .gitignore
├── .jupyter
└── jupyter_notebook_config.py
├── Licence.txt
├── README.md
├── img
├── Dropbox.png
├── Jupyter.png
├── NoVacancy.png
├── Slack.png
├── automate_all_the_things.jpeg
├── automation.png
├── code-camp.gif
├── jupyter-example.png
├── notebook1
│ ├── Modern.png
│ └── OldSchool.png
├── notebook5
│ └── alice.jpg
├── null-island.png
├── stack-overflow.jpg
├── untitled.txt
└── venn.png
├── notebook-01-getting-started.ipynb
├── notebook-02-thinking-like-a-computer.ipynb
├── notebook-03-basic-concepts.ipynb
├── notebook-04-errors-and-debugging.ipynb
├── notebook-05-truth-and-conditions.ipynb
├── notebook-06-recap.ipynb
├── notebook-07-lists.ipynb
├── notebook-08-dictionaries.ipynb
├── notebook-09-iteration.ipynb
├── notebook-10-recap.ipynb
├── notebook-11-functions.ipynb
├── notebook-12-packages.ipynb
├── notebook-13-classes.ipynb
└── notebook-14-style-and-summary.ipynb
/.gitignore:
--------------------------------------------------------------------------------
1 | .ipynb_checkpoints
2 | .ipython
3 | .jupyter
4 | .local
5 | *.geojson
6 | *.cache
7 |
--------------------------------------------------------------------------------
/.jupyter/jupyter_notebook_config.py:
--------------------------------------------------------------------------------
1 | c.JupyterLabRmotrSolutions.is_enabled = True
2 | c.JupyterLabRmotrSolutions.role = 'student'
3 |
--------------------------------------------------------------------------------
/Licence.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 King's Geocomputation
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Code Camp [](https://doi.org/10.5281/zenodo.3474043)
2 |
3 | **An on-line coding primer for Python produced by King's College London's Department of Geography**
4 |
5 | ---
6 |
7 | 
8 |
9 |
10 | ### List of Contents
11 |
12 | Welcome! This repository uses interactive Jupyter notebooks to teach you the basics of how to code in Python. Our focus is on simple examples that (we hope) speak to students who are _not_ Computer Scientists by training. Perhaps you dabbled a bit in High School? Perhaps you've never done any programming ever? Whatever, we're here to help!
13 |
14 | The list of topics include:
15 | - [Notebook-1: Introduction](./notebook-01-getting-started.ipynb)
16 | - [Notebook-2: Thinking like a computer](./notebook-02-thinking-like-a-computer.ipynb)
17 | - [Notebook-3: The Basics (Variables, Operators and Precedence)](./notebook-03-basic-concepts.ipynb)
18 | - [Notebook-4: Dealing with Errors & Debugging](./notebook-04-errors-and-debugging.ipynb)
19 | - [Notebook-5: Truth & Conditions (Comparisons, Conditions and George Boole)](./notebook-05-truth-and-conditions.ipynb)
20 | - [Notebook-6: Recap 1](./notebook-06-recap.ipynb)
21 | - [Notebook-7: Lists](./notebook-07-lists.ipynb)
22 | - [Notebook-8: Dictionaries](./notebook-08-dictionaries.ipynb)
23 | - [Notebook-9: Loops and Iteration](./notebook-09-iteration.ipynb)
24 | - [Notebook-10: Recap 2](./notebook-10-recap.ipynb)
25 | - [Notebook-11: Functions (Automate All the Things)](./notebook-11-functions.ipynb)
26 | - [Notebook-12: Packages (Bundles of Functions)](./notebook-12-packages.ipynb)
27 | - [Notebook-13: Classes and Objects](./notebook-13-classes.ipynb)
28 | - [Notebook-14: The Terminal (Working Without Buttons)](./notebook-14-terminal.ipynb)
29 | - [Notebook-15: Wrapping Up (A Matter of Style)](./notebook-15-style-and-summary.ipynb)
30 |
31 | ### Setting Up
32 |
33 | There are several ways to run these notebooks, but the most straightforward way to get started is [using Binder](https://mybinder.org/v2/gh/kingsgeocomp/code-camp-env/master?urlpath=git-pull%3Frepo%3Dhttps%253A%252F%252Fgithub.com%252Fkingsgeocomp%252Fcode-camp%26urlpath%3Dtree%252Fcode-camp%252F%26branch%3Dmaster).
34 |
35 | ### Learning Outcomes
36 |
37 | By the end of the course, you will have a basic understanding of:
38 |
39 | - The rationale behind the usage of computers and computational methodologies
40 | - The way computers "think" and "work"
41 | - How computers programs are structured and how to execute them
42 | - The fundamental concepts of programming (in Python) such as the concept of `variable`, `iteration`, `data structure`, `list`, `object`, `loop`, `function` etc., etc.
43 | - How your newly acquired coding skills can help you be _effectively lazy_.
44 |
45 | ### Credits
46 | The material contained in this repository is heavily inspired by the [great work](http://darribas.org/gds15/index.html) of [Dani Aribas-Bel](https://twitter.com/darribas):
47 | `Arribas-Bel, D. (2016). Geographic Data Science’15. http://doi.org/{10.5281/zenodo.46313}`
48 |
49 | #### Contributors
50 | The following individuals have contributed to these teaching materials:
51 | - [James Millington](https://github.com/jamesdamillington)
52 | - [Jon Reades](https://github.com/jreades)
53 | - [Michele Ferretti](https://github.com/miccferr)
54 | - [Zahratu Shabrina](https://github.com/zarashabrina)
55 |
56 | #### License
57 | The content and structure of this teaching project itself is licensed under the [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 license](https://creativecommons.org/licenses/by-nc-sa/4.0/), and the contributing source code is licensed under [The MIT License](https://opensource.org/licenses/mit-license.php).
58 |
--------------------------------------------------------------------------------
/img/Dropbox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingsgeocomp/code-camp/eeb68d82eaedd96e78a89e7cab88777714af148d/img/Dropbox.png
--------------------------------------------------------------------------------
/img/Jupyter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingsgeocomp/code-camp/eeb68d82eaedd96e78a89e7cab88777714af148d/img/Jupyter.png
--------------------------------------------------------------------------------
/img/NoVacancy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingsgeocomp/code-camp/eeb68d82eaedd96e78a89e7cab88777714af148d/img/NoVacancy.png
--------------------------------------------------------------------------------
/img/Slack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingsgeocomp/code-camp/eeb68d82eaedd96e78a89e7cab88777714af148d/img/Slack.png
--------------------------------------------------------------------------------
/img/automate_all_the_things.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingsgeocomp/code-camp/eeb68d82eaedd96e78a89e7cab88777714af148d/img/automate_all_the_things.jpeg
--------------------------------------------------------------------------------
/img/automation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingsgeocomp/code-camp/eeb68d82eaedd96e78a89e7cab88777714af148d/img/automation.png
--------------------------------------------------------------------------------
/img/code-camp.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingsgeocomp/code-camp/eeb68d82eaedd96e78a89e7cab88777714af148d/img/code-camp.gif
--------------------------------------------------------------------------------
/img/jupyter-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingsgeocomp/code-camp/eeb68d82eaedd96e78a89e7cab88777714af148d/img/jupyter-example.png
--------------------------------------------------------------------------------
/img/notebook1/Modern.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingsgeocomp/code-camp/eeb68d82eaedd96e78a89e7cab88777714af148d/img/notebook1/Modern.png
--------------------------------------------------------------------------------
/img/notebook1/OldSchool.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingsgeocomp/code-camp/eeb68d82eaedd96e78a89e7cab88777714af148d/img/notebook1/OldSchool.png
--------------------------------------------------------------------------------
/img/notebook5/alice.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingsgeocomp/code-camp/eeb68d82eaedd96e78a89e7cab88777714af148d/img/notebook5/alice.jpg
--------------------------------------------------------------------------------
/img/null-island.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingsgeocomp/code-camp/eeb68d82eaedd96e78a89e7cab88777714af148d/img/null-island.png
--------------------------------------------------------------------------------
/img/stack-overflow.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingsgeocomp/code-camp/eeb68d82eaedd96e78a89e7cab88777714af148d/img/stack-overflow.jpg
--------------------------------------------------------------------------------
/img/untitled.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingsgeocomp/code-camp/eeb68d82eaedd96e78a89e7cab88777714af148d/img/untitled.txt
--------------------------------------------------------------------------------
/img/venn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingsgeocomp/code-camp/eeb68d82eaedd96e78a89e7cab88777714af148d/img/venn.png
--------------------------------------------------------------------------------
/notebook-02-thinking-like-a-computer.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "geopyter": {
7 | "Contributors": [
8 | "Michele Ferretti (https://github.com/miccferr)",
9 | "Jon Reades (https://github.com/jreades)",
10 | "James Millington (https://github.com/jamesdamillington)",
11 | "Jon Reades (https://github.com/jreades)"
12 | ],
13 | "git": {
14 | "active_branch": "master",
15 | "author.name": "Jon Reades",
16 | "authored_date": "2017-06-05 16:12:59",
17 | "committed_date": "2017-06-05 16:12:59",
18 | "committer.name": "Jon Reades",
19 | "sha": "2fce1fab4c3c28acabee493a24302e5ee1e8a1eb"
20 | }
21 | }
22 | },
23 | "source": [
24 | "# Notebook-2: Thinking Like a Computer\n",
25 | "\n",
26 | "In order to understand how to _program_ a computer, it helps to learn how to _think_ like a computer. At least a little bit. A lot of what we do in programming strips back the veneer of point-and-click friendliness of OSX or Windows so that we can interact with the computer in ways that require much less human input. The world will seem a bit less friendly in the short term, but you will gain the ability to work much more quickly and powerfully with the computer through code."
27 | ]
28 | },
29 | {
30 | "cell_type": "markdown",
31 | "metadata": {
32 | "geopyter": {
33 | "Contributors": [
34 | "Michele Ferretti (https://github.com/miccferr)",
35 | "Jon Reades (https://github.com/jreades)",
36 | "James Millington (https://github.com/jamesdamillington)",
37 | "Jon Reades (https://github.com/jreades)"
38 | ],
39 | "git": {
40 | "active_branch": "master",
41 | "author.name": "Jon Reades",
42 | "authored_date": "2017-06-05 16:12:59",
43 | "committed_date": "2017-06-05 16:12:59",
44 | "committer.name": "Jon Reades",
45 | "sha": "2fce1fab4c3c28acabee493a24302e5ee1e8a1eb"
46 | }
47 | }
48 | },
49 | "source": [
50 | "## What _is_ a computer?\n",
51 | "\n",
52 | "At it's most basic, a computer is a programmable device for performing calculations.\n",
53 | "\n",
54 | "_This_ is a kind of computer.\n",
55 | "\n",
56 | "
\n",
57 | "\n",
58 | "\n",
59 | "As is _this_.\n",
60 | "\n",
61 | "
"
62 | ]
63 | },
64 | {
65 | "cell_type": "markdown",
66 | "metadata": {},
67 | "source": [
68 | "There are a huge number of videos on YouTube and resources accessible from Google that delve into more detail than we possibly can here about what is happening inside your computer."
69 | ]
70 | },
71 | {
72 | "cell_type": "markdown",
73 | "metadata": {
74 | "geopyter": {
75 | "Contributors": [
76 | "Michele Ferretti (https://github.com/miccferr)",
77 | "Jon Reades (https://github.com/jreades)",
78 | "James Millington (https://github.com/jamesdamillington)",
79 | "Jon Reades (https://github.com/jreades)"
80 | ],
81 | "git": {
82 | "active_branch": "master",
83 | "author.name": "Jon Reades",
84 | "authored_date": "2017-06-05 16:12:59",
85 | "committed_date": "2017-06-05 16:12:59",
86 | "committer.name": "Jon Reades",
87 | "sha": "2fce1fab4c3c28acabee493a24302e5ee1e8a1eb"
88 | }
89 | }
90 | },
91 | "source": [
92 | "### What's Going on Inside Your Computer?\n",
93 | "\n",
94 | "If you've never really got to grips with what is happening inside a computer, then [this TedED video](http://www.youtube.com/watch?v=AkFi90lZmXA) would be a good way to get started because it helps to explain the basics of things like I/O and what actually happens when you click with the mouse on a button. In fact, you will see that we've used code to import the YouTube video in a way that requires me to do very little work and this is one of the strengths of programming: that someone else created the code to embed a YouTube video into a notebook (which is what this web page is) and all I need to do is know how to ask that code to find the video on the YouTube web site. Everything else happens automatically."
95 | ]
96 | },
97 | {
98 | "cell_type": "markdown",
99 | "metadata": {
100 | "geopyter": {
101 | "Contributors": [
102 | "Michele Ferretti (https://github.com/miccferr)",
103 | "Jon Reades (https://github.com/jreades)",
104 | "James Millington (https://github.com/jamesdamillington)",
105 | "Jon Reades (https://github.com/jreades)"
106 | ],
107 | "git": {
108 | "active_branch": "master",
109 | "author.name": "Jon Reades",
110 | "authored_date": "2017-06-05 16:12:59",
111 | "committed_date": "2017-06-05 16:12:59",
112 | "committer.name": "Jon Reades",
113 | "sha": "2fce1fab4c3c28acabee493a24302e5ee1e8a1eb"
114 | }
115 | }
116 | },
117 | "source": [
118 | "[](http://www.youtube.com/watch?v=AkFi90lZmXA)"
119 | ]
120 | },
121 | {
122 | "cell_type": "markdown",
123 | "metadata": {
124 | "geopyter": {
125 | "Contributors": [
126 | "Michele Ferretti (https://github.com/miccferr)",
127 | "Jon Reades (https://github.com/jreades)",
128 | "James Millington (https://github.com/jamesdamillington)",
129 | "Jon Reades (https://github.com/jreades)"
130 | ],
131 | "git": {
132 | "active_branch": "master",
133 | "author.name": "Jon Reades",
134 | "authored_date": "2017-06-05 16:12:59",
135 | "committed_date": "2017-06-05 16:12:59",
136 | "committer.name": "Jon Reades",
137 | "sha": "2fce1fab4c3c28acabee493a24302e5ee1e8a1eb"
138 | }
139 | }
140 | },
141 | "source": [
142 | "### How a Computer Adds Numbers\n",
143 | "\n",
144 | "[This next video](http://www.youtube.com/watch?v=VBDoT8o4q00) is a little more technical and we don't really expect you to remember it, but it touches on a lot of really important concepts: binary numbers, Boolean logic, and how these basic building blocks are assembled into much more complex processes like adding numbers or, ultimately, manipulating data.\n",
145 | "\n",
146 | "[](http://www.youtube.com/watch?v=VBDoT8o4q00)"
147 | ]
148 | },
149 | {
150 | "cell_type": "markdown",
151 | "metadata": {
152 | "geopyter": {
153 | "Contributors": [
154 | "Michele Ferretti (https://github.com/miccferr)",
155 | "Jon Reades (https://github.com/jreades)",
156 | "James Millington (https://github.com/jamesdamillington)",
157 | "Jon Reades (https://github.com/jreades)"
158 | ],
159 | "git": {
160 | "active_branch": "master",
161 | "author.name": "Jon Reades",
162 | "authored_date": "2017-06-05 16:12:59",
163 | "committed_date": "2017-06-05 16:12:59",
164 | "committer.name": "Jon Reades",
165 | "sha": "2fce1fab4c3c28acabee493a24302e5ee1e8a1eb"
166 | }
167 | }
168 | },
169 | "source": [
170 | "The really important thing to get from this last video is that computers are chaining together long sets of simple operations which always basically work out to 1 or 0, which is the same as True or False. This is [Boolean logic](http://computer.howstuffworks.com/boolean.htm) and it is integral to computation _and_ to data processing, but you should always keep in mind that a huge set of calculations are going on in your computer in an order specified by a set of *rules*: do 'A', then do 'B', then... When these rules become sufficiently complex (they become like long recipes!) they are called algorithms. And when they get so complicated that they are not easy to write down as a set of logical outputs, it's often easier to express in a more human-readable form... which is why we have [programming languages](http://www.computerhope.com/jargon/p/proglang.htm).\n",
171 | "\n",
172 | "But remember: finding the average of a set of numbers involves an algorithm (which, in a digital comupter, is based on lots of logical operations involving 1s and 0s). And calculating the probability that the lecturer won't show up to the first lecture also involves an algorithm, it's just that it's a much more complicated one unless you take matters into your own hands and arrange for an accident..."
173 | ]
174 | },
175 | {
176 | "cell_type": "markdown",
177 | "metadata": {},
178 | "source": [
179 | "# Working Without Buttons\n",
180 | "\n",
181 | "At _some_ point in your exploration of programming you will need to learn how to read/write data from elsewhere on the computer or the Internet. At that point, for anyone who learned how to use a computer after about 1990, computers can quickly come to seem very unfriendly indeed! That's because when you are programming you will need to give the computer a lot more information about how to find, read, and write data. \n",
182 | "\n",
183 | "When you use an app on your mobile phone or on your computer it will often save your files somewhere convenient so that you can find them again, but this isn't magic: a programmer made a choice about how to do this for you, and now that you're learning to program _you_ have to make that choice."
184 | ]
185 | },
186 | {
187 | "cell_type": "markdown",
188 | "metadata": {},
189 | "source": [
190 | "## Paths\n",
191 | "\n",
192 | "Have you ever thought about how and where files are stored on your computer? Probably not, unless you were very, very bored. Unfortunately, when you start programming you _do_ need to learn a bit more about this -- enough, at least, to tell Python where to find the file that you want it to read... though you can do much more than that!\n",
193 | "\n",
194 | "A few starting principles:\n",
195 | "\n",
196 | "1. Directories (a.k.a. folders) and files all have a unique location _somewhere_ on your hard drive.\n",
197 | "\n",
198 | "2. A directory (or folder) can contain directories and files. A _simple_ file cannot contain a directory. Only special _types_ of file such as a Zip archive can contain a folder.\n",
199 | "\n",
200 | "3. The directory that sits at the bottom of the hierarchy (_i.e._ the _one_ directory that is not _in_ another directory) is known as the **root directory**.\n",
201 | "\n",
202 | "4. The directory in which _your_ settings and documents are saved (_i.e._ the stuff associated with your *username*) is known as the **home directory**.\n",
203 | "\n",
204 | "5. A file must be stored in a directory (there are no root files)."
205 | ]
206 | },
207 | {
208 | "cell_type": "markdown",
209 | "metadata": {},
210 | "source": [
211 | "### Paths in the Finder\n",
212 | "\n",
213 | "We'll get to a video in a second, but on a Mac you can 'view' the path in the Finder simply by clicking on any finder window and typing `Alt + Command + P` (or selecting `View > Path` from the menu bar). This will show the current path in a strip along the bottom of the finder window. Pay attention to the Path view in the movie below.\n",
214 | "\n",
215 | "[](http://www.youtube.com/watch?v=U0hrKz5Ajrg \"The Path (Graphical Version)\")"
216 | ]
217 | },
218 | {
219 | "cell_type": "markdown",
220 | "metadata": {},
221 | "source": [
222 | "### Paths in a Terminal\n",
223 | "\n",
224 | "Now, sooner or later you are going to have to learn how to use the Terminal (a.k.a. Shell) because it will make certain tedious tasks go much, much faster. It is also by far the best way to install Python libraries or other 'libraries' that you need to develop your code. Learning to the use the Terminal is also going to help a _lot_ in learning to fix subtle problems with your program (e.g. checking that you are reading the _right_ data). \n",
225 | "\n",
226 | "So here's the _same_ process of navigating from a directory called `KCL Modules` down to the 2017-18 Geocomputation Teaching directory as you just saw above, but using the Terminal:\n",
227 | "\n",
228 | "[](http://www.youtube.com/watch?v=qxvhsuOKGaY \"The Path (Terminal Version)\")\n",
229 | "\n",
230 | "You'll notice that there were several seemingly cryptic commands -- we'll examine them in more detail below, but the important ones to note in this video are:\n",
231 | "\n",
232 | "- `ls` *lists* the contents of the current directory\n",
233 | "- `cd` *c*hanges *d*irectory\n",
234 | "\n",
235 | "We always say that programmers are lazy and this is a good example of that: why write `list` when we can write `ls` or `change directory` when we can write `cd`? You obviously need to learn what those bits of laziness mean, but they can help a lot in speeding up your code!"
236 | ]
237 | },
238 | {
239 | "cell_type": "markdown",
240 | "metadata": {},
241 | "source": [
242 | "### Navigating Using the Terminal\n",
243 | "\n",
244 | "You need to think of the terminal 'prompt' as having a 'location in' your computer: this works the same as clicking around through the Finder (or Windows Explorer) in that you have to move 'down' into sub-folders or 'up' into parent directories to see what's available at that 'level' of the drive. \n",
245 | "\n",
246 | "So in the movie you've just watched:\n",
247 | "1. I started the movie while in a directory called `KCL Teaching` that (you see at the end) sits under `/Users/my.blurred.username/Documents/`.\n",
248 | "2. I listed the files and directories in `KCL Teaching` using `ls`.\n",
249 | "3. I then changed directory (using `cd`) into the directory called `KCL Modules` (and note that, because there's a space in the directory name I had to write this as `KCL\\ Modules`... that's because the Terminal couldn't otherwise tell if we meant to move to a directory called `KCL` and then run a command called `Modules`; it's a long story).\n",
250 | "4. I then changed directory again into a directory called `Undergraduate` -- if you're keeping track this now means that we're 'in' `/Users/my.blurred.username/Documents/KCL\\ Teaching/KCL\\ Modules/Undergraduate/`\n",
251 | "5. We carry on navigating down the hierarchy until we reach the 2017-18 teaching folder, at which point I print the working directory to show you where we've ended up and that it's the same location as we reached using the Finder in the other video. \n",
252 | "\n",
253 | "For people who are used to just saving a document pretty much anywhere on their computer (or iPad or iPhone) and having it accessible via Spotlight/Search functions this can take a _lot_ of getting used to, but remember that in learning to program we're gradually stripping away the bells and whistles that sit between you and what the computer's actually doing. "
254 | ]
255 | },
256 | {
257 | "cell_type": "markdown",
258 | "metadata": {},
259 | "source": [
260 | "### Chaining\n",
261 | "\n",
262 | "In and of itself the advantages of the Terminal might not be obvious, but we can also _chain_ commands together to do several things in one go. **You do not _need_ to run the example below** because it's for illustrative purposes:\n",
263 | "\n",
264 | "```\n",
265 | "curl https://raw.githubusercontent.com/kingsgeocomp/geocomputation/master/CitiesWithWikipediaData.csv | head > Header.csv\n",
266 | "```"
267 | ]
268 | },
269 | {
270 | "cell_type": "markdown",
271 | "metadata": {},
272 | "source": [
273 | "**_Note_**: If you _do_ want to run the code above then you will probably need to do some prep-work:\n",
274 | "* **On a Mac** you will need to run (from the Terminal!) the following command: `xcode-select --install`. This installs the `curl` utility along with a bunch of other useful applications.\n",
275 | "* **On Windows** you will probably need to run something like `conda install posix` but unless you want to donate a Windows machine I can't test this.\n",
276 | "* Once that's done you should type all of the above (or copy+paste) on to _one new line_ and you should see the first line of this file printed to the Terminal."
277 | ]
278 | },
279 | {
280 | "cell_type": "markdown",
281 | "metadata": {},
282 | "source": [
283 | "This command might seem _really_ cryptic but we can break this problem down into steps (as I did when trying to remember how to do it!):\n",
284 | "\n",
285 | "1. `curl` -- this is a tool (hopefully installed on your computer!) that allows you to download a file from the internet using only the Terminal.\n",
286 | "2. `head` -- this utility works with the _top_ part of a file (`tail` starts working with the _end_ of a file)\n",
287 | "3. We 'glue' the output of `curl` together with `head` using `|` (known as a 'pipe') -- this tells the computer to pass the output of `curl` to the input of `head` (i.e. we are _chaining_ the commands together).\n",
288 | "4. `head` alone returns the first _ten_ lines of the file (but if we used `head -5`, for example, we would get the first _five_ lines of the file - you can read more about the `head` command here).\n",
289 | "5. We then direct the output of `head` to a file called `Header.csv` using the redirection command `>`."
290 | ]
291 | },
292 | {
293 | "cell_type": "markdown",
294 | "metadata": {},
295 | "source": [
296 | "So this one line of _code_ allows you to download a file, extract the first line of the file (so that we can see what the variables are!), and then write it to a file on the local computer. Assuming that you didn't see any errors, you should now have a file called `Header.csv` in your home directory containing the first 10 lines of the file! Go check this in your home directory. \n",
297 | "\n",
298 | "The point of this is that we are gaining in power at the cost of 'ease of use'. Doing this same task _without_ using code would require: navigating to the web page, clicking the link to download the file, opening the file in a text editor or Excel, selecting and copying the first line, and then opening a new file, pasting in the copied data, and saving the file with a name and location! Easier the first time round, but much more work in the long run!"
299 | ]
300 | },
301 | {
302 | "cell_type": "markdown",
303 | "metadata": {},
304 | "source": [
305 | "### Delayed Gratification\n",
306 | "\n",
307 | "As you will experience, learning to program involves a _lot_ of delayed gratification: you will need to invest quite a bit of time in learning to crawl before you can walk, and even more time in learning to walk before you can run. A well-designed programming language like Python can make it a little bit _easier_ to learn each step, but it still _**won't make it easy**_. \n",
308 | "\n",
309 | "We've said this before and we'll say it again (and again, and again) that you _must_ think of this as learning a language: at first very little will make sense and you won't be able to say much more than 'hello, my name is X', but if you _practice_ and really _think_ about what you're doing it will get easier and easier, and faster and faster, to program.\n",
310 | "\n",
311 | "Learning to navigate around your hard drive using the Terminal is a good case in point: why learn to do this when you could just dump all of your data into one directory and then never need to think about it again? Or navigate around by clicking on folders and files using the mouse?\n",
312 | "\n",
313 | "Two reasons:\n",
314 | "\n",
315 | "1. Because you will need to think _logically_ and in an _organised_ way about how you manage data when you start doing data analysis: you will almost never receive raw data that doesn't require some work; so if you save your raw data and your processed data in the same directory how can you be sure you've loaded the right file? It's much easier to have two folders -- `raw` or `source`, and `clean` or `output` -- and use Python to read in raw data from one directory and then write the processed data out to a different directory than it would be using Excel's `File > Save As`. That's because the path is just _text_ and you can easily work with it that way!\n",
316 | "\n",
317 | "2. Because once you've learned how to use the path on your _own_ computer, you can use the _same_ techniques to navigate a web server or computer on the other side of the planet using URLs (which are just paths with some special 'sauce' that tell Python that the file isn't on _your_ computer).\n",
318 | "\n",
319 | "Here's an example that does the _same_ thing (and then some) as the Terminal code above, but using Python code instead:"
320 | ]
321 | },
322 | {
323 | "cell_type": "code",
324 | "execution_count": null,
325 | "metadata": {},
326 | "outputs": [],
327 | "source": [
328 | "import csv\n",
329 | "import requests as r\n",
330 | "\n",
331 | "url = 'http://bit.ly/2iIK9bA'\n",
332 | "data = r.get(url)\n",
333 | "content = data.content.decode('utf-8').splitlines()\n",
334 | "cr = csv.reader(content)\n",
335 | "\n",
336 | "for row in cr:\n",
337 | " if row[3] != 'Population':\n",
338 | " print(row[1] + \" has a population of \" + \"{:,}\".format(int(row[3])))"
339 | ]
340 | },
341 | {
342 | "cell_type": "markdown",
343 | "metadata": {},
344 | "source": [
345 | "Just to highlight what we've just done:\n",
346 | "\n",
347 | "1. We retrieved a CSV file from somewhere on this planet using `requests`,\n",
348 | "2. We 'parsed' it so that each `row` became something from which we could extract variables thanks to the magic of the `csv` library that we `import`,\n",
349 | "3. We skip the first row because it's a 'header' row and doesn't contain data,\n",
350 | "4. We print out the 2nd and 4th columns of the CSV file.\n",
351 | "\n",
352 | "You can see what the source looked like [here](http://bit.ly/2iIK9bA). And you'll note that the URL looks _exactly_ like the path that we saw when we used the Terminal to navigate between directories. \n",
353 | "\n",
354 | "So if you grasp the concept in one context, you can apply it in others. _That_ is scalability!"
355 | ]
356 | },
357 | {
358 | "cell_type": "markdown",
359 | "metadata": {},
360 | "source": [
361 | "### Special Paths\n",
362 | "\n",
363 | "Finally, there are a few 'special' paths that you need to know about when using the Terminal or Python:\n",
364 | "\n",
365 | "- `/` is the **root directory** (so `cd /` would take you to the 'root' of the hard drive)\n",
366 | "- `~` is the **home directory** (this is a shortcut: it's the same as typing `cd /Users/myusername/`)\n",
367 | "- `.` means the **current directory** (this is avoid ambiguity: `cd ./myusername/Documents` would assume that in the current directory there is a sub-directory called `Documents` under `myusername`)\n",
368 | "- `..` means the **next directory up** (this is so that you don't have to type out `cd /Users/myusername/Settings` just to go from `Documents` 'over' to `Settings`; instead, you can use `cd ../Settings/`.\n",
369 | "\n",
370 | "In terms of terminology: any path that starts with a `/` is an _**absolute path**_ because we are starting from the _**root**_ directory; any path that starts with either `..` or `.` is a _**relative path**_ because it is starting from the _**current**_ directory and is relative to where the Terminal or program 'is' now.\n",
371 | "\n",
372 | "### Windows vs Everyone Else\n",
373 | "\n",
374 | "For historical reasons, Windows has long been just a bit different from Unix/Linux/Mac in how paths are specified: on Windows it has long been the case that the path separator was `\\`, not `/` (`:` also isn't allowed in file name on a Mac for historical reasons)!\n",
375 | "\n",
376 | "So the following Unix/Mac path: `/Users/myusername/Documents/`\n",
377 | "\n",
378 | "On a Windows machine would usually be: `\\Users\\myusername\\Documents\\`\n",
379 | "\n",
380 | "If you remember that on a Unix/Mac we use `\\` to manage things like spaces in a directory name then you can see how it gets pretty confusing and complicated pretty quickly when you start to program... But in Python we can cope with _both_ of these situations and others using the `os` library (short for 'operating system'); see the examples below!\n",
381 | "\n",
382 | "**Remember**: to turn code you need click in the code block below and then either type `Ctrl+Enter` or hit the 'Run' button on the menu bar above."
383 | ]
384 | },
385 | {
386 | "cell_type": "code",
387 | "execution_count": null,
388 | "metadata": {},
389 | "outputs": [],
390 | "source": [
391 | "import os\n",
392 | "# Create a path by joining together the input arguments\n",
393 | "print(os.path.join('Users','myusername','Documents'))\n",
394 | "print(\" \")\n",
395 | "# Create an absolute path (starts with '/') and file names that contain spaces (we don't need to worry about spaces)\n",
396 | "print(os.path.join('/','Users','myusername','Documents','My Work','Code Camp'))\n",
397 | "print(\" \")\n",
398 | "# Create a relative path (starts with '..') and file names that contain spaces (we don't need to worry about spaces)\n",
399 | "print(os.path.join('..','Documents','My Work','Code Camp'))\n",
400 | "print(\" \")\n",
401 | "# 'Expand' the current user's home directory (~) into an absolute path\n",
402 | "print(os.path.expanduser(\"~\"))\n",
403 | "print(\" \")\n",
404 | "# 'Expand' the current directory path (.) into an absolute path\n",
405 | "print(os.path.abspath('.'))\n",
406 | "print(\" \")"
407 | ]
408 | },
409 | {
410 | "cell_type": "markdown",
411 | "metadata": {},
412 | "source": [
413 | "That's quite a lot of new content to get your head around, so you might want to spend some time exploring your own computer using the Terminal (Shell/Power Shell on Windows) to get the hang of how paths work. All you'll need are: `cd`, `ls`, and `pwd` (or [their Windows equivalents](https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/4/html/Step_by_Step_Guide/ap-doslinux.html) and [here for more](https://courses.cs.washington.edu/courses/cse140/13wi/shell-usage.html#windows)) to get started!"
414 | ]
415 | },
416 | {
417 | "cell_type": "markdown",
418 | "metadata": {},
419 | "source": [
420 | "## Power Terminal Features\n",
421 | "\n",
422 | "You might recall that we previously said that [all programmers are lazy](notebook-1.ipynb), and understanding this laziness is the key to thinking that most Terminal (and Python) commands look like the result of monkeys hitting keys at random to being able to write code without constantly having to check Google. Let's revisit some of the commeands we've just seen:\n",
423 | "\n",
424 | "| Command | Short For... | What It Does |\n",
425 | "|:------- |:------------ |:------------ |\n",
426 | "| ls | list | List the contents of the directory I'm about to give you ('`ls ~`' lists the contents of your home directory; '`ls ..`' lists the contents of the parent directory; etc.) |\n",
427 | "| cd | change directory | Move to the path I'm about to give you ('`cd ~`' moves you to your home directory; '`cd ..`' moves you to the parent directory; etc.) | \n",
428 | "| pwd | print working directory | Show the path of the current current directory (_i.e._ where am I now?) |\n",
429 | "| head | Get the head of a file | Print the first few lines of the specified file (_e.g._ `head somefile.csv`) |\n",
430 | "| tail | Get the tail of a file | Print the last few lines of the specified file (_e.g._ `tail -n 2 somefile.csv`) |\n",
431 | "| clear | Clear the Terminal | When the Terminal looks really busy with junk you don't need just type `clear` |\n",
432 | "| exit | Exit the Terminal | To close an open Terminal window |\n",
433 | "| mv | Move a directory or file | Like dragging things in the Finder window (_e.g._ `mv somefile.csv ../another_dir/` to move the file `somefile.csv` _over_ to a directory named `another_dir` that is accessible from the parent directory of this one) |\n",
434 | "| cp | Copy a directory or file | Like dragging things in the Finder window (_e.g._ `cp somefile.csv ../another_dir/` to make a copy of the file `somefile.csv` in a directory named `another_dir` that is accessible from the parent directory of this one) |\n",
435 | "\n",
436 | "You can see how all of these commands are basically abbreviations/mnemonics that make it easy for programmers to be lazy, but productive: less time typing == more time thinking/doing."
437 | ]
438 | },
439 | {
440 | "cell_type": "markdown",
441 | "metadata": {},
442 | "source": [
443 | "### Credits!\n",
444 | "\n",
445 | "#### Contributors:\n",
446 | "The following individuals have contributed to these teaching materials: \n",
447 | "- [James Millington](https://github.com/jamesdamillington)\n",
448 | "- [Jon Reades](https://github.com/jreades)\n",
449 | "- [Michele Ferretti](https://github.com/miccferr)\n",
450 | "- [Zahratu Shabrina](https://github.com/zarashabrina)\n",
451 | "\n",
452 | "#### License\n",
453 | "The content and structure of this teaching project itself is licensed under the [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 license](https://creativecommons.org/licenses/by-nc-sa/4.0/), and the contributing source code is licensed under [The MIT License](https://opensource.org/licenses/mit-license.php).\n",
454 | "\n",
455 | "#### Acknowledgements:\n",
456 | "Supported by the [Royal Geographical Society](https://www.rgs.org/HomePage.htm) (with the Institute of British Geographers) with a Ray Y Gildea Jr Award.\n",
457 | "\n",
458 | "#### Potential Dependencies:\n",
459 | "This notebook may depend on the following libraries: None"
460 | ]
461 | }
462 | ],
463 | "metadata": {
464 | "anaconda-cloud": {},
465 | "geopyter": {
466 | "Contributors": [
467 | "Michele Ferretti (https://github.com/miccferr)",
468 | "Jon Reades (https://github.com/jreades)",
469 | "James Millington (https://github.com/jamesdamillington)"
470 | ],
471 | "git": {
472 | "active_branch": "master",
473 | "author.name": "Jon Reades",
474 | "authored_date": "2017-06-05 16:12:59",
475 | "committed_date": "2017-06-05 16:12:59",
476 | "committer.name": "Jon Reades",
477 | "sha": "2fce1fab4c3c28acabee493a24302e5ee1e8a1eb"
478 | },
479 | "libs": {}
480 | },
481 | "kernelspec": {
482 | "display_name": "Python 3 (ipykernel)",
483 | "language": "python",
484 | "name": "python3"
485 | },
486 | "language_info": {
487 | "codemirror_mode": {
488 | "name": "ipython",
489 | "version": 3
490 | },
491 | "file_extension": ".py",
492 | "mimetype": "text/x-python",
493 | "name": "python",
494 | "nbconvert_exporter": "python",
495 | "pygments_lexer": "ipython3",
496 | "version": "3.10.12"
497 | }
498 | },
499 | "nbformat": 4,
500 | "nbformat_minor": 4
501 | }
502 |
--------------------------------------------------------------------------------
/notebook-04-errors-and-debugging.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Notebook-4: Dealing with Errors & Debugging"
8 | ]
9 | },
10 | {
11 | "cell_type": "markdown",
12 | "metadata": {},
13 | "source": [
14 | "#### Lesson Topics: \n",
15 | "\n",
16 | "- Introduction to errors \n",
17 | " - Syntax Errors\n",
18 | " - Exceptions \n",
19 | "- How to read errors\n",
20 | "- Learn to find help\n",
21 | "\n",
22 | "Welcome to the third Code Camp notebook! This lesson is all about learning to deal with the (unavoidable) errors that you wil encounter when programming in Ptyhon. Most programmers spend _most_ of their day dealing with errors of one sort or another: sometimes they are easy to solve (e.g. you mis-typed a variable name), other times they are very, very hard (e.g. you are writing a cloud computing platform and have to deal with competition for resources). Either way, learning how to find, diagnose, and resolve errors, as well as how to minimize their consequences, is thus a crucial skill for programmers. \n",
23 | "\n",
24 | "*Acknowledgement: This notebook is heavily based on the [official Python Documentation](https://docs.python.org/2/tutorial/errors.html) about Errors and Exception. Check it out for further examples.*"
25 | ]
26 | },
27 | {
28 | "cell_type": "markdown",
29 | "metadata": {},
30 | "source": [
31 | "---\n",
32 | "# Introduction to errors\n",
33 | "\n",
34 | "In the preceding notebooks we've already pointed out a few simple errors and made some suggestions about how to read them, but as you have seen when there's something wrong Python stops whatever it's doing and prints out an error message.\n",
35 | "\n",
36 | "Run the next code cell and examine the error message:"
37 | ]
38 | },
39 | {
40 | "cell_type": "code",
41 | "execution_count": null,
42 | "metadata": {
43 | "scrolled": true
44 | },
45 | "outputs": [],
46 | "source": [
47 | "print \"Ouch!\""
48 | ]
49 | },
50 | {
51 | "cell_type": "markdown",
52 | "metadata": {},
53 | "source": [
54 | "The error gives you a helpful clue as to what is going wrong: it's something to do with the Syntax.\n",
55 | "```python\n",
56 | " Input In [1]\n",
57 | " print \"Ouch!\"\n",
58 | " ^ \n",
59 | "SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)?\n",
60 | "```\n",
61 | "In this case it even tells you what the most _likely_ resolution is: we need to use `()` with the `print` function (i.e. `print(\"Ouch!\")`). Not all errors are as easy to diagnose, but by carefully reviewing the output it is often possible to get a pretty good _sense_ of where things are going wrong. "
62 | ]
63 | },
64 | {
65 | "cell_type": "markdown",
66 | "metadata": {},
67 | "source": [
68 | "And here's another, our familiar \"division by zero\" example:"
69 | ]
70 | },
71 | {
72 | "cell_type": "code",
73 | "execution_count": null,
74 | "metadata": {},
75 | "outputs": [],
76 | "source": [
77 | "45 / 0"
78 | ]
79 | },
80 | {
81 | "cell_type": "markdown",
82 | "metadata": {},
83 | "source": [
84 | "This error also gives you a helpful clue: You can't divide by zero!\n",
85 | "```python\n",
86 | "ZeroDivisionError Traceback (most recent call last)\n",
87 | " in \n",
88 | "----> 1 45 / 0\n",
89 | "\n",
90 | "ZeroDivisionError: division by zero\n",
91 | "```"
92 | ]
93 | },
94 | {
95 | "cell_type": "markdown",
96 | "metadata": {},
97 | "source": [
98 | "As you can see, depending on what just broke we see different error messages fromy the Python interpreter. Although it's not the most crucial distinction, there are roughly two main kinds of errors: **Syntax Errors** and **Exceptions**."
99 | ]
100 | },
101 | {
102 | "cell_type": "markdown",
103 | "metadata": {},
104 | "source": [
105 | "## Syntax Errors\n",
106 | "\n",
107 | "A Syntax Error is likely to the be the most frequent error you encounter when you're getting started. Syntax errors occur when the Python interpreter has trouble *[parsing](https://en.wikipedia.org/wiki/Parsing)* your code. In other words, it can read what you've typed but it doesn't quite make sense.\n",
108 | "\n",
109 | "It's a bit like when someone who doesn't speak your language fluently makes a mistake that to you seems funny, but to them is quite natural because they're extrapolating from what they know in a different language. Many English-speakers who are 'embarassed' by their level of Spanish are also apparently happy to inform Spanish-speakers that they are pregnant ('embarazada')! Or perhaps you think that the opposite of 'regardless' is 'irregardless'? These are natural mistakes, but they are 'errors' nonetheless. It's just that human beings – being smart – can figure out what you meant, while computers – being almost irredeemably stupid – cannot."
110 | ]
111 | },
112 | {
113 | "cell_type": "markdown",
114 | "metadata": {},
115 | "source": [
116 | "### A simple typo\n",
117 | "In the first example for instance, the error consists in a `print` command missing its parentheses:\n",
118 | "\n",
119 | "```python\n",
120 | " Input In [1]\n",
121 | " print \"Ouch!\"\n",
122 | " ^ \n",
123 | "SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)?\n",
124 | "```\n",
125 | "\n",
126 | "Let's read the error message together, from top-to-bottom: \n",
127 | "- First, the interpreter prints out the line number where it thinks the error can be found. In our simple case that's not a big deal since we only have one line of code anyway, but if you had thousands of lines of code spread across dozens of separate files this could be a life-saver!\n",
128 | "- In addition, Python also prints the _actual_ line where it threw up its hands and said \"I can't read this!\" \n",
129 | "- It has even added a little 'caret' ( ‸ ) to try to point out where on that line it _thinks_ the error is. We wouldn't recommend that you study _only_ that bit of the code but it's not a bad place to start.. \n",
130 | "- Lastly, Python prints out very clearly that the error is something to do with the syntax and in this case _even_ suggests a solution.\n",
131 | "\n",
132 | "Let's try to see if you can fix some bits of broken code by reading the errors and spotting the place where I've made some mistakes."
133 | ]
134 | },
135 | {
136 | "cell_type": "markdown",
137 | "metadata": {},
138 | "source": [
139 | "### A Challenge for You!\n",
140 | "\n",
141 | "Run the next two code cells and use the error messages to see if you can fix the following problems:"
142 | ]
143 | },
144 | {
145 | "cell_type": "code",
146 | "execution_count": null,
147 | "metadata": {},
148 | "outputs": [],
149 | "source": [
150 | "projection=\"Cassini-Soldner\" \n",
151 | "print(\"The \" projection + \" projection preserves distances along the central meridian.\")"
152 | ]
153 | },
154 | {
155 | "cell_type": "markdown",
156 | "metadata": {
157 | "solution2": "hidden",
158 | "solution2_first": true
159 | },
160 | "source": [
161 | "In this case do you think the suggested solution is the best? Or maybe we just need to add a `+`? _Beware_: the suggested solution to an error (by the computer) is not always the one you want!"
162 | ]
163 | },
164 | {
165 | "cell_type": "code",
166 | "execution_count": null,
167 | "metadata": {
168 | "solution2": "hidden"
169 | },
170 | "outputs": [],
171 | "source": [
172 | "projection=\"Cassini-Soldner\" \n",
173 | "print(\"The \" + projection + \" projection preserves distances along the central meridian.\")"
174 | ]
175 | },
176 | {
177 | "cell_type": "code",
178 | "execution_count": null,
179 | "metadata": {},
180 | "outputs": [],
181 | "source": [
182 | "other_projection \"Mollweide\"\n",
183 | "print(\"In the \" + other_projection + \" projection, meridians are ellipses.\")"
184 | ]
185 | },
186 | {
187 | "cell_type": "markdown",
188 | "metadata": {
189 | "solution2": "hidden",
190 | "solution2_first": true
191 | },
192 | "source": [
193 | "In this error we don't even get a suggestion of what to do to fix it! To identify the solution, first check which line the error occures on. Then, think about how assignment works (from a previous notebook). "
194 | ]
195 | },
196 | {
197 | "cell_type": "code",
198 | "execution_count": null,
199 | "metadata": {
200 | "solution2": "hidden"
201 | },
202 | "outputs": [],
203 | "source": [
204 | "other_projection=\"Mollweide\"\n",
205 | "print(\"In the \" + other_projection + \" projection, meridians are ellipses.\")"
206 | ]
207 | },
208 | {
209 | "cell_type": "markdown",
210 | "metadata": {},
211 | "source": [
212 | "## Exceptions\n",
213 | "\n",
214 | "Even if your code is syntatically exemplary (i.e. it's all perfectly written before you hit 'run'), errors might still occur for a wide variety of reasons: your computer is freaking out, you're not online, you haven't defined a variable yet... Obviously, these aren't syntax errors because your code would ordinarly work fine, it's just that something is missing and we think this is... _exceptional_. \n",
215 | "\n",
216 | "To help you find out which of the problems you've just hit, Python has the concept of exceptions and a huge taxonomy of specific errors ([here's a list](https://docs.python.org/2/library/exceptions.html)). That way, when something exceptional happens we know whether to restart the computer, check the Internet connection, or look for the place where the variable was supposedly defined.\n",
217 | "\n",
218 | "Let's start by considering these two *exception* examples (run each and examine the error message returned):"
219 | ]
220 | },
221 | {
222 | "cell_type": "code",
223 | "execution_count": null,
224 | "metadata": {},
225 | "outputs": [],
226 | "source": [
227 | "print(\"london has an approx. popoulation of \"+ popn + \" million people \")"
228 | ]
229 | },
230 | {
231 | "cell_type": "markdown",
232 | "metadata": {},
233 | "source": [
234 | "What do you think is going on here? What type of error have we received? (we'll examine the specific issue below). \n",
235 | "\n",
236 | "And this one:"
237 | ]
238 | },
239 | {
240 | "cell_type": "code",
241 | "execution_count": null,
242 | "metadata": {},
243 | "outputs": [],
244 | "source": [
245 | "\"london has an approx. popoulation of\" + 8.5 + \"million people \""
246 | ]
247 | },
248 | {
249 | "cell_type": "markdown",
250 | "metadata": {},
251 | "source": [
252 | "What 'type' of exception is this?\n",
253 | "\n",
254 | "We've seen `TypeError` before: remember that programmers don't like to write 'string' when they could write 'str', and that 'float' means 'floating point number'."
255 | ]
256 | },
257 | {
258 | "cell_type": "markdown",
259 | "metadata": {},
260 | "source": [
261 | "And now let's reconsider the `ZeroDivisionError` Exception:"
262 | ]
263 | },
264 | {
265 | "cell_type": "code",
266 | "execution_count": null,
267 | "metadata": {},
268 | "outputs": [],
269 | "source": [
270 | "4 / 0"
271 | ]
272 | },
273 | {
274 | "cell_type": "markdown",
275 | "metadata": {},
276 | "source": [
277 | "First of all we can see that every exception displays a specific message in the last line which gives you useful information about what just went wrong. That's because* exceptions* are different from plain *syntax errors* because they come in different types which are recognized by Python and printed accordingly. In our three examples above the *Exceptions* were: `NameError`, `TypeError`and `ZeroDivisionError`. These are the *exception types*.\n",
278 | "\n",
279 | "As with the *syntax errors*, the remaining part of the error message gives us useful pointers to how to go about fixing the code:\n",
280 | "\n",
281 | "- Once again Python starts with the location. This time though, it doesn't immediately point to a specific line but rather it explicits the *stack traceback*[1](https://en.wikipedia.org/wiki/Stack_trace),[2](https://en.wikipedia.org/wiki/Call_stack#STACK-FRAME) level (i.e. **very roughly speaking** the list of operations active in your code) where some mischief happened. \n",
282 | "\n",
283 | "- Luckly, there's a line reference in the following line (see the purple arrow (--->) pointing at line 1)\n",
284 | "\n",
285 | "```python\n",
286 | "Input In [5], in ()\n",
287 | "----> 1 print(\"london has an approx. popoulation of \"+ popn + \" million people \")\n",
288 | "\n",
289 | "NameError: name 'popn' is not defined\n",
290 | "\n",
291 | "```\n",
292 | "\n",
293 | "In the `NameError` example, the problem is that we have not yet created a variable named `popn` (we would need something like `popn = \"8.5\"` on the line prior to printing; the 8.5 in `\"\"` to avoid the `TypeError`). "
294 | ]
295 | },
296 | {
297 | "cell_type": "code",
298 | "execution_count": null,
299 | "metadata": {},
300 | "outputs": [],
301 | "source": [
302 | "popn = \"8.5\"\n",
303 | "print(\"london has an approx. popoulation of \"+ popn + \" million people \")"
304 | ]
305 | },
306 | {
307 | "cell_type": "markdown",
308 | "metadata": {},
309 | "source": [
310 | "Do you see how these are different from *Syntax Errors* conceptually and that they require you to do something different? Indeed, *Exceptions* are clearly specified in the language for two main reasons:\n",
311 | "\n",
312 | "- It allows you to restrict the range of possibilities regarding what went wrong, allowing faster and easier debugging. \n",
313 | "- Because exceptions are \"named\" errors, they're easier for the programmer to \"catch\" when the code is running. \n",
314 | "\n",
315 | "In other words, you can't know in advance whether your application will always have Internet access, so rather than just having your program 'blow up' or say \"Can't run, sorry!\", wouldn't it be better if it printed a helpful message to the user saying \"Hey, I don't seem to be online. Can you check the network connection?\" So in Python, one part of the application can 'throw' an exception (\"Hey, I'm not online\") when it tries to download a file and then it's up to the application to _catch_ that problem and print a warning to the user.\n",
316 | "\n",
317 | "If you see any of the following commands `TRY/EXCEPT/FINALLY` then that means a programmer is trying to limit the damage that could be caused by an exception."
318 | ]
319 | },
320 | {
321 | "cell_type": "markdown",
322 | "metadata": {},
323 | "source": [
324 | "### A Challenge for You!\n",
325 | "\n",
326 | "Run the next three code cells and use the error messages to see if you can fix the following problems:"
327 | ]
328 | },
329 | {
330 | "cell_type": "code",
331 | "execution_count": null,
332 | "metadata": {},
333 | "outputs": [],
334 | "source": [
335 | "london_population = 8600000\n",
336 | "print(\"London's population is \" + london_population)"
337 | ]
338 | },
339 | {
340 | "cell_type": "raw",
341 | "metadata": {
342 | "solution2": "hidden",
343 | "solution2_first": true
344 | },
345 | "source": [
346 | "HINT: Think about the _type_ of data that `london_population` holds _and_ how to convert a number into a string with the appropriate function"
347 | ]
348 | },
349 | {
350 | "cell_type": "code",
351 | "execution_count": null,
352 | "metadata": {
353 | "solution2": "hidden"
354 | },
355 | "outputs": [],
356 | "source": [
357 | "london_population = 8600000\n",
358 | "print(\"London's population is \" + str(london_population))"
359 | ]
360 | },
361 | {
362 | "cell_type": "code",
363 | "execution_count": null,
364 | "metadata": {},
365 | "outputs": [],
366 | "source": [
367 | "london_population / paris_population"
368 | ]
369 | },
370 | {
371 | "cell_type": "markdown",
372 | "metadata": {
373 | "solution2": "hidden",
374 | "solution2_first": true
375 | },
376 | "source": [
377 | "HINT: Define a new variable with the amount of people living in the French capital (2.2 million) "
378 | ]
379 | },
380 | {
381 | "cell_type": "code",
382 | "execution_count": null,
383 | "metadata": {
384 | "solution2": "hidden"
385 | },
386 | "outputs": [],
387 | "source": [
388 | "paris_population = 2200000\n",
389 | "london_population / paris_population"
390 | ]
391 | },
392 | {
393 | "cell_type": "code",
394 | "execution_count": null,
395 | "metadata": {},
396 | "outputs": [],
397 | "source": [
398 | "print(\"The ratio of london_population to paris_population is \" + london_population / paris_population)"
399 | ]
400 | },
401 | {
402 | "cell_type": "markdown",
403 | "metadata": {
404 | "solution2": "hidden",
405 | "solution2_first": true
406 | },
407 | "source": [
408 | "HINT: Think about the data type the result of the calculation needs to be"
409 | ]
410 | },
411 | {
412 | "cell_type": "code",
413 | "execution_count": null,
414 | "metadata": {
415 | "solution2": "hidden"
416 | },
417 | "outputs": [],
418 | "source": [
419 | "print(\"The ratio of london_population to paris_population is \" + str(london_population / paris_population))"
420 | ]
421 | },
422 | {
423 | "cell_type": "markdown",
424 | "metadata": {},
425 | "source": [
426 | "# How to read errors\n",
427 | "---\n",
428 | "\n",
429 | "Here's a \"rule of thumb\" list of actions to take when Python throws an error at you:\n",
430 | "\n",
431 | "- *Don't Panic!*\n",
432 | "- Take a deep breath and *READ CAREFULLY* the error message.\n",
433 | "- Ask yourself: is it a *Syntax Error* or an *Exception*?\n",
434 | "- In both cases: where's the faulty line?\n",
435 | "- For *Syntax Errors*: where's the little caret character ( ‸ ) pointing at?\n",
436 | "- For *Exceptions*: what kind of exception is that? Read the [Official Docs](https://docs.python.org/2/library/exceptions.html) and try to make sense of it\n",
437 | "- *No, really. Don't Panic!*"
438 | ]
439 | },
440 | {
441 | "cell_type": "markdown",
442 | "metadata": {},
443 | "source": [
444 | "# Learn to find help\n",
445 | "---\n",
446 | "\n",
447 | "We can't say this enough: [Google is your friend!](http://lmgtfy.com/?q=%22Learning+what+questions+to+ask+is+a+skill+in+itself%22+-Somebody+smart). And we really mean it. \n",
448 | "\n",
449 | "\n",
450 | "\n",
451 | "If learning how to interpret error messages is the first step to fixing broken code, the _second_ one (before you think about asking for help) is doing your 'homework'. And since you're not the first students to learn to program, there's a pretty good chance that someone has had your problem -- or one very similar to it -- before. \n",
452 | "\n",
453 | "The largest website/community/forum online that programmers from all over the world use on a daily basis is **StackOverflow**. The name itself is a nerdy inside joke referring to a *bad* situation in programming:\n",
454 | "\n",
455 | ">When a program attempts to use more space than is available on the call stack... the stack is said to overflow, typically resulting in a program crash. (source: [Wikipedia](https://en.wikipedia.org/wiki/Stack_overflow) )\n",
456 | "\n",
457 | "As the name implies, it's often the first resource that you want to consult if your program is not behaving as expected. For a quick overview of it's features refer directly to [StackOverflow's intro section](http://stackoverflow.com/tour).\n",
458 | "\n",
459 | "Often, you don't end up even needing to ask a question at all because the answer is already somewhere on Stack Overflow. But if, after carefully checking the site, you still can't find an answer to your problem then it's time to start thinking about asking your own question. \n",
460 | "\n",
461 | "In order to maximise your chances of success (and to avoid flooding the board with unclear and repetitive questions) read thoroughly the [How do I ask a good question?](http://stackoverflow.com/help/how-to-ask) section and always refer to the [Help Center](http://stackoverflow.com/help/asking).\n",
462 | "\n",
463 | "This is not meant to put you off in any way, but rather to let you know from the first moment what is the appropriate 'Netiquette' and accetable code of conduct that will make you stand out as competent programmer, even when you are asking for help."
464 | ]
465 | },
466 | {
467 | "cell_type": "markdown",
468 | "metadata": {},
469 | "source": [
470 | "# Code (Applied Geo-example)\n",
471 | "---"
472 | ]
473 | },
474 | {
475 | "cell_type": "markdown",
476 | "metadata": {},
477 | "source": [
478 | "If in the previous notebook we didn't even leave the U.K., this time we'll fly to the far away magical [Null Island](https://en.wikipedia.org/wiki/Null_Island).\n",
479 | "\n",
480 | "\n",
481 | "\n",
482 | "From its official government's [touristic office](http://www.nullisland.com/):\n",
483 | " \n",
484 | "> The Republic of Null Island\t \n",
485 | "> *LIKE NO PLACE ON EARTH!*\n",
486 | "\n",
487 | "In order to get there, you'll have to first solve the exercise, avoiding those pesky *Syntax Errors* and *Exceptions\"!"
488 | ]
489 | },
490 | {
491 | "cell_type": "code",
492 | "execution_count": null,
493 | "metadata": {
494 | "solution2": "hidden",
495 | "solution2_first": true
496 | },
497 | "outputs": [],
498 | "source": [
499 | "longitude = ???(0.0)\n",
500 | "latitude ??? str(0.0)\n",
501 | "\n",
502 | "\n",
503 | "# King's College marker\n",
504 | "null_island = \"https://www.openstreetmap.org/?mlat=\"+???+\"&mlon=\"+longitude+\"#map=5/\"+latitude+\"/\"+longitude\n",
505 | " \n",
506 | "prnt null_island\n"
507 | ]
508 | },
509 | {
510 | "cell_type": "code",
511 | "execution_count": null,
512 | "metadata": {
513 | "solution2": "hidden"
514 | },
515 | "outputs": [],
516 | "source": [
517 | "longitude = str(0.0)\n",
518 | "latitude = str(0.0)\n",
519 | "\n",
520 | "\n",
521 | "# Null_island marker\n",
522 | "null_island = \"https://www.openstreetmap.org/?mlat=\"+latitude+\"&mlon=\"+longitude+\"#map=5/\"+latitude+\"/\"+longitude\n",
523 | "print(null_island)"
524 | ]
525 | },
526 | {
527 | "cell_type": "markdown",
528 | "metadata": {},
529 | "source": [
530 | "To conclude: remember to always read the output, and try to understand what Python is telling you. You might learn a lot from these simple messages! "
531 | ]
532 | },
533 | {
534 | "cell_type": "markdown",
535 | "metadata": {},
536 | "source": [
537 | "### Further references:\n",
538 | "\n",
539 | "\n",
540 | "For more information on the island you might watch this [short video](https://motherboard.vice.com/read/a-journey-to-the-center-of-null-island). \n",
541 | "\n",
542 | "If you are on Twitter, don't forget to follow the [Null Island buoy](https://twitter.com/NullIslandBuoy)!\n",
543 | "\n",
544 | "\n",
545 | "General list or resources\n",
546 | "- [Awesome list of resources](https://github.com/vinta/awesome-python)\n",
547 | "- [Python Docs](https://docs.python.org/2.7/tutorial/introduction.html)\n",
548 | "- [HitchHiker's guide to Python](http://docs.python-guide.org/en/latest/intro/learning/)\n",
549 | "- [Python for Informatics](http://www.pythonlearn.com/book_007.pdf)\n",
550 | "- [Learn Python the Hard Way](http://learnpythonthehardway.org/book/)\n",
551 | "- [CodeAcademy](https://www.codecademy.com/learn/python)\n",
552 | "\n"
553 | ]
554 | },
555 | {
556 | "cell_type": "markdown",
557 | "metadata": {},
558 | "source": [
559 | "### Credits!\n",
560 | "\n",
561 | "#### Contributors:\n",
562 | "The following individuals have contributed to these teaching materials: \n",
563 | "- [James Millington](https://github.com/jamesdamillington)\n",
564 | "- [Jon Reades](https://github.com/jreades)\n",
565 | "- [Michele Ferretti](https://github.com/miccferr)\n",
566 | "- [Zahratu Shabrina](https://github.com/zarashabrina)\n",
567 | "\n",
568 | "#### License\n",
569 | "The content and structure of this teaching project itself is licensed under the [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 license](https://creativecommons.org/licenses/by-nc-sa/4.0/), and the contributing source code is licensed under [The MIT License](https://opensource.org/licenses/mit-license.php).\n",
570 | "\n",
571 | "#### Acknowledgements:\n",
572 | "Supported by the [Royal Geographical Society](https://www.rgs.org/HomePage.htm) (with the Institute of British Geographers) with a Ray Y Gildea Jr Award.\n",
573 | "\n",
574 | "#### Potential Dependencies:\n",
575 | "This notebook may depend on the following libraries: None"
576 | ]
577 | }
578 | ],
579 | "metadata": {
580 | "anaconda-cloud": {},
581 | "kernelspec": {
582 | "display_name": "Python 3 (ipykernel)",
583 | "language": "python",
584 | "name": "python3"
585 | },
586 | "language_info": {
587 | "codemirror_mode": {
588 | "name": "ipython",
589 | "version": 3
590 | },
591 | "file_extension": ".py",
592 | "mimetype": "text/x-python",
593 | "name": "python",
594 | "nbconvert_exporter": "python",
595 | "pygments_lexer": "ipython3",
596 | "version": "3.10.5"
597 | }
598 | },
599 | "nbformat": 4,
600 | "nbformat_minor": 4
601 | }
602 |
--------------------------------------------------------------------------------
/notebook-08-dictionaries.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Notebook-8: Dictionaries"
8 | ]
9 | },
10 | {
11 | "cell_type": "markdown",
12 | "metadata": {},
13 | "source": [
14 | "### Lesson Content \n",
15 | "\n",
16 | "In this lesson, we'll continue our exploration of more advanced data structures. Last time we took a peek at a way to represent ordered collections of items via **lists**.\n",
17 | "\n",
18 | "This time we'll use **dictionaries** to create collections of unordered items (this is just an easy distinction - there's much more to it - but it's a good way to start wrapping your head around the subject).\n",
19 | "\n",
20 | "### In this Notebook\n",
21 | "\n",
22 | "- Creating dictionaries\n",
23 | "- Accessing elements of dictionaries\n",
24 | "- Methods of dictionaries"
25 | ]
26 | },
27 | {
28 | "cell_type": "markdown",
29 | "metadata": {},
30 | "source": [
31 | "# Dictionaries\n",
32 | "----\n",
33 | "\n",
34 | "Dictionaries are another data type in Python that, like lists, contains multiple items. The difference is that while lists use an index to access _ordered_ items, dictionaries use 'keys' to access _unordered_ values.\n",
35 | "\n",
36 | "You can go back to our short video that talks about dictionaries:\n",
37 | "\n",
38 | "[](https://youtu.be/UDNtW3sy-og?t=613)"
39 | ]
40 | },
41 | {
42 | "cell_type": "markdown",
43 | "metadata": {},
44 | "source": [
45 | "Like lists, dictionaries are also found in other programming languages, often under a different name. For instance, Python dictionaries might be referred to elsewhere as \"maps\", \"hashes\", or \"associative arrays\").\n",
46 | "\n",
47 | "According to the [Official Docs](https://docs.python.org/2/tutorial/datastructures.html#dictionaries):\n",
48 | "\n",
49 | "> It is best to think of a dictionary as an unordered set of key-value pairs, with the requirement that the keys are unique (within one dictionary). A pair of braces creates an empty dictionary: {}\n",
50 | "\n",
51 | "In other words, dictionaries are not lists: instead of just a checklist, we now have a _key_ and a _value_. We use the key to find the value. So a generic dictionary looks like this:\n",
52 | "\n",
53 | "```python\n",
54 | "theDictionary = {\n",
55 | " key1: value1,\n",
56 | " key2: value2,\n",
57 | " key3: value3,\n",
58 | " ...\n",
59 | "}\n",
60 | "```\n",
61 | "Each key/value _pair_ is linked by a ':', and each pair is separated by a ','. It doesn't really matter if you put everything on newlines (as we do here) or all on the same line. We're just doing it this way to make it easier to read.\n",
62 | "\n",
63 | "Here's a more useful implementation of a dictionary:"
64 | ]
65 | },
66 | {
67 | "cell_type": "code",
68 | "execution_count": null,
69 | "metadata": {},
70 | "outputs": [],
71 | "source": [
72 | "myDict = {\n",
73 | " \"key1\": \"Value 1\",\n",
74 | " 3: \"3rd Value\",\n",
75 | " \"key2\": \"2nd Value\",\n",
76 | " \"Fourth Key\": [4.0, 'Jon']\n",
77 | "}\n",
78 | "print(myDict)"
79 | ]
80 | },
81 | {
82 | "cell_type": "markdown",
83 | "metadata": {},
84 | "source": [
85 | "At the danger of repeating ourselves (but to make the point!): an important difference between `dictionaries` and `lists` is that dictionaries are unordered. Always remember that you have no idea where things are stored in a `dictionary` and you can't rely on indexing like you can with a `list`. From this perspective, a Python dictionary is _not_ like a real dictionary (as a real dictionary presents the keys, i.e. words, in alphabetical order). \n",
86 | "\n",
87 | "And notice too that every type of data can go into a dictionary: strings, integers, and floats. There's even a `list` in this dictionary (`[4.0, 'Jon']`)! The _only_ constraint is that the **key must be *immutable***; this means that it is a simple, static identifier and that can't change."
88 | ]
89 | },
90 | {
91 | "cell_type": "code",
92 | "execution_count": null,
93 | "metadata": {},
94 | "outputs": [],
95 | "source": [
96 | "# this will result in an error\n",
97 | "myFaultyDict = {\n",
98 | " [\"key1\", 1]: \"Value 1\", \n",
99 | " \"key2\": \"2nd Value\", \n",
100 | " 3: \"3rd Value\", \n",
101 | " 8.0: [5, 'jon']\n",
102 | "}"
103 | ]
104 | },
105 | {
106 | "cell_type": "markdown",
107 | "metadata": {},
108 | "source": [
109 | "This doesn't work because you can't use a list (`[\"key1\",1]`) as a key, though as you saw above you can use a list as a _value_. For more on the subject of (im)mutability checkout [this SO answer](http://stackoverflow.com/a/8059504) ).\n",
110 | "\n",
111 | "## Accessing Dictionaries\n",
112 | "\n",
113 | "Like lists, we access an element in a dictionary using a 'location' marked out by a pair of square brackets (*[...]*). The difference is that the index is no longer an integer indicating the position of the item that we want to access, but is a *key* in the *key:value* pair:"
114 | ]
115 | },
116 | {
117 | "cell_type": "code",
118 | "execution_count": null,
119 | "metadata": {},
120 | "outputs": [],
121 | "source": [
122 | "print(myDict[\"key1\"])\n",
123 | "print(myDict[\"Fourth Key\"])"
124 | ]
125 | },
126 | {
127 | "cell_type": "markdown",
128 | "metadata": {},
129 | "source": [
130 | "Notice how now we just jump straight to the item we want? We don't need to think about \"Was that the fourth item on the list? Or the fifth?\" We just use a sensible key, and we can ask for the associated value directly.\n",
131 | "\n",
132 | "#### A challenge for you!\n",
133 | "\n",
134 | "How would you print out \"2nd Value\" from `myDict`?"
135 | ]
136 | },
137 | {
138 | "cell_type": "code",
139 | "execution_count": null,
140 | "metadata": {
141 | "solution2": "hidden",
142 | "solution2_first": true
143 | },
144 | "outputs": [],
145 | "source": [
146 | "print(???)"
147 | ]
148 | },
149 | {
150 | "cell_type": "code",
151 | "execution_count": null,
152 | "metadata": {
153 | "solution2": "hidden"
154 | },
155 | "outputs": [],
156 | "source": [
157 | "print(myDict[\"key2\"])"
158 | ]
159 | },
160 | {
161 | "cell_type": "markdown",
162 | "metadata": {},
163 | "source": [
164 | "When it comes to error messages, `dict`s and `list`s behave in similar ways. If you try to access a dictionary using a key that doesn't exist then Python raises an *exception*. \n",
165 | "\n",
166 | "What is the name of the exception generated by the following piece of code? Can you find it the [Official Docs](https://docs.python.org/2/library/exceptions.html)?"
167 | ]
168 | },
169 | {
170 | "cell_type": "code",
171 | "execution_count": null,
172 | "metadata": {},
173 | "outputs": [],
174 | "source": [
175 | "print(myDict[99])"
176 | ]
177 | },
178 | {
179 | "cell_type": "markdown",
180 | "metadata": {},
181 | "source": [
182 | "Handy, no? Again, Python's error messages are giving you helpful clues about where the problem it's encountering might be! Up above we had a `TypeError` when we tried to create a key using a list. Here, we have a `KeyError` that tells us something must be wrong with using `99` as a key in `myDict`. In this case, it's that there is no key 99!"
183 | ]
184 | },
185 | {
186 | "cell_type": "markdown",
187 | "metadata": {},
188 | "source": [
189 | "#### A challenge for you\n",
190 | "\n",
191 | "Can you turn these lists into dictionary called `capitalDict`?"
192 | ]
193 | },
194 | {
195 | "cell_type": "code",
196 | "execution_count": null,
197 | "metadata": {},
198 | "outputs": [],
199 | "source": [
200 | "country = ['Costa Rica','Croatia','Cuba'] #keys\n",
201 | "capital_city = ['San Jose','Zagreb','Havana'] #values"
202 | ]
203 | },
204 | {
205 | "cell_type": "markdown",
206 | "metadata": {},
207 | "source": [
208 | "We already found out that we can easily convert between different types. Dictionary is also a data type, so we can convert these lists to dictionary just like we convert strings into integers using `str` and `int`. However, since we need to pair them, we need an additional function called `zip` to pair the keys and values."
209 | ]
210 | },
211 | {
212 | "cell_type": "code",
213 | "execution_count": null,
214 | "metadata": {
215 | "solution2": "hidden",
216 | "solution2_first": true
217 | },
218 | "outputs": [],
219 | "source": [
220 | "capitalDict = ???(zip(???,???))"
221 | ]
222 | },
223 | {
224 | "cell_type": "code",
225 | "execution_count": null,
226 | "metadata": {
227 | "solution2": "hidden"
228 | },
229 | "outputs": [],
230 | "source": [
231 | "capitalDict = dict(zip(country,capital_city))\n",
232 | "capitalDict"
233 | ]
234 | },
235 | {
236 | "cell_type": "markdown",
237 | "metadata": {},
238 | "source": [
239 | "How would you print out the capital city of Croatia from `capitalDict`?"
240 | ]
241 | },
242 | {
243 | "cell_type": "code",
244 | "execution_count": null,
245 | "metadata": {
246 | "solution2": "hidden",
247 | "solution2_first": true
248 | },
249 | "outputs": [],
250 | "source": [
251 | "print(???)"
252 | ]
253 | },
254 | {
255 | "cell_type": "code",
256 | "execution_count": null,
257 | "metadata": {
258 | "solution2": "hidden"
259 | },
260 | "outputs": [],
261 | "source": [
262 | "print(capitalDict['Croatia'])"
263 | ]
264 | },
265 | {
266 | "cell_type": "markdown",
267 | "metadata": {},
268 | "source": [
269 | "## Creating a Simple Phone Book\n",
270 | "\n",
271 | "One of the simplest uses of a dictionary is as a phone book! (If you're not sure what a phone book is [here's a handy guide](https://www.youtube.com/watch?v=QtntnfgIyUg) and [here's an example of someone using one](https://www.youtube.com/watch?v=updE5LVe6tg)). \n",
272 | "\n",
273 | "So here are some useful contact numbers:\n",
274 | "1. American Emergency Number: 911\n",
275 | "2. British Emergency Number: 999\n",
276 | "3. Icelandic Emergency Number: 112\n",
277 | "4. French Emergency Number: 112\n",
278 | "5. Russian Emergency Number: 102\n",
279 | "\n",
280 | "Now, how would you create a dictionary that allowed us to look up and print out an emergency phone number based on the [two-character ISO country code](http://www.nationsonline.org/oneworld/country_code_list.htm)? It's going to look a little like this:\n",
281 | "```python\n",
282 | "eNumbers = {\n",
283 | " ...\n",
284 | "}\n",
285 | "print(\"The Icelandic emergency number is \" + eNumbers['IS'])\n",
286 | "print(\"The American emergency number is \" + eNumbers['US'])\n",
287 | "```"
288 | ]
289 | },
290 | {
291 | "cell_type": "code",
292 | "execution_count": null,
293 | "metadata": {},
294 | "outputs": [],
295 | "source": [
296 | "eNumbers = {\n",
297 | " \"IS\": '112', # It's not very important here whether we use single- or double-quotes\n",
298 | " \"US\": '911'\n",
299 | "}\n",
300 | "print(\"The Icelandic emergency number is \" + eNumbers['IS'])\n",
301 | "print(\"The American emergency number is \" + eNumbers['US']) "
302 | ]
303 | },
304 | {
305 | "cell_type": "markdown",
306 | "metadata": {},
307 | "source": [
308 | "## Useful Dictionary Methods\n",
309 | "\n",
310 | "We are going to see in the next couple of notebooks how to systematically access values in a dictionary (amongst other things). For now, let's also take in the fact the dictionaries _also_ have utility *methods* similar to what we saw with the `list`. And as with the list, these methods are functions that only make sense _when_ you're working with a dictionary, so they're bundled up in a way that makes them easy to use.\n",
311 | "\n",
312 | "Let's say that you have forgotten what *keys* you put in your dictionary..."
313 | ]
314 | },
315 | {
316 | "cell_type": "code",
317 | "execution_count": null,
318 | "metadata": {},
319 | "outputs": [],
320 | "source": [
321 | "programmers = {\n",
322 | " \"Charles\": \"Babbage\",\n",
323 | " \"Ada\": \"Lovelace\",\n",
324 | " \"Alan\": \"Turing\"\n",
325 | "}\n",
326 | "\n",
327 | "print(programmers.keys())"
328 | ]
329 | },
330 | {
331 | "cell_type": "markdown",
332 | "metadata": {},
333 | "source": [
334 | "Or maybe you just need to access all of the values without troubling to ask for each key:"
335 | ]
336 | },
337 | {
338 | "cell_type": "code",
339 | "execution_count": null,
340 | "metadata": {},
341 | "outputs": [],
342 | "source": [
343 | "print(programmers.values())"
344 | ]
345 | },
346 | {
347 | "cell_type": "markdown",
348 | "metadata": {},
349 | "source": [
350 | "Or maybe you even need to get them as pairs:"
351 | ]
352 | },
353 | {
354 | "cell_type": "code",
355 | "execution_count": null,
356 | "metadata": {},
357 | "outputs": [],
358 | "source": [
359 | "# Output is a list of key-value pairs!\n",
360 | "print(programmers.items())"
361 | ]
362 | },
363 | {
364 | "cell_type": "markdown",
365 | "metadata": {},
366 | "source": [
367 | "#### A challenge for you\n",
368 | "\n",
369 | "Can you access all the values of `capitalDict` from the previous challenge?"
370 | ]
371 | },
372 | {
373 | "cell_type": "code",
374 | "execution_count": null,
375 | "metadata": {
376 | "solution2": "hidden",
377 | "solution2_first": true
378 | },
379 | "outputs": [],
380 | "source": [
381 | "print(???)"
382 | ]
383 | },
384 | {
385 | "cell_type": "code",
386 | "execution_count": null,
387 | "metadata": {
388 | "solution2": "hidden"
389 | },
390 | "outputs": [],
391 | "source": [
392 | "print(capitalDict.values())"
393 | ]
394 | },
395 | {
396 | "cell_type": "markdown",
397 | "metadata": {},
398 | "source": [
399 | "### Are You On the List? (Part 2)\n",
400 | "\n",
401 | "As with the `list` data type, you can check the presence or absence of a key in a dictionary, using the *in* / *not in* operators... but note that they only work on keys."
402 | ]
403 | },
404 | {
405 | "cell_type": "code",
406 | "execution_count": null,
407 | "metadata": {},
408 | "outputs": [],
409 | "source": [
410 | "print(\"Charles\" in programmers)\n",
411 | "print(\"Babbage\" in programmers)\n",
412 | "print(True not in programmers)"
413 | ]
414 | },
415 | {
416 | "cell_type": "markdown",
417 | "metadata": {},
418 | "source": [
419 | "### What Do You Do if You're Not On the List?\n",
420 | "\n",
421 | "One challenge with dictionaries is that sometimes we have no real idea if a key exists or not. With a list, it's pretty easy to figure out whether or not an index exists because we can just ask Python to tell us the _length_ of the list. So that makes it fairly easy to avoid having the list 'blow up' by throwing an exception.\n",
422 | "\n",
423 | "It's rather harder for a dictionary though, so that's why we have the dedicated **`get()`** method: it not only allows us to fetch the *value* associated with a *key*, it also allows us to specify a default value in case the key does not exist:"
424 | ]
425 | },
426 | {
427 | "cell_type": "code",
428 | "execution_count": null,
429 | "metadata": {},
430 | "outputs": [],
431 | "source": [
432 | "print(programmers.get(\"Lady Ada\", \"Are you sure you spelled that right?\") )"
433 | ]
434 | },
435 | {
436 | "cell_type": "markdown",
437 | "metadata": {},
438 | "source": [
439 | "See how this works: the key doesn't exist, but unlike what happened when we asked for `myDict[99]` we don't get an exception, we get the default value specified as the _second_ input to the method `get`. \n",
440 | "\n",
441 | "So you've learned two things here: that functions can take more than one input (this one takes both the key that we're looking for, and value to return if Python can't find the key); and that different types (or classes) of data have different methods (there's no `get` for lists)."
442 | ]
443 | },
444 | {
445 | "cell_type": "markdown",
446 | "metadata": {},
447 | "source": [
448 | "## Lists of Lists, Dictionaries of Lists, Dictionaries of Dictionaries... Oh my!\n",
449 | "\n",
450 | "OK, this is where it's going to get a little weird but you're also going to see how programming is a little like Lego: once you get the building blocks, you can make lots of cool/strange/useful contraptions from some pretty simple concepts.\n",
451 | "\n",
452 | "Remember that a list or dictionary can store _anything_: so the first item in your list could itself _be_ a list! For most people starting out on programming, this is the point where their brain starts hurting (it happened to us) and you might want to throw up your hands in frustration thinking \"I'm never going to understand this!\" But if you stick with it, you will. \n",
453 | "\n",
454 | "And this is really the start of the power of computation.\n",
455 | "\n",
456 | "### A Data Set of City Attributes\n",
457 | "\n",
458 | "Let's start out with what some (annoying) people would call a 'trivial' example of how a list-of-lists (LoLs, though most people aren't laughing) can be useful. Let's think through what's going on below: what happens if we write `cityData[0]`?"
459 | ]
460 | },
461 | {
462 | "cell_type": "code",
463 | "execution_count": null,
464 | "metadata": {},
465 | "outputs": [],
466 | "source": [
467 | "# Format: city, country, population, area (km^2)\n",
468 | "cityData = [\n",
469 | " ['London','U.K.',8673713,1572],\n",
470 | " ['Paris','France',2229621,105],\n",
471 | " ['Washington, D.C.','U.S.A.',672228,177],\n",
472 | " ['Abuja','Nigeria',1235880,1769],\n",
473 | " ['Beijing','China',21700000,16411],\n",
474 | "]\n",
475 | "\n",
476 | "print(cityData[0])"
477 | ]
478 | },
479 | {
480 | "cell_type": "markdown",
481 | "metadata": {},
482 | "source": [
483 | "So how would we access something inside the list returned from `cityData[0]`?\n",
484 | "\n",
485 | "Why not try:\n",
486 | "```python\n",
487 | "cityData[0][1]\n",
488 | "```\n",
489 | "See if you can figure out how to retrieve and print the following from `cityData`:\n",
490 | "1. France\n",
491 | "2. 16411\n",
492 | "3. Washington, D.C.\n",
493 | "\n",
494 | "Type the code into the coding area below..."
495 | ]
496 | },
497 | {
498 | "cell_type": "code",
499 | "execution_count": null,
500 | "metadata": {},
501 | "outputs": [],
502 | "source": [
503 | "print(cityData[1][1])\n",
504 | "print(cityData[4][3])\n",
505 | "print(cityData[2][0])"
506 | ]
507 | },
508 | {
509 | "cell_type": "markdown",
510 | "metadata": {},
511 | "source": [
512 | "#### A challenge for you\n",
513 | "Can you retrieve and print the following from `cityData`:\n",
514 | "1. Nigeria\n",
515 | "2. 8673713\n",
516 | "3. 177"
517 | ]
518 | },
519 | {
520 | "cell_type": "code",
521 | "execution_count": null,
522 | "metadata": {
523 | "solution2": "hidden",
524 | "solution2_first": true
525 | },
526 | "outputs": [],
527 | "source": [
528 | "print(???)\n",
529 | "print(???)\n",
530 | "print(???) "
531 | ]
532 | },
533 | {
534 | "cell_type": "code",
535 | "execution_count": null,
536 | "metadata": {
537 | "solution2": "hidden"
538 | },
539 | "outputs": [],
540 | "source": [
541 | "print(cityData[3][1])\n",
542 | "print(cityData[0][2])\n",
543 | "print(cityData[2][3])"
544 | ]
545 | },
546 | {
547 | "cell_type": "markdown",
548 | "metadata": {},
549 | "source": [
550 | "### A Phonebook+\n",
551 | "\n",
552 | "So that's an LoL (list-of-lists). Let's extend this idea to what we'll call Phonebook+ which will be a DoL (dictionary-of-lists). In other words, a phonebook that can _do more_ than just give us phone numbers! We're going to build on the emergency phonebook example above."
553 | ]
554 | },
555 | {
556 | "cell_type": "code",
557 | "execution_count": null,
558 | "metadata": {},
559 | "outputs": [],
560 | "source": [
561 | "# American Emergency Number: 911\n",
562 | "# British Emergency Number: 999\n",
563 | "# Icelandic Emergency Number: 112\n",
564 | "# French Emergency Number: 112\n",
565 | "# Russian Emergency Number: 102\n",
566 | "eNumbers = {\n",
567 | " 'IS': ['Icelandic',112],\n",
568 | " 'US': ['American',911],\n",
569 | " 'FR': ['French',112],\n",
570 | " 'RU': ['Russian',102],\n",
571 | " 'UK': ['British',999]\n",
572 | "}\n",
573 | "print(\"The \" + eNumbers['IS'][0] + \" emergency number is \" + str(eNumbers['IS'][1]))\n",
574 | "print(\"The \" + eNumbers['US'][0] + \" emergency number is \" + str(eNumbers['US'][1]))\n",
575 | "print(\"The \" + eNumbers['FR'][0] + \" emergency number is \" + str(eNumbers['FR'][1]))"
576 | ]
577 | },
578 | {
579 | "cell_type": "markdown",
580 | "metadata": {},
581 | "source": [
582 | "#### A Challenge for you\n",
583 | "\n",
584 | "See if you can create the rest of the `eNumbers` dictionary and then print out the Russian and British emergency numbers."
585 | ]
586 | },
587 | {
588 | "cell_type": "code",
589 | "execution_count": null,
590 | "metadata": {
591 | "solution2": "hidden",
592 | "solution2_first": true
593 | },
594 | "outputs": [],
595 | "source": [
596 | "print(\"The \" + ??? + \" emergency number is \" + ???)\n",
597 | "print(\"The \" + ??? + \" emergency number is \" + ???) "
598 | ]
599 | },
600 | {
601 | "cell_type": "code",
602 | "execution_count": null,
603 | "metadata": {
604 | "solution2": "hidden"
605 | },
606 | "outputs": [],
607 | "source": [
608 | "print(\"The \" + eNumbers['RU'][0] + \" emergency number is \" + str(eNumbers['RU'][1]))\n",
609 | "print(\"The \" + eNumbers['UK'][0] + \" emergency number is \" + str(eNumbers['UK'][1]))"
610 | ]
611 | },
612 | {
613 | "cell_type": "markdown",
614 | "metadata": {},
615 | "source": [
616 | "### Dictionary-of-Dictionaries\n",
617 | "\n",
618 | "OK, this is the last thing we're going to throw at you today – getting your head around 'nested' lists and dictionaries is _hard_. Really hard. But it's the all-important first step to thinking about data the way that computer 'thinks' about it. This is really abstract: something that you access by keys, which in turn gives you access to other keys... it's got a name: recursion. And it's probably one of the cleverest things about computing. \n",
619 | "\n",
620 | "Here's a bit of a complex DoD, combined with a DoL, and other nasties:"
621 | ]
622 | },
623 | {
624 | "cell_type": "code",
625 | "execution_count": null,
626 | "metadata": {},
627 | "outputs": [],
628 | "source": [
629 | "cityData2 = {\n",
630 | " 'London' : {\n",
631 | " 'population': 8673713,\n",
632 | " 'area': 1572, \n",
633 | " 'location': [51.507222, -0.1275],\n",
634 | " 'country': {\n",
635 | " 'ISO2': 'UK',\n",
636 | " 'Full': 'United Kingdom',\n",
637 | " },\n",
638 | " },\n",
639 | " 'Paris' : {\n",
640 | " 'population': 2229621,\n",
641 | " 'area': 105.4,\n",
642 | " 'location': [48.8567, 2.3508],\n",
643 | " 'country': {\n",
644 | " 'ISO2': 'FR',\n",
645 | " 'Full': 'France',\n",
646 | " },\n",
647 | " }\n",
648 | "}"
649 | ]
650 | },
651 | {
652 | "cell_type": "markdown",
653 | "metadata": {},
654 | "source": [
655 | "Try the following code in the code cell below:\n",
656 | "```python\n",
657 | "print(cityData2['Paris'])\n",
658 | "print(cityData2['Paris']['country']['ISO2'])\n",
659 | "print(cityData2['Paris']['location'][0])\n",
660 | "```"
661 | ]
662 | },
663 | {
664 | "cell_type": "code",
665 | "execution_count": null,
666 | "metadata": {
667 | "solution2": "hidden",
668 | "solution2_first": true
669 | },
670 | "outputs": [],
671 | "source": [
672 | "#your code here"
673 | ]
674 | },
675 | {
676 | "cell_type": "code",
677 | "execution_count": null,
678 | "metadata": {
679 | "solution2": "hidden"
680 | },
681 | "outputs": [],
682 | "source": [
683 | "print(cityData2['Paris'])\n",
684 | "print(cityData2['Paris']['country']['ISO2'])\n",
685 | "print(cityData2['Paris']['location'][0])"
686 | ]
687 | },
688 | {
689 | "cell_type": "markdown",
690 | "metadata": {},
691 | "source": [
692 | "Now, figure out how to print:\n",
693 | "`The population of Paris, the capital of France (FR), is 2229621.`"
694 | ]
695 | },
696 | {
697 | "cell_type": "code",
698 | "execution_count": null,
699 | "metadata": {
700 | "solution2": "hidden",
701 | "solution2_first": true
702 | },
703 | "outputs": [],
704 | "source": [
705 | "print(\"The population of Paris, the capital of \" + str(cityData2['Paris']['country']['Full']) + \" \" \\\n",
706 | " + \"(\" + ??? + \") \" + \"is \"+ ??? + \".\")"
707 | ]
708 | },
709 | {
710 | "cell_type": "code",
711 | "execution_count": null,
712 | "metadata": {
713 | "solution2": "hidden"
714 | },
715 | "outputs": [],
716 | "source": [
717 | "print(\"The population of Paris, the capital of \" + str(cityData2['Paris']['country']['Full']) + \" \" \\\n",
718 | " + \"(\" + str(cityData2['Paris']['country']['ISO2']) + \") \" + \"is \"+ str(cityData2['Paris']['population']) + \".\")"
719 | ]
720 | },
721 | {
722 | "cell_type": "markdown",
723 | "metadata": {},
724 | "source": [
725 | "And print `It has a density of 21153.899 persons per square km.` \n",
726 | "**Hint**: to calculate density, divide population with area."
727 | ]
728 | },
729 | {
730 | "cell_type": "code",
731 | "execution_count": null,
732 | "metadata": {
733 | "solution2": "hidden",
734 | "solution2_first": true
735 | },
736 | "outputs": [],
737 | "source": [
738 | "print(\"It has a density of \" + ???)"
739 | ]
740 | },
741 | {
742 | "cell_type": "code",
743 | "execution_count": null,
744 | "metadata": {
745 | "solution2": "hidden"
746 | },
747 | "outputs": [],
748 | "source": [
749 | "print(\"It has a density of \" + str(cityData2['Paris']['population'] / cityData2['Paris']['area'] ))"
750 | ]
751 | },
752 | {
753 | "cell_type": "markdown",
754 | "metadata": {},
755 | "source": [
756 | "Do the same for London."
757 | ]
758 | },
759 | {
760 | "cell_type": "code",
761 | "execution_count": null,
762 | "metadata": {
763 | "solution2": "hidden",
764 | "solution2_first": true
765 | },
766 | "outputs": [],
767 | "source": [
768 | "print(???)"
769 | ]
770 | },
771 | {
772 | "cell_type": "code",
773 | "execution_count": null,
774 | "metadata": {
775 | "solution2": "hidden"
776 | },
777 | "outputs": [],
778 | "source": [
779 | "# Note that we can tweak the formatting a bit: Python is smart \n",
780 | "# enough to understand that if you have a '+' on the end of a\n",
781 | "# string and there next line is also a string then it'll \n",
782 | "# continue to concatenate the string...\n",
783 | "print(\"The population of \" + 'London' + \", the capital of \" + \n",
784 | " cityData2['London']['country']['Full'] + \" (\" + cityData2['London']['country']['ISO2'] + \"), is \" + \n",
785 | " str(cityData2['London']['population']) + \". It has a density of \" + \n",
786 | " str(cityData2['London']['population']/cityData2['London']['area']) + \" persons per square km\")\n",
787 | "\n",
788 | "# But a _better_ way to do this might be one in which we don't\n",
789 | "# hard-code 'London' into the output -- by changing the variable\n",
790 | "# 'c' to Paris we can change the output completely...\n",
791 | "c = 'Paris'\n",
792 | "cd = cityData2[c]\n",
793 | "print(\"The population of \" + c + \", the capital of \" + \n",
794 | " cd['country']['Full'] + \" (\" + cd['country']['ISO2'] + \"), is \" + \n",
795 | " str(cd['population']) + \". It has a density of \" + \n",
796 | " \"{0:8.1f}\".format(cd['population']/cd['area']) + \" persons per square km\")"
797 | ]
798 | },
799 | {
800 | "cell_type": "markdown",
801 | "metadata": {},
802 | "source": [
803 | "# Code (Applied Geo-example)"
804 | ]
805 | },
806 | {
807 | "cell_type": "markdown",
808 | "metadata": {},
809 | "source": [
810 | "Let's continue our trips around the world! This time though, we'll do things better, and instead of using a simple URL, we are going to use a real-word geographic data type, that you can use on a web-map or in your favourite GIS software.\n",
811 | "\n",
812 | "If you look down below at the `KCL_position` variable you'll see that we're assigning it a complex and scary data structure. Don't be afraid! If you look closely enough you will notice that is just made out the \"building blocks\" that we've seen so far: `floats`, `lists`, `strings`..all wrapped comfortably in a cosy `dictionary`!\n",
813 | "\n",
814 | "This is simply a formalised way to represent a *geographic marker* (a pin on the map!) in a format called `GeoJSON`.\n",
815 | "\n",
816 | "According to the awesome [Lizy Diamond](https://twitter.com/lyzidiamond?lang=en-gb) \n",
817 | "\n",
818 | ">[GeoJSON](http://geojson.org/geojson-spec.html) is an open and popular geographic data format commonly used in web applications. It is an extension of a format called [JSON](http://json.org), which stands for *JavaScript Object Notation*. Basically, JSON is a table turned on its side. GeoJSON extends JSON by adding a section called \"geometry\" such that you can define coordinates for the particular object (point, line, polygon, multi-polygon, etc). A point in a GeoJSON file might look like this:\n",
819 | "\n",
820 | " {\n",
821 | " \"type\": \"Feature\",\n",
822 | " \"geometry\": {\n",
823 | " \"type\": \"Point\",\n",
824 | " \"coordinates\": [\n",
825 | " -122.65335738658904,\n",
826 | " 45.512083676585156\n",
827 | " ]\n",
828 | " },\n",
829 | " \"properties\": {\n",
830 | " \"name\": \"Hungry Heart Cupcakes\",\n",
831 | " \"address\": \"1212 SE Hawthorne Boulevard\",\n",
832 | " \"website\": \"http://www.hungryheartcupcakes.com\",\n",
833 | " \"gluten free\": \"no\"\n",
834 | " }\n",
835 | " }\n",
836 | " \n",
837 | ">GeoJSON files have to have both a `\"geometry\"` section and a `\"properties\"` section. The `\"geometry\"` section houses the geographic information of the feature (its location and type) and the `\"properties\"` section houses all of the descriptive information about the feature (like fields in an attribute table). [Source](https://github.com/lyzidiamond/learn-geojson)\n",
838 | "\n",
839 | "\n",
840 | "Now, in order to have our first \"webmap\", we have to re-create such `GeoJSON` structure. \n",
841 | "\n",
842 | "As you can see there are two variables containing King's College Longitude/Latitude coordinate position. Unfortunately, they are in the wrong data type. Also, the variable `longitude` is not included in the list `KCLCoords` and the list itself is not assigned as a value to the `KCLGeometry`dictionary.\n",
843 | "\n",
844 | "Take all the necessary steps to fix the code, using the functions we've seen so far.\n",
845 | "\n"
846 | ]
847 | },
848 | {
849 | "cell_type": "code",
850 | "execution_count": null,
851 | "metadata": {},
852 | "outputs": [],
853 | "source": [
854 | "# Don't worry about the following lines\n",
855 | "# I'm simply requesting some modules to\n",
856 | "# have additional functions at my disposal\n",
857 | "# which usually are not immediately available\n",
858 | "import json\n",
859 | "from ipyleaflet import Map, GeoJSON, basemaps\n",
860 | "\n",
861 | "# King's College coordinates\n",
862 | "# What format are they in? Does it seem appropriate?\n",
863 | "# How would you convert them back to numbers?\n",
864 | "longitude = -0.11596798896789551\n",
865 | "latitude = 51.51130657591914\n",
866 | "\n",
867 | "# Set this up as a coordinate pair \n",
868 | "KCL_Coords = [longitude, latitude ]\n",
869 | "\n",
870 | "# How can you assign KCLCoords to \n",
871 | "# the key KCLGeometry[\"coordinates\"]?\n",
872 | "KCL_Geometry = {\n",
873 | " \"type\": \"Point\",\n",
874 | "\n",
875 | " \"coordinates\": KCL_Coords\n",
876 | " }\n",
877 | "\n",
878 | "KCL_Position = {\n",
879 | " \"type\": \"FeatureCollection\",\n",
880 | " \"features\": [\n",
881 | " {\n",
882 | " \"type\": \"Feature\",\n",
883 | " \"properties\": {\n",
884 | " \"marker-color\": \"#7e7e7e\",\n",
885 | " \"marker-size\": \"medium\",\n",
886 | " \"marker-symbol\": \"building\",\n",
887 | " \"name\": \"KCL\"\n",
888 | " },\n",
889 | " \"geometry\": KCL_Geometry\n",
890 | " }\n",
891 | " ]\n",
892 | "}\n",
893 | "\n",
894 | "# OUTPUT\n",
895 | "# -----------------------------------------------------------\n",
896 | "# I'm justing using the \"imported\" module to print the output\n",
897 | "# in a nice and formatted way\n",
898 | "print(json.dumps(KCL_Position, indent=4))"
899 | ]
900 | },
901 | {
902 | "cell_type": "code",
903 | "execution_count": null,
904 | "metadata": {},
905 | "outputs": [],
906 | "source": [
907 | "# We can also show this in Jupyter directly \n",
908 | "# (it won't show up in the PDF version though)\n",
909 | "m = Map(center = (51.51, -0.10), zoom=12, min_zoom=5, max_zoom=20, \n",
910 | " basemap=basemaps.OpenTopoMap)\n",
911 | "geo = GeoJSON(data=KCL_Position)\n",
912 | "m.add_layer(geo)\n",
913 | "m"
914 | ]
915 | },
916 | {
917 | "cell_type": "markdown",
918 | "metadata": {},
919 | "source": [
920 | "And here we request a remote GeoJSON file (from `url`), convert to a dictionary, and place it in a map as a new layer."
921 | ]
922 | },
923 | {
924 | "cell_type": "code",
925 | "execution_count": null,
926 | "metadata": {},
927 | "outputs": [],
928 | "source": [
929 | "import json\n",
930 | "import random\n",
931 | "import requests\n",
932 | "\n",
933 | "from ipyleaflet import Map, GeoJSON\n",
934 | "\n",
935 | "url = 'https://github.com/jupyter-widgets/ipyleaflet/raw/master/examples/europe_110.geo.json'\n",
936 | "r = requests.get(url)\n",
937 | "d = r.content.decode(\"utf-8\")\n",
938 | "j = json.loads(d)\n",
939 | "\n",
940 | "def random_color(feature):\n",
941 | " return {\n",
942 | " 'color': 'black',\n",
943 | " 'fillColor': random.choice(['red', 'yellow', 'green', 'orange']),\n",
944 | " }\n",
945 | "\n",
946 | "m = Map(center=(50.6252978589571, 0.34580993652344), zoom=3)\n",
947 | "\n",
948 | "geo_json = GeoJSON(\n",
949 | " data=j,\n",
950 | " style={\n",
951 | " 'opacity': 1, 'dashArray': '9', 'fillOpacity': 0.1, 'weight': 1\n",
952 | " },\n",
953 | " hover_style={\n",
954 | " 'color': 'white', 'dashArray': '0', 'fillOpacity': 0.5\n",
955 | " },\n",
956 | " style_callback=random_color\n",
957 | ")\n",
958 | "m.add_layer(geo_json)\n",
959 | "\n",
960 | "m"
961 | ]
962 | },
963 | {
964 | "cell_type": "markdown",
965 | "metadata": {},
966 | "source": [
967 | "As proof that behind this all is just a dictionary:"
968 | ]
969 | },
970 | {
971 | "cell_type": "code",
972 | "execution_count": null,
973 | "metadata": {},
974 | "outputs": [],
975 | "source": [
976 | "print(json.dumps(j, indent=4))"
977 | ]
978 | },
979 | {
980 | "cell_type": "markdown",
981 | "metadata": {},
982 | "source": [
983 | "After you've run the code, Python will have saved a file called `my-first-marker.geojson` in the folder where you are running the notebook. Try to upload it on [this website (Geojson.io)](http://geojson.io/#map=2/20.0/0.0) and check it shows a marker _somewhere_ in central London... "
984 | ]
985 | },
986 | {
987 | "cell_type": "markdown",
988 | "metadata": {},
989 | "source": [
990 | "\n",
991 | "### Further references:\n",
992 | "\n",
993 | "General list or resources\n",
994 | "- [Awesome list of resources](https://github.com/vinta/awesome-python)\n",
995 | "- [Python Docs](https://docs.python.org/2.7/tutorial/introduction.html)\n",
996 | "- [HitchHiker's guide to Python](http://docs.python-guide.org/en/latest/intro/learning/)\n",
997 | "- [Python for Informatics](http://www.pythonlearn.com/book_007.pdf)\n",
998 | "- [Learn Python the Hard Way - Lists](http://learnpythonthehardway.org/book/ex32.html)\n",
999 | "- [Learn Python the Hard Way - Dictionaries](http://learnpythonthehardway.org/book/ex39.html)\n",
1000 | "- [CodeAcademy](https://www.codecademy.com/courses/python-beginner-en-pwmb1/0/1)\n",
1001 | "\n"
1002 | ]
1003 | },
1004 | {
1005 | "cell_type": "markdown",
1006 | "metadata": {},
1007 | "source": [
1008 | "### Credits!\n",
1009 | "\n",
1010 | "#### Contributors:\n",
1011 | "The following individuals have contributed to these teaching materials: \n",
1012 | "- [James Millington](https://github.com/jamesdamillington)\n",
1013 | "- [Jon Reades](https://github.com/jreades)\n",
1014 | "- [Michele Ferretti](https://github.com/miccferr)\n",
1015 | "- [Zahratu Shabrina](https://github.com/zarashabrina)\n",
1016 | "\n",
1017 | "#### License\n",
1018 | "The content and structure of this teaching project itself is licensed under the [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 license](https://creativecommons.org/licenses/by-nc-sa/4.0/), and the contributing source code is licensed under [The MIT License](https://opensource.org/licenses/mit-license.php).\n",
1019 | "\n",
1020 | "#### Acknowledgements:\n",
1021 | "Supported by the [Royal Geographical Society](https://www.rgs.org/HomePage.htm) (with the Institute of British Geographers) with a Ray Y Gildea Jr Award.\n",
1022 | "\n",
1023 | "#### Potential Dependencies:\n",
1024 | "This notebook may depend on the following libraries: None"
1025 | ]
1026 | }
1027 | ],
1028 | "metadata": {
1029 | "anaconda-cloud": {},
1030 | "kernelspec": {
1031 | "display_name": "Python 3",
1032 | "language": "python",
1033 | "name": "python3"
1034 | },
1035 | "language_info": {
1036 | "codemirror_mode": {
1037 | "name": "ipython",
1038 | "version": 3
1039 | },
1040 | "file_extension": ".py",
1041 | "mimetype": "text/x-python",
1042 | "name": "python",
1043 | "nbconvert_exporter": "python",
1044 | "pygments_lexer": "ipython3",
1045 | "version": "3.8.5"
1046 | }
1047 | },
1048 | "nbformat": 4,
1049 | "nbformat_minor": 4
1050 | }
1051 |
--------------------------------------------------------------------------------
/notebook-10-recap.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Recap 2"
8 | ]
9 | },
10 | {
11 | "cell_type": "markdown",
12 | "metadata": {},
13 | "source": [
14 | "## Second Checkpoint\n",
15 | "\n",
16 | "Since the first recap, you've learned about lists, dictionaries and loops. Let's revise those concepts and how to use them in this notebook before continuing on to some new material. Answer the questions as best you can, working through any error messages you receive and remembering to refer back to previous notebooks. "
17 | ]
18 | },
19 | {
20 | "cell_type": "markdown",
21 | "metadata": {},
22 | "source": [
23 | "## Lists\n",
24 | "First, here's a reminder of some useful methods (_i.e._ functions) that apply to lists:\n",
25 | "\n",
26 | "| Method | Action |\n",
27 | "|------------------------|------------------------------------------------------------------|\n",
28 | "| list.count(`x`) | Return the number of times x appears in the list |\n",
29 | "| list.insert(`i`, `x`) | Insert value `x` at a given position `i` |\n",
30 | "| list.pop([`i`]) | Remove and return the value at position `i` (`i` is optional) |\n",
31 | "| list.remove(`x`) | Remove the first element from the list whose value is `x` |\n",
32 | "| list.reverse() | Reverse the elements of the list in place |\n",
33 | "| list.sort() | Sort the items of the list in place |\n",
34 | "| list.index(`x`) | Find the first occurence of `x` in the list |\n",
35 | "| list[x:y] | Subset the list from index `x` to `y-1` |\n"
36 | ]
37 | },
38 | {
39 | "cell_type": "markdown",
40 | "metadata": {},
41 | "source": [
42 | "### Interacting with Lists\n",
43 | "\n",
44 | "Replace `???` in the following code blocks to make the code work as instructed in the comments. All of the methods that you need are listed above, so this is about testing yourself on your understanding *both* of how to read the help *and* how to index elements in a list."
45 | ]
46 | },
47 | {
48 | "cell_type": "markdown",
49 | "metadata": {},
50 | "source": [
51 | "**a)** The next line creates a list of city names (each element is a string) - run the code and check you understand what it is doing. "
52 | ]
53 | },
54 | {
55 | "cell_type": "code",
56 | "execution_count": null,
57 | "metadata": {},
58 | "outputs": [],
59 | "source": [
60 | "cities = [\"Bristol\", \"London\", \"Manchester\", \"Edinburgh\", \"Belfast\", \"York\"]"
61 | ]
62 | },
63 | {
64 | "cell_type": "markdown",
65 | "metadata": {},
66 | "source": [
67 | "**b)** Replace the `???` so that it prints the position of Manchester in the list"
68 | ]
69 | },
70 | {
71 | "cell_type": "code",
72 | "execution_count": null,
73 | "metadata": {
74 | "solution2": "hidden",
75 | "solution2_first": true
76 | },
77 | "outputs": [],
78 | "source": [
79 | "print(\"The position of Manchester in the list is: \" + str(cities.???('Manchester')))"
80 | ]
81 | },
82 | {
83 | "cell_type": "code",
84 | "execution_count": null,
85 | "metadata": {
86 | "solution2": "hidden"
87 | },
88 | "outputs": [],
89 | "source": [
90 | "print(\"The position of Manchester in the list is: \" + str(cities.index('Manchester')))"
91 | ]
92 | },
93 | {
94 | "cell_type": "markdown",
95 | "metadata": {},
96 | "source": [
97 | "**c)** Replace the `???` so that it prints Belfast"
98 | ]
99 | },
100 | {
101 | "cell_type": "code",
102 | "execution_count": null,
103 | "metadata": {
104 | "solution2": "hidden",
105 | "solution2_first": true
106 | },
107 | "outputs": [],
108 | "source": [
109 | "print(cities[2 + ???])"
110 | ]
111 | },
112 | {
113 | "cell_type": "code",
114 | "execution_count": null,
115 | "metadata": {
116 | "solution2": "hidden"
117 | },
118 | "outputs": [],
119 | "source": [
120 | "print(cities[2 + 2])"
121 | ]
122 | },
123 | {
124 | "cell_type": "markdown",
125 | "metadata": {},
126 | "source": [
127 | "**d)** Use a **negative** index to print *Belfast*"
128 | ]
129 | },
130 | {
131 | "cell_type": "code",
132 | "execution_count": null,
133 | "metadata": {
134 | "solution2": "hidden",
135 | "solution2_first": true
136 | },
137 | "outputs": [],
138 | "source": [
139 | "print(cities[???])"
140 | ]
141 | },
142 | {
143 | "cell_type": "code",
144 | "execution_count": null,
145 | "metadata": {
146 | "solution2": "hidden"
147 | },
148 | "outputs": [],
149 | "source": [
150 | "print(cities[-2])"
151 | ]
152 | },
153 | {
154 | "cell_type": "markdown",
155 | "metadata": {},
156 | "source": [
157 | "**e)** Force Python to generate a `list index out of range` error. NB: This error happens you provide an index for which a list element does not exist"
158 | ]
159 | },
160 | {
161 | "cell_type": "code",
162 | "execution_count": null,
163 | "metadata": {
164 | "solution2": "hidden",
165 | "solution2_first": true
166 | },
167 | "outputs": [],
168 | "source": [
169 | "print(cities[???])"
170 | ]
171 | },
172 | {
173 | "cell_type": "code",
174 | "execution_count": null,
175 | "metadata": {
176 | "solution2": "hidden"
177 | },
178 | "outputs": [],
179 | "source": [
180 | "print(cities[6]) #anything above five would do it"
181 | ]
182 | },
183 | {
184 | "cell_type": "markdown",
185 | "metadata": {},
186 | "source": [
187 | "**f)** Think about what the next line creates, then run the code. "
188 | ]
189 | },
190 | {
191 | "cell_type": "code",
192 | "execution_count": null,
193 | "metadata": {},
194 | "outputs": [],
195 | "source": [
196 | "temperatures = [15.6, 16.5, 13.4, 14.0, 15.2, 14.8]"
197 | ]
198 | },
199 | {
200 | "cell_type": "markdown",
201 | "metadata": {},
202 | "source": [
203 | "**g)** What would you change `???` to, to return `[16.5, 13.4, 14.0]`?"
204 | ]
205 | },
206 | {
207 | "cell_type": "code",
208 | "execution_count": null,
209 | "metadata": {
210 | "solution2": "hidden",
211 | "solution2_first": true
212 | },
213 | "outputs": [],
214 | "source": [
215 | "print(temperatures[???])"
216 | ]
217 | },
218 | {
219 | "cell_type": "code",
220 | "execution_count": null,
221 | "metadata": {
222 | "solution2": "hidden"
223 | },
224 | "outputs": [],
225 | "source": [
226 | "print(temperatures[1:4])"
227 | ]
228 | },
229 | {
230 | "cell_type": "markdown",
231 | "metadata": {},
232 | "source": [
233 | "**h)** What are two different ways of getting `[15.2, 14.8]` from the `temperatures` list?"
234 | ]
235 | },
236 | {
237 | "cell_type": "code",
238 | "execution_count": null,
239 | "metadata": {
240 | "solution2": "hidden",
241 | "solution2_first": true
242 | },
243 | "outputs": [],
244 | "source": [
245 | "print(temperatures[???])\n",
246 | "print(temperatures[???])"
247 | ]
248 | },
249 | {
250 | "cell_type": "code",
251 | "execution_count": null,
252 | "metadata": {
253 | "solution2": "hidden"
254 | },
255 | "outputs": [],
256 | "source": [
257 | "print(temperatures[4:6])\n",
258 | "print(temperatures[-3:-1])"
259 | ]
260 | },
261 | {
262 | "cell_type": "markdown",
263 | "metadata": {},
264 | "source": [
265 | "**i)** Notice that the list of `temperatures` is the same length as the list of `cities`, that's because these are (roughly) average temperatures for each city! Given this, how do you print: \"The average temperature in Manchester is 13.4 degrees.\" _without_ doing any of the following:\n",
266 | "1. Using a list index directly (*i.e.* `cities[2]` and `temperatures[2]`) or \n",
267 | "2. Hard-coding the name of the city? \n",
268 | "\n",
269 | "**To put it another way, neither of these solutions is the answer**:\n",
270 | "```python\n",
271 | "print(\"The average temperature in Manchester is \" + str(temperatures[2]) + \" degrees.\")\n",
272 | "```\n",
273 | "or \n",
274 | "```python\n",
275 | "city=2\n",
276 | "print(\"The average temperature in \" + cities[city] + \" is \" + str(temperatures[city]) + \" degrees.\")\n",
277 | "```\n",
278 | "\n",
279 | "_Hint:_ you need to combine some of the ideas we've used above!"
280 | ]
281 | },
282 | {
283 | "cell_type": "code",
284 | "execution_count": null,
285 | "metadata": {
286 | "solution2": "hidden",
287 | "solution2_first": true
288 | },
289 | "outputs": [],
290 | "source": [
291 | "city=\"Manchester\" # Use this to start the solution...\n",
292 | "#your code here"
293 | ]
294 | },
295 | {
296 | "cell_type": "code",
297 | "execution_count": null,
298 | "metadata": {
299 | "solution2": "hidden"
300 | },
301 | "outputs": [],
302 | "source": [
303 | "city=\"Manchester\" # Use this to get the solution...\n",
304 | "index = cities.index(city)\n",
305 | "print(\"The average temperature in \" + cities[index] + \" is \" + str(temperatures[index]) + \" degrees.\")"
306 | ]
307 | },
308 | {
309 | "cell_type": "markdown",
310 | "metadata": {},
311 | "source": [
312 | "Now copy+paste your code and change only **one** thing in order to print out: \"The average temperature in Belfast is 15.2 degrees\""
313 | ]
314 | },
315 | {
316 | "cell_type": "code",
317 | "execution_count": null,
318 | "metadata": {
319 | "solution2": "hidden",
320 | "solution2_first": true
321 | },
322 | "outputs": [],
323 | "source": [
324 | "???"
325 | ]
326 | },
327 | {
328 | "cell_type": "code",
329 | "execution_count": null,
330 | "metadata": {
331 | "solution2": "hidden"
332 | },
333 | "outputs": [],
334 | "source": [
335 | "city=\"Belfast\"\n",
336 | "index = cities.index(city)\n",
337 | "print(\"The average temperature in \" + cities[index] + \" is \" + str(temperatures[index]) + \" degrees.\")"
338 | ]
339 | },
340 | {
341 | "cell_type": "markdown",
342 | "metadata": {},
343 | "source": [
344 | "### 1.2 Manipulating Multiple Lists"
345 | ]
346 | },
347 | {
348 | "cell_type": "markdown",
349 | "metadata": {},
350 | "source": [
351 | "We'll create two lists for the next set of questions"
352 | ]
353 | },
354 | {
355 | "cell_type": "code",
356 | "execution_count": null,
357 | "metadata": {},
358 | "outputs": [],
359 | "source": [
360 | "list1 = [1, 2, 3]\n",
361 | "list2 = [4, 5, 6]"
362 | ]
363 | },
364 | {
365 | "cell_type": "markdown",
366 | "metadata": {},
367 | "source": [
368 | "**j)** How do you get Python to print: `[1, 2, 3, 4, 5, 6]`"
369 | ]
370 | },
371 | {
372 | "cell_type": "code",
373 | "execution_count": null,
374 | "metadata": {
375 | "solution2": "hidden",
376 | "solution2_first": true
377 | },
378 | "outputs": [],
379 | "source": [
380 | "print( ??? )"
381 | ]
382 | },
383 | {
384 | "cell_type": "code",
385 | "execution_count": null,
386 | "metadata": {
387 | "solution2": "hidden"
388 | },
389 | "outputs": [],
390 | "source": [
391 | "print ( list1 + list2 )"
392 | ]
393 | },
394 | {
395 | "cell_type": "markdown",
396 | "metadata": {},
397 | "source": [
398 | "**k)** How to you get Python to print: `[1, 2, 3, [4, 5, 6]]`"
399 | ]
400 | },
401 | {
402 | "cell_type": "code",
403 | "execution_count": null,
404 | "metadata": {
405 | "solution2": "hidden",
406 | "solution2_first": true
407 | },
408 | "outputs": [],
409 | "source": [
410 | "print( ??? )"
411 | ]
412 | },
413 | {
414 | "cell_type": "code",
415 | "execution_count": null,
416 | "metadata": {
417 | "solution2": "hidden"
418 | },
419 | "outputs": [],
420 | "source": [
421 | "print( list1+[list2])"
422 | ]
423 | },
424 | {
425 | "cell_type": "markdown",
426 | "metadata": {},
427 | "source": [
428 | "Let's re-set the lists (run the next code block)"
429 | ]
430 | },
431 | {
432 | "cell_type": "code",
433 | "execution_count": null,
434 | "metadata": {},
435 | "outputs": [],
436 | "source": [
437 | "list1 = [1, 2, 3]\n",
438 | "list2 = [4, 5, 6]"
439 | ]
440 | },
441 | {
442 | "cell_type": "markdown",
443 | "metadata": {},
444 | "source": [
445 | "**l)** How would you print out: `[6, 5, 4, 3, 2, 1]` ?"
446 | ]
447 | },
448 | {
449 | "cell_type": "code",
450 | "execution_count": null,
451 | "metadata": {
452 | "solution2": "hidden",
453 | "solution2_first": true
454 | },
455 | "outputs": [],
456 | "source": [
457 | "list3 = ???\n",
458 | "list3.???\n",
459 | "print(list3)"
460 | ]
461 | },
462 | {
463 | "cell_type": "code",
464 | "execution_count": null,
465 | "metadata": {
466 | "solution2": "hidden"
467 | },
468 | "outputs": [],
469 | "source": [
470 | "list3 = list1+list2\n",
471 | "list3.reverse()\n",
472 | "print(list3)"
473 | ]
474 | },
475 | {
476 | "cell_type": "markdown",
477 | "metadata": {},
478 | "source": [
479 | "**m)** How would you print out: `[3, 2, 1, 6, 5, 4]` ?"
480 | ]
481 | },
482 | {
483 | "cell_type": "code",
484 | "execution_count": null,
485 | "metadata": {
486 | "solution2": "hidden",
487 | "solution2_first": true
488 | },
489 | "outputs": [],
490 | "source": [
491 | "list1.???\n",
492 | "list2.???\n",
493 | "print( list1+list2 )"
494 | ]
495 | },
496 | {
497 | "cell_type": "code",
498 | "execution_count": null,
499 | "metadata": {
500 | "solution2": "hidden"
501 | },
502 | "outputs": [],
503 | "source": [
504 | "list1.reverse()\n",
505 | "list2.reverse()\n",
506 | "print( list1+list2 )"
507 | ]
508 | },
509 | {
510 | "cell_type": "markdown",
511 | "metadata": {},
512 | "source": [
513 | "**n)** How would you print out `[3, 2, 6, 5]` with a permanent change to the list (not slicing)? NB: this follows on from the previous question, so note that the order is still 'reversed'."
514 | ]
515 | },
516 | {
517 | "cell_type": "code",
518 | "execution_count": null,
519 | "metadata": {
520 | "solution2": "hidden",
521 | "solution2_first": true
522 | },
523 | "outputs": [],
524 | "source": [
525 | "list1.???\n",
526 | "list2.???\n",
527 | "print( list1+list2 )"
528 | ]
529 | },
530 | {
531 | "cell_type": "code",
532 | "execution_count": null,
533 | "metadata": {
534 | "solution2": "hidden"
535 | },
536 | "outputs": [],
537 | "source": [
538 | "list1.remove(1)\n",
539 | "list2.remove(4)\n",
540 | "print( list1+list2 )"
541 | ]
542 | },
543 | {
544 | "cell_type": "markdown",
545 | "metadata": {},
546 | "source": [
547 | "## Dictionaries\n",
548 | "\n",
549 | "Remember that dictionaries (a.k.a. dicts) are like lists in that they are [data structures](https://docs.python.org/2/tutorial/datastructures.html) containing multiple elements. A key difference between [dictionaries](https://docs.python.org/2/tutorial/datastructures.html#dictionaries) and [lists](https://docs.python.org/2/tutorial/introduction.html#lists) is that while elements in lists are ordered, dicts are unordered. This means that whereas for lists we use integers as indexes to access elements, in dictonaries we use 'keys' (which can multiple different types; strings, integers, etc.). Consequently, an important concept for dicts is that of key-value pairs. "
550 | ]
551 | },
552 | {
553 | "cell_type": "markdown",
554 | "metadata": {},
555 | "source": [
556 | "### Creating an Atlas\n",
557 | "\n",
558 | "Replace `???` in the following code block to make the code work as instructed in the comments. If you need some hints and reminders, revisit [Code Camp Lesson 7](https://kingsgeocomputation.org/teaching/code-camp/code-camp-python/lessons/)."
559 | ]
560 | },
561 | {
562 | "cell_type": "markdown",
563 | "metadata": {},
564 | "source": [
565 | "Run the code and check you understand what the data structure that is being created (the data for each city are latitude, longitude and airport code)"
566 | ]
567 | },
568 | {
569 | "cell_type": "code",
570 | "execution_count": null,
571 | "metadata": {},
572 | "outputs": [],
573 | "source": [
574 | "cities = {\n",
575 | " 'San Francisco': [37.77, -122.43, 'SFO'],\n",
576 | " 'London': [51.51, -0.08, 'LDN'],\n",
577 | " 'Paris': [48.86,2.29, 'PAR'],\n",
578 | " 'Beijing': [39.92,116.40 ,'BEI'],\n",
579 | "}"
580 | ]
581 | },
582 | {
583 | "cell_type": "markdown",
584 | "metadata": {},
585 | "source": [
586 | "**a)** Add a record to the dictionary for Chennai ([data here](https://en.wikipedia.org/wiki/Chennai))"
587 | ]
588 | },
589 | {
590 | "cell_type": "code",
591 | "execution_count": null,
592 | "metadata": {
593 | "solution2": "hidden",
594 | "solution2_first": true
595 | },
596 | "outputs": [],
597 | "source": [
598 | "cities = ???"
599 | ]
600 | },
601 | {
602 | "cell_type": "code",
603 | "execution_count": null,
604 | "metadata": {
605 | "solution2": "hidden"
606 | },
607 | "outputs": [],
608 | "source": [
609 | "cities = {\n",
610 | " 'San Francisco': [37.77, -122.43, 'SFO'],\n",
611 | " 'London': [51.51, -0.08, 'LDN'],\n",
612 | " 'Paris': [48.86,2.29, 'PAR'],\n",
613 | " 'Beijing': [39.92,116.40 ,'BEI'],\n",
614 | " 'Chennai': [13.08, 80.27,'MAA']\n",
615 | "}"
616 | ]
617 | },
618 | {
619 | "cell_type": "markdown",
620 | "metadata": {},
621 | "source": [
622 | "**b)** In *one* line of code, print out the airport code for Chennai"
623 | ]
624 | },
625 | {
626 | "cell_type": "code",
627 | "execution_count": null,
628 | "metadata": {
629 | "solution2": "hidden",
630 | "solution2_first": true
631 | },
632 | "outputs": [],
633 | "source": [
634 | "print(???)"
635 | ]
636 | },
637 | {
638 | "cell_type": "code",
639 | "execution_count": null,
640 | "metadata": {
641 | "solution2": "hidden"
642 | },
643 | "outputs": [],
644 | "source": [
645 | "print(\"The airport code for Chennai is \" + cities[\"Chennai\"][2])"
646 | ]
647 | },
648 | {
649 | "cell_type": "markdown",
650 | "metadata": {},
651 | "source": [
652 | "**c)** Check you understand the difference between the following two blocks of code by running them, checking the output and editing them (e.g. try the code again, but replacing *Berlin* with *London*)"
653 | ]
654 | },
655 | {
656 | "cell_type": "code",
657 | "execution_count": null,
658 | "metadata": {
659 | "scrolled": true
660 | },
661 | "outputs": [],
662 | "source": [
663 | "print(cities['Berlin'])"
664 | ]
665 | },
666 | {
667 | "cell_type": "code",
668 | "execution_count": null,
669 | "metadata": {
670 | "solution2": "hidden",
671 | "solution2_first": true
672 | },
673 | "outputs": [],
674 | "source": [
675 | "print(cities.get('Berlin'))"
676 | ]
677 | },
678 | {
679 | "cell_type": "code",
680 | "execution_count": null,
681 | "metadata": {
682 | "solution2": "hidden"
683 | },
684 | "outputs": [],
685 | "source": [
686 | "#Berlin is not in the dict. \n",
687 | "#The first code block above returns an error because Berlin is missing\n",
688 | "#The second does not because it uses the .get method which handles the error for us (returning a None value)\n",
689 | "#This second method is 'safer' because of how it handles this error\n",
690 | "\n",
691 | "#compare to use using the two different methods for London\n",
692 | "print(cities['London'])\n",
693 | "print(cities.get('London'))"
694 | ]
695 | },
696 | {
697 | "cell_type": "code",
698 | "execution_count": null,
699 | "metadata": {
700 | "solution2": "hidden"
701 | },
702 | "outputs": [],
703 | "source": [
704 | "print(cities.get('London'))"
705 | ]
706 | },
707 | {
708 | "cell_type": "markdown",
709 | "metadata": {},
710 | "source": [
711 | "**d)** Adapting the code below, print out the city name and airport code for every city in our Atlas."
712 | ]
713 | },
714 | {
715 | "cell_type": "code",
716 | "execution_count": null,
717 | "metadata": {
718 | "solution2": "hidden",
719 | "solution2_first": true
720 | },
721 | "outputs": [],
722 | "source": [
723 | "for k, v in cities.items():\n",
724 | " print(k)"
725 | ]
726 | },
727 | {
728 | "cell_type": "code",
729 | "execution_count": null,
730 | "metadata": {
731 | "solution2": "hidden"
732 | },
733 | "outputs": [],
734 | "source": [
735 | "for k, v in cities.items():\n",
736 | " print(\"The city of \" + str(k) + \" has an airport code of \" + str(v[2]) )"
737 | ]
738 | },
739 | {
740 | "cell_type": "markdown",
741 | "metadata": {},
742 | "source": [
743 | "## Loops\n",
744 | "\n",
745 | "Recall from the previous notebook that loops are a way to *iterate* (or repeat) chunks of code. The two most common ways to iterate a set of commands are the `while` loop and the `for` loop. "
746 | ]
747 | },
748 | {
749 | "cell_type": "markdown",
750 | "metadata": {},
751 | "source": [
752 | "### Working with Loops\n",
753 | "The questions below use `for` loops. Replace `???` in the following code block to make the code work as instructed in the comments. If you need some hints and reminders, revisit the previous notebook."
754 | ]
755 | },
756 | {
757 | "cell_type": "markdown",
758 | "metadata": {},
759 | "source": [
760 | "**a)** Print out the name and latitude of every city in the cities dictionary using a for loop"
761 | ]
762 | },
763 | {
764 | "cell_type": "code",
765 | "execution_count": null,
766 | "metadata": {
767 | "solution2": "hidden",
768 | "solution2_first": true
769 | },
770 | "outputs": [],
771 | "source": [
772 | "for ??? in cities.???:\n",
773 | " print(??? + \" is at latitude \" + str(???))"
774 | ]
775 | },
776 | {
777 | "cell_type": "code",
778 | "execution_count": null,
779 | "metadata": {
780 | "solution2": "hidden"
781 | },
782 | "outputs": [],
783 | "source": [
784 | "for city, latitude in cities.items():\n",
785 | " print(city + \" is at latitude \" + str(latitude[0]))"
786 | ]
787 | },
788 | {
789 | "cell_type": "markdown",
790 | "metadata": {},
791 | "source": [
792 | "**b)** Print out every city on a separate line using a for loop:"
793 | ]
794 | },
795 | {
796 | "cell_type": "code",
797 | "execution_count": null,
798 | "metadata": {
799 | "solution2": "hidden",
800 | "solution2_first": true
801 | },
802 | "outputs": [],
803 | "source": [
804 | "for c in ???:\n",
805 | " print(???)"
806 | ]
807 | },
808 | {
809 | "cell_type": "code",
810 | "execution_count": null,
811 | "metadata": {
812 | "solution2": "hidden"
813 | },
814 | "outputs": [],
815 | "source": [
816 | "for c in cities.items():\n",
817 | " print(c)"
818 | ]
819 | },
820 | {
821 | "cell_type": "markdown",
822 | "metadata": {},
823 | "source": [
824 | "**c)** Now print using a loop this new data structure:"
825 | ]
826 | },
827 | {
828 | "cell_type": "code",
829 | "execution_count": null,
830 | "metadata": {
831 | "solution2": "hidden",
832 | "solution2_first": true
833 | },
834 | "outputs": [],
835 | "source": [
836 | "citiesB = [\n",
837 | " {'name': 'San Francisco',\n",
838 | " 'position': [37.77, -122.43],\n",
839 | " 'airport': 'SFO'},\n",
840 | " {'name': 'London',\n",
841 | " 'position': [51.51, -0.08],\n",
842 | " 'airport': 'LDN'},\n",
843 | " {'name': 'Paris',\n",
844 | " 'position': [48.86, 2.29],\n",
845 | " 'airport': 'PAR'},\n",
846 | " {'name': 'Beijing',\n",
847 | " 'position': [39.92, 116.40],\n",
848 | " 'airport': 'BEI'}\n",
849 | "]\n",
850 | "\n",
851 | "for ??? in citiesB.???:\n",
852 | " print(??? + \" is at latitude \" + str(???))"
853 | ]
854 | },
855 | {
856 | "cell_type": "code",
857 | "execution_count": null,
858 | "metadata": {
859 | "solution2": "hidden"
860 | },
861 | "outputs": [],
862 | "source": [
863 | "citiesB = [\n",
864 | " {'name': 'San Francisco',\n",
865 | " 'position': [37.77, -122.43],\n",
866 | " 'airport': 'SFO'},\n",
867 | " {'name': 'London',\n",
868 | " 'position': [51.51, -0.08],\n",
869 | " 'airport': 'LDN'},\n",
870 | " {'name': 'Paris',\n",
871 | " 'position': [48.86, 2.29],\n",
872 | " 'airport': 'PAR'},\n",
873 | " {'name': 'Beijing',\n",
874 | " 'position': [39.92, 116.40],\n",
875 | " 'airport': 'BEI'}\n",
876 | "]\n",
877 | "\n",
878 | "for city in citiesB:\n",
879 | " print(city['name'] + \" is at latitude \" + str(city['position'][0]))"
880 | ]
881 | },
882 | {
883 | "cell_type": "markdown",
884 | "metadata": {},
885 | "source": [
886 | "Nice work. Hopefully these questions have helped you compound your understanding. Onwards!"
887 | ]
888 | },
889 | {
890 | "cell_type": "markdown",
891 | "metadata": {},
892 | "source": [
893 | "### Credits!\n",
894 | "\n",
895 | "#### Contributors:\n",
896 | "The following individuals have contributed to these teaching materials: \n",
897 | "- [James Millington](https://github.com/jamesdamillington)\n",
898 | "- [Jon Reades](https://github.com/jreades)\n",
899 | "- [Michele Ferretti](https://github.com/miccferr)\n",
900 | "- [Zahratu Shabrina](https://github.com/zarashabrina)\n",
901 | "\n",
902 | "#### License\n",
903 | "The content and structure of this teaching project itself is licensed under the [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 license](https://creativecommons.org/licenses/by-nc-sa/4.0/), and the contributing source code is licensed under [The MIT License](https://opensource.org/licenses/mit-license.php).\n",
904 | "\n",
905 | "#### Acknowledgements:\n",
906 | "Supported by the [Royal Geographical Society](https://www.rgs.org/HomePage.htm) (with the Institute of British Geographers) with a Ray Y Gildea Jr Award.\n",
907 | "\n",
908 | "#### Potential Dependencies:\n",
909 | "This notebook may depend on the following libraries: None"
910 | ]
911 | }
912 | ],
913 | "metadata": {
914 | "anaconda-cloud": {},
915 | "kernelspec": {
916 | "display_name": "Python 3",
917 | "language": "python",
918 | "name": "python3"
919 | },
920 | "language_info": {
921 | "codemirror_mode": {
922 | "name": "ipython",
923 | "version": 3
924 | },
925 | "file_extension": ".py",
926 | "mimetype": "text/x-python",
927 | "name": "python",
928 | "nbconvert_exporter": "python",
929 | "pygments_lexer": "ipython3",
930 | "version": "3.7.6"
931 | }
932 | },
933 | "nbformat": 4,
934 | "nbformat_minor": 4
935 | }
936 |
--------------------------------------------------------------------------------
/notebook-11-functions.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Notebook-11: Introduction to Functions"
8 | ]
9 | },
10 | {
11 | "cell_type": "markdown",
12 | "metadata": {},
13 | "source": [
14 | "### Lesson Content \n",
15 | "\n",
16 | "- Function Anatomy 101\n",
17 | " - Function definiton & call\n",
18 | " - Arguments\n",
19 | " - Return statement\n",
20 | "- Function calling!\n",
21 | " - Assign a function to a variable\n",
22 | " - Function as a parameter to another function"
23 | ]
24 | },
25 | {
26 | "cell_type": "markdown",
27 | "metadata": {},
28 | "source": [
29 | "In this lesson we'll cover *functions* in Python, a concept that you've already encountered but to which you've not yet been formally introduced. Now we're going to dig into this a little bit more because writing functions is where lazy programmers become good programmers.\n",
30 | "\n",
31 | "In other words, as we saw with the concept of *iteration*, programmers are lazy and they tend want to avoid doing boring tasks over and over again. The idea is to avoid \"wasting time re-inventing the wheel\" and programmers have abbreviated this idea to the acronym **D.R.Y.** (Do not Repeat Yourself): if you are doing something more than once or twice, ask yourself if there's a way to encapsulate what you are doing in a function: you write the function _once_, and then _call_ it whenever you need to complete that task."
32 | ]
33 | },
34 | {
35 | "cell_type": "markdown",
36 | "metadata": {},
37 | "source": [
38 | ""
39 | ]
40 | },
41 | {
42 | "cell_type": "markdown",
43 | "metadata": {},
44 | "source": [
45 | "Naturally, **D.R.Y.** has its opposite: **W.E.T.** (We Enjoy Typing or Write Everything Twice). Dry is nearly always better than wet.\n",
46 | "\n",
47 | "Encapsulating regularly-used bits of code in functions has several advantages:\n",
48 | "* Your code is more readable: because you only have to write a function once and can then re-use it as many times as you like, your files are shorter.\n",
49 | "* Your code is easier to maintain: because you only have to write a function once, if you find a mistake in your code, you also only have to fix it in one place.\n",
50 | "* Your can code more quickly: things that you do a _lot_ can even be stuck in a separate file that you _import_ into your code so that your most-used functions are immediately available.\n",
51 | "\n",
52 | "Basically, a function is a way to _do_ something _to_ something in a portable, easy-to-use little bundle of code."
53 | ]
54 | },
55 | {
56 | "cell_type": "markdown",
57 | "metadata": {},
58 | "source": [
59 | "## Functions 101\n",
60 | "\n",
61 | "We've already met and used some functions, especially when we dealt with lists and dictionaries:"
62 | ]
63 | },
64 | {
65 | "cell_type": "code",
66 | "execution_count": null,
67 | "metadata": {},
68 | "outputs": [],
69 | "source": [
70 | "myList = [1,\"two\", False, 9.99]\n",
71 | "len(myList) # A function"
72 | ]
73 | },
74 | {
75 | "cell_type": "code",
76 | "execution_count": null,
77 | "metadata": {},
78 | "outputs": [],
79 | "source": [
80 | "print(myList) # A different function!"
81 | ]
82 | },
83 | {
84 | "cell_type": "markdown",
85 | "metadata": {},
86 | "source": [
87 | "## Layout of a Function\n",
88 | "\n",
89 | "As we briefly mentioned in another notebook, any 'word' followed by a set of parenthesis is a function. The 'word' is the function's *name*, and anything that you write within the parantheses are the function's inputs (also known as *parameters*). Like so:\n",
90 | "\n",
91 | "```python\n",
92 | "\n",
93 | "function_name(optional_parameter_1, optional_parameter_2, ...)\n",
94 | "\n",
95 | "```\n",
96 | "\n",
97 | "So how do we create (*instantiate* in programming terms) a new function? Like everything else in Python, functions have specific rules that you have to follow for the computer to understand what you want it to do. In this case there are two separate steps: the *function definition* and the *function call*."
98 | ]
99 | },
100 | {
101 | "cell_type": "markdown",
102 | "metadata": {},
103 | "source": [
104 | "### Function Definition\n",
105 | "\n",
106 | "This is a function definition:\n",
107 | "```python\n",
108 | "def myFirstFunc():\n",
109 | " print(\"Nice to meet you!\")\n",
110 | "```\n",
111 | "Let's see what happened there:\n",
112 | "- We indicated that we wanted to _define_ (lazy version: `def`) a new function.\n",
113 | "- Right after `def` we gave the function a name: `myFirstFunc`.\n",
114 | "- After the new function's name there's the set of parenthesis and a colon.\n",
115 | "- The line(s) of the function are indented (just like a loop).\n",
116 | "\n",
117 | "The reason for the indenting is the same as for a `while` loop or an `if` condition! It indicates to the Python interpreter that whatever is indented *belongs* to the function. Is like saying: \"Look man, I'm going to define this `myFirstFunc` function, and whatever is indented afterwards is part of the function\". That is what we call the *function's body*, and it's the full package of instructions that we want the computer to run every time we *call* the function. "
118 | ]
119 | },
120 | {
121 | "cell_type": "markdown",
122 | "metadata": {},
123 | "source": [
124 | "### Function Call\n",
125 | "\n",
126 | "Cool, now that we have defined a function how do we use it? \n",
127 | "\n",
128 | "The same that we do with 'built-in' functions like `print` and `len`; we *call* it by just typing:\n",
129 | "```python\n",
130 | "myFirstFunct()\n",
131 | "```\n",
132 | "Try it yourself in the code cell below!"
133 | ]
134 | },
135 | {
136 | "cell_type": "code",
137 | "execution_count": null,
138 | "metadata": {},
139 | "outputs": [],
140 | "source": [
141 | "# the function definition\n",
142 | "def myFirstFunc():\n",
143 | " print(\"Nice to meet you!\")\n",
144 | "\n",
145 | "# the function call\n",
146 | "myFirstFunc()"
147 | ]
148 | },
149 | {
150 | "cell_type": "markdown",
151 | "metadata": {},
152 | "source": [
153 | "Notice that the sequence of function **definiton** (`def`) *and then* function **call** (`function_name()`) is important! Think about it: how would Python know what we are referring to (i.e. what is the `myFirstFunc` it has to call?), if we haven't yet specified it?\n",
154 | "\n",
155 | "It's the same as with variables: try to `print` one before you've defined it and Python will complain!"
156 | ]
157 | },
158 | {
159 | "cell_type": "code",
160 | "execution_count": null,
161 | "metadata": {},
162 | "outputs": [],
163 | "source": [
164 | "print(myVariable)\n",
165 | "myVariable = \"Hallo Hallo!\""
166 | ]
167 | },
168 | {
169 | "cell_type": "markdown",
170 | "metadata": {},
171 | "source": [
172 | "Reading (out loud!) the error message hopefully makes the error obvious... Quite explicit, isn't it? :)"
173 | ]
174 | },
175 | {
176 | "cell_type": "code",
177 | "execution_count": null,
178 | "metadata": {},
179 | "outputs": [],
180 | "source": [
181 | "myVariable = \"Hallo Hallo!\"\n",
182 | "print(myVariable)"
183 | ]
184 | },
185 | {
186 | "cell_type": "markdown",
187 | "metadata": {},
188 | "source": [
189 | "#### A challenge for you!\n",
190 | "\n",
191 | "Define a new function called \"sunnyDay\" that prints the string \"What a lovely day!\" "
192 | ]
193 | },
194 | {
195 | "cell_type": "code",
196 | "execution_count": null,
197 | "metadata": {
198 | "solution2": "hidden",
199 | "solution2_first": true
200 | },
201 | "outputs": [],
202 | "source": [
203 | "#your code here"
204 | ]
205 | },
206 | {
207 | "cell_type": "code",
208 | "execution_count": 1,
209 | "metadata": {
210 | "solution2": "hidden"
211 | },
212 | "outputs": [],
213 | "source": [
214 | "def sunnyDay():\n",
215 | " print(\"What a lovely day!\")"
216 | ]
217 | },
218 | {
219 | "cell_type": "markdown",
220 | "metadata": {},
221 | "source": [
222 | "Now define a function named \"gloomyDay\" that prints \"I hate rainy days!\" "
223 | ]
224 | },
225 | {
226 | "cell_type": "code",
227 | "execution_count": null,
228 | "metadata": {},
229 | "outputs": [],
230 | "source": [
231 | "#your code here"
232 | ]
233 | },
234 | {
235 | "cell_type": "markdown",
236 | "metadata": {},
237 | "source": [
238 | "Finally, call the two functions you have defined so that \"I hate rainy days!\" is printed before \"What a lovely day!\""
239 | ]
240 | },
241 | {
242 | "cell_type": "code",
243 | "execution_count": null,
244 | "metadata": {
245 | "solution2": "hidden",
246 | "solution2_first": true
247 | },
248 | "outputs": [],
249 | "source": [
250 | "#your code here"
251 | ]
252 | },
253 | {
254 | "cell_type": "code",
255 | "execution_count": null,
256 | "metadata": {
257 | "solution2": "hidden"
258 | },
259 | "outputs": [],
260 | "source": [
261 | "gloomyDay()\n",
262 | "sunnyDay()"
263 | ]
264 | },
265 | {
266 | "cell_type": "markdown",
267 | "metadata": {},
268 | "source": [
269 | "## Arguments\n",
270 | "\n",
271 | "Those are pretty basic functions, and as you might have noticed they all kind of do the same thing but are no shorter than the thing they replaced (a single print command). You will definetely need them though whenever you are using a function to process some *input* and *return* some *output*. In that case the paramters are inputs that you are *passing* to the function.\n",
272 | "\n",
273 | "```python\n",
274 | "def myFunction( input_parameter ):\n",
275 | "# do something to the input\n",
276 | " return input_parameter\n",
277 | "```"
278 | ]
279 | },
280 | {
281 | "cell_type": "code",
282 | "execution_count": null,
283 | "metadata": {},
284 | "outputs": [],
285 | "source": [
286 | "def printMyName( name ):\n",
287 | " print(\"Hi! My name is: \" + name)\n",
288 | "\n",
289 | "printMyName(\"Gerardus\")"
290 | ]
291 | },
292 | {
293 | "cell_type": "markdown",
294 | "metadata": {},
295 | "source": [
296 | "#### A challenge for you!\n",
297 | "\n",
298 | "We've already defined `printMyName`, so you don't need to do that again. Just ask the function to print _your_ name!"
299 | ]
300 | },
301 | {
302 | "cell_type": "code",
303 | "execution_count": null,
304 | "metadata": {
305 | "solution2": "hidden",
306 | "solution2_first": true
307 | },
308 | "outputs": [],
309 | "source": [
310 | "#your code here"
311 | ]
312 | },
313 | {
314 | "cell_type": "code",
315 | "execution_count": null,
316 | "metadata": {
317 | "solution2": "hidden"
318 | },
319 | "outputs": [],
320 | "source": [
321 | "printMyName(\"James\")"
322 | ]
323 | },
324 | {
325 | "cell_type": "markdown",
326 | "metadata": {},
327 | "source": [
328 | "A *little* more useful, right? If we had to print out name badges for a big conference, rather than typing \"Hi! My name is ...\" hundreds of times, if we had a list of people's names, we could just use a `for` loop to print out each one in turn using this function. The function adds the part that *is the same* for every name badge and all we need to do is pass it the input parameters. In fact, why don't we try that now?"
329 | ]
330 | },
331 | {
332 | "cell_type": "code",
333 | "execution_count": null,
334 | "metadata": {},
335 | "outputs": [],
336 | "source": [
337 | "for name in [\"Jon Reades\", \"James Millington\", \"Chen Zhong\", \"Naru Shiode\"]:\n",
338 | " printMyName(name)"
339 | ]
340 | },
341 | {
342 | "cell_type": "markdown",
343 | "metadata": {},
344 | "source": [
345 | "In the function `printMyName` we used just one parameter as an input, but we are not constrained to just one. We can input many parameters *separated by commas*; let's _redefine_ the `printMyName` function:"
346 | ]
347 | },
348 | {
349 | "cell_type": "code",
350 | "execution_count": null,
351 | "metadata": {},
352 | "outputs": [],
353 | "source": [
354 | "def printMyName(name, surname):\n",
355 | " print(\"Hi! My name is \"+ name + \" \" + surname)\n",
356 | "\n",
357 | "printMyName(\"Gerardus\", \"Merkatoor\")"
358 | ]
359 | },
360 | {
361 | "cell_type": "markdown",
362 | "metadata": {},
363 | "source": [
364 | "And now can pass input parameters to a function dynamically from a data structure within a loop: "
365 | ]
366 | },
367 | {
368 | "cell_type": "code",
369 | "execution_count": null,
370 | "metadata": {},
371 | "outputs": [],
372 | "source": [
373 | "britishProgrammers = [\n",
374 | " [\"Babbage\", \"Charles\"],\n",
375 | " [\"Lovelace\", \"Ada\"], \n",
376 | " [\"Turing\", \"Alan\"],\n",
377 | "]\n",
378 | "\n",
379 | "for p in britishProgrammers:\n",
380 | " printMyName(p[1], p[0])"
381 | ]
382 | },
383 | {
384 | "cell_type": "markdown",
385 | "metadata": {},
386 | "source": [
387 | "Neat right? We've simplified things to that we can focus only on what's important: we have our 'data structure' (the list-of-lists) and we have our printing function (`printMyName`). And now we just use a `for` loop to do the hard work. If we had 1,000 british programmers to print out it would be the *same* level of effort.\n",
388 | "\n",
389 | "See what we mean about it being like Lego? We've combined a new concept with a concept covered in the last notebook to simplify the process of printing out nametags."
390 | ]
391 | },
392 | {
393 | "cell_type": "markdown",
394 | "metadata": {},
395 | "source": [
396 | "#### A challenge for you!\n",
397 | "\n",
398 | "Define and use a function that takes as input parameters a `` (String) and `` (Integer) and then prints out the phrase: ` + \"is\" + +\" years old\"`"
399 | ]
400 | },
401 | {
402 | "cell_type": "code",
403 | "execution_count": null,
404 | "metadata": {
405 | "solution2": "hidden",
406 | "solution2_first": true
407 | },
408 | "outputs": [],
409 | "source": [
410 | "#your code here"
411 | ]
412 | },
413 | {
414 | "cell_type": "code",
415 | "execution_count": null,
416 | "metadata": {
417 | "solution2": "hidden"
418 | },
419 | "outputs": [],
420 | "source": [
421 | "def printMyAge(name, age):\n",
422 | " print(name + \" is \" + str(age) + \" years old.\")\n",
423 | " \n",
424 | "printMyAge('Jon',25)"
425 | ]
426 | },
427 | {
428 | "cell_type": "markdown",
429 | "metadata": {
430 | "solution2": "hidden",
431 | "solution2_first": true
432 | },
433 | "source": [
434 | "There's actually another way to do this that is quite helpful because it's easier to read:"
435 | ]
436 | },
437 | {
438 | "cell_type": "code",
439 | "execution_count": null,
440 | "metadata": {
441 | "solution2": "hidden"
442 | },
443 | "outputs": [],
444 | "source": [
445 | "def printMyAge(name, age):\n",
446 | " print(f\"{name} is {age} years old.\") # This is called a 'f-string' and we use {...} to add variables\n",
447 | " \n",
448 | "printMyAge('Jon',25)"
449 | ]
450 | },
451 | {
452 | "cell_type": "markdown",
453 | "metadata": {},
454 | "source": [
455 | "## Scoping\n",
456 | "\n",
457 | "Now I'd like you to focus on a particuarly important concept: something called 'scoping'. Notice that the names we are using for the parameters are *de facto* creating new variables that we then use in the *function body* (the indented block of code). In the example below, 'name' and 'surname' are _scoped_ to the body of the funciton. Outside of that block (outside of that scope) they don't exit!\n",
458 | "\n",
459 | "Here's the proof:"
460 | ]
461 | },
462 | {
463 | "cell_type": "code",
464 | "execution_count": null,
465 | "metadata": {},
466 | "outputs": [],
467 | "source": [
468 | "def whoAmI(myname, mysurname):\n",
469 | " if not myname:\n",
470 | " myname = 'Charles'\n",
471 | " if not mysurname:\n",
472 | " mysurname = 'Babbage'\n",
473 | " print(\"Hi! My name is \"+ myname + \" \" + mysurname + \"!\")\n",
474 | "\n",
475 | "print(myname) # myname _only_ exists 'inside' the function definition"
476 | ]
477 | },
478 | {
479 | "cell_type": "markdown",
480 | "metadata": {},
481 | "source": [
482 | "Notice how the ErrorMessage is the same as before when we tried to `print` a variable that wasn't defined yet? It's the same concept: the variables defined as parameters exist only in the indented code block of the function (the [function *scope*](http://python-textbok.readthedocs.io/en/latest/Variables_and_Scope.html) ).\n",
483 | "\n",
484 | "But notice too that if you replace `print name` with `whoAmI(\"Ada\", \"Lovelace\")` then the error disappears and you will see the output: \"Hi! My name is Ada Lovelace.\" So to reiterate: parameters to a function exist as variables **only within the function scope**."
485 | ]
486 | },
487 | {
488 | "cell_type": "code",
489 | "execution_count": null,
490 | "metadata": {},
491 | "outputs": [],
492 | "source": [
493 | "whoAmI('Ada','Lovelace')"
494 | ]
495 | },
496 | {
497 | "cell_type": "markdown",
498 | "metadata": {},
499 | "source": [
500 | "## Default Parameters\n",
501 | "\n",
502 | "Let's say that your namebade printing function is a worldwide hit, and while most conferences take place in English, in some cases they might need to say 'Hello' in a different languages. In this case, we might like to have a parameter with a _default_ value (\"Hi\") but allow the programmer to override that with a _different_ value (e.g. \"Bonjour\").\n",
503 | "\n",
504 | "Here's how that works:"
505 | ]
506 | },
507 | {
508 | "cell_type": "code",
509 | "execution_count": null,
510 | "metadata": {},
511 | "outputs": [],
512 | "source": [
513 | "def printInternational(name, surname, greeting=\"Hi\"):\n",
514 | " print(greeting + \"! My name is \"+ name + \" \" + surname)\n",
515 | "\n",
516 | "printInternational(\"Ada\", \"Lovelace\")\n",
517 | "printInternational(\"Charles\", \"Babbage\")\n",
518 | "printInternational(\"Laurent\", \"Ribardière\", \"Bonjour\")\n",
519 | "printInternational(\"François\", \"Lionet\", \"Bonjour\")\n",
520 | "printInternational(\"Alan\", \"Turing\")\n",
521 | "printInternational(\"Harsha\",\"Suryanarayana\", \"Namaste\")"
522 | ]
523 | },
524 | {
525 | "cell_type": "markdown",
526 | "metadata": {},
527 | "source": [
528 | "So we only have to provide a value for a parameter with a default setting if we want to change it for some reason.\n",
529 | "\n",
530 | "## Return statement\n",
531 | "\n",
532 | "Up to here we've only had a function that printed out whatever we told it to. Of course, that's pretty limited and there are a lot of cases where we would want the function to do something and then come back to us with an answer! And remember that the problem of variable scoping means that variables declared inside a function aren't visible to the rest of the program.\n",
533 | "\n",
534 | "So if you want to access a value calculated inside a function then you have to explicitely return it using the *reserved keyword* `return`:"
535 | ]
536 | },
537 | {
538 | "cell_type": "code",
539 | "execution_count": null,
540 | "metadata": {},
541 | "outputs": [],
542 | "source": [
543 | "def sumOf(firstQuantity, secondQuantity):\n",
544 | " return firstQuantity + secondQuantity\n",
545 | "\n",
546 | "print(sumOf(1,2))\n",
547 | "print(sumOf(109845309234.30945098345,223098450985698054902309342.43598723900923489))"
548 | ]
549 | },
550 | {
551 | "cell_type": "markdown",
552 | "metadata": {},
553 | "source": [
554 | "### Assigning to a Variable\n",
555 | "\n",
556 | "The `return` keyword, somewhat obviously, returns whatever you tell it to so that that 'thing' become accessible outside of the function's scope. You can do whatever you want with the returned value, like assign it to a new variable:"
557 | ]
558 | },
559 | {
560 | "cell_type": "code",
561 | "execution_count": null,
562 | "metadata": {},
563 | "outputs": [],
564 | "source": [
565 | "returnedValue = sumOf(4, 3)\n",
566 | "\n",
567 | "# Notice the casting from int to str!\n",
568 | "print(f\"This is the returned value: {returnedValue}\")"
569 | ]
570 | },
571 | {
572 | "cell_type": "markdown",
573 | "metadata": {},
574 | "source": [
575 | "One important thing to remember is that `return` always marks the end of the list of instructions in a function. So whatever code is written below `return` *and yet still indented in the function scope* won't be executed:\n",
576 | "\n",
577 | "```python\n",
578 | "def genericFunc(parameter):\n",
579 | " # do something to parameter\n",
580 | " # ...\n",
581 | " # do something else..\n",
582 | " # ...\n",
583 | " return \n",
584 | " print(\"this line won't be ever executed! how sad!\")\n",
585 | " print(\"nope. this won't either, sorry.\")\n",
586 | "```\n",
587 | " "
588 | ]
589 | },
590 | {
591 | "cell_type": "markdown",
592 | "metadata": {},
593 | "source": [
594 | "#### A challenge for you!\n",
595 | "\n",
596 | "Guess which will be the highest number to be printed from this function (**think about your guess _before_ you execute the code)**:"
597 | ]
598 | },
599 | {
600 | "cell_type": "code",
601 | "execution_count": null,
602 | "metadata": {
603 | "solution2": "hidden",
604 | "solution2_first": true
605 | },
606 | "outputs": [],
607 | "source": [
608 | "def printNumbers():\n",
609 | " print(2)\n",
610 | " print(5)\n",
611 | " return\n",
612 | " print(9999)\n",
613 | " print(800000)\n",
614 | "\n",
615 | "printNumbers()"
616 | ]
617 | },
618 | {
619 | "cell_type": "markdown",
620 | "metadata": {
621 | "solution2": "hidden"
622 | },
623 | "source": [
624 | "5 is the last value printed becayse a `return` statement ends the execution of the function, regardless of whether a result (i.e. a value following the `return` keyword _on the same line_ ) to the caller."
625 | ]
626 | },
627 | {
628 | "cell_type": "markdown",
629 | "metadata": {},
630 | "source": [
631 | "Now that you have seen a bit more what is happening in a function, we can combine some concepts that we have seen in previous notebooks to produce interesting bits of code. Take a look at how I've combined the `range` function, and the `for in` loop to print only the odd numbers for a given range."
632 | ]
633 | },
634 | {
635 | "cell_type": "code",
636 | "execution_count": null,
637 | "metadata": {},
638 | "outputs": [],
639 | "source": [
640 | "def oddNumbers(inputRange):\n",
641 | " \"\"\"\n",
642 | " A function that prints only the odd numbers for a given range from 0 to inputRange.\n",
643 | " inputRange - an integer representing the maximum of the range\n",
644 | " \"\"\"\n",
645 | " for i in range(inputRange):\n",
646 | " if i%2 != 0:\n",
647 | " print(i)\n",
648 | "\n",
649 | "oddNumbers(10)\n",
650 | "\n",
651 | "print(\"And...\")\n",
652 | "\n",
653 | "oddNumbers(15)\n",
654 | "\n",
655 | "help(oddNumbers)"
656 | ]
657 | },
658 | {
659 | "cell_type": "markdown",
660 | "metadata": {},
661 | "source": [
662 | "Let's take a closer look at what's happening above...\n",
663 | "```python\n",
664 | "def oddNumbers(inputRange):\n",
665 | " \"\"\"\n",
666 | " A function that prints only the odd numbers for a given range from 0 to inputRange.\n",
667 | " inputRange - an integer representing the maximum of the range\n",
668 | " \"\"\"\n",
669 | " for i in range(inputRange):\n",
670 | " if i%2 != 0:\n",
671 | " print(i)\n",
672 | "```\n",
673 | "This defines a new function called `oddNumbers` which takes one parameter – it's not immediately clear what type of variable `inputRange` is, but we can guess it pretty quickly from what happens next.\n",
674 | "\n",
675 | "You'll notice that there's are some lines immediately _after_ the function definition (between the triple-quotes) that aren't printed or obviously used, but that look like documentation of some sort. We'll come back to that in a minute.\n",
676 | "\n",
677 | "The next line is a simple `for` loop: `for i in range(inputRange)`. The `range` function [generates](https://www.pythoncentral.io/pythons-range-function-explained/) a list of numbers from 0 to the input parameter passed to it. So we are going to be running a loop from 0 to *n* (where *n*=`inputRange`) and assigning the result of that to `i`.\n",
678 | "\n",
679 | "The next line is nested inside the for loop: so we take _each_ `i` in turn and perform the modulo calculation on it: if `i%2` is `0` then `i` is divisble by 2. It's even. If it's not equal to `0` then it's not an even number, and in that case we'll print it out.\n",
680 | "\n",
681 | "Which is exactly what happens with:\n",
682 | "```python\n",
683 | "oddNumbers(10)\n",
684 | "oddNumbers(15)\n",
685 | "```\n",
686 | "\n",
687 | "The last line is something new:\n",
688 | "```python\n",
689 | "help(oddNumbers)\n",
690 | "```\n",
691 | "If you look at the output of this, you'll see that it prints out the content we wrote into the triple-quotes in the function definition. So if you want to give your function some documentation that others can access, this is how you do it. In fact, this is how _every_ function in Python should be documented. \n",
692 | "\n",
693 | "Try these (and others) in the empty code block below:\n",
694 | "```python\n",
695 | "help(len)\n",
696 | "help(str)\n",
697 | "myList = [1,2,3]\n",
698 | "help(myList.append)\n",
699 | "```"
700 | ]
701 | },
702 | {
703 | "cell_type": "code",
704 | "execution_count": null,
705 | "metadata": {},
706 | "outputs": [],
707 | "source": [
708 | "help(len)\n",
709 | "myList = [1,2,3]\n",
710 | "help(myList.append)"
711 | ]
712 | },
713 | {
714 | "cell_type": "markdown",
715 | "metadata": {},
716 | "source": [
717 | "#### A Challenge for you!\n",
718 | "\n",
719 | "Now modify the oddNumbers function so that it _also_ prints \"Yuck, an even number!\" for every even number..."
720 | ]
721 | },
722 | {
723 | "cell_type": "code",
724 | "execution_count": null,
725 | "metadata": {
726 | "solution2": "hidden",
727 | "solution2_first": true
728 | },
729 | "outputs": [],
730 | "source": [
731 | "#your code here"
732 | ]
733 | },
734 | {
735 | "cell_type": "code",
736 | "execution_count": null,
737 | "metadata": {
738 | "solution2": "hidden"
739 | },
740 | "outputs": [],
741 | "source": [
742 | "def oddNumbers(inputRange):\n",
743 | " for i in range(inputRange):\n",
744 | " if i%2 != 0:\n",
745 | " print(i)\n",
746 | " else:\n",
747 | " print(\"Yuck, an even number!\")\n",
748 | "\n",
749 | "oddNumbers(8)"
750 | ]
751 | },
752 | {
753 | "cell_type": "markdown",
754 | "metadata": {},
755 | "source": [
756 | "## Functions as Parameters of Other Functions\n",
757 | "\n",
758 | "This leads us to another intersting idea: since moving around functions is so easy, what happens when we use them as inputs to other functions?"
759 | ]
760 | },
761 | {
762 | "cell_type": "code",
763 | "execution_count": null,
764 | "metadata": {},
765 | "outputs": [],
766 | "source": [
767 | "def addTwo(param1):\n",
768 | " return param1 + 2\n",
769 | "\n",
770 | "def multiplyByThree(param1): # Note: this is a *separate* variable from the param1 in addTwo() because of scoping!\n",
771 | " return param1 * 3\n",
772 | "\n",
773 | "# you can use multiplyByThree\n",
774 | "# with a regular argument as input \n",
775 | "print(multiplyByThree(2))\n",
776 | "\n",
777 | "# but also with a function as input\n",
778 | "print(multiplyByThree(addTwo(2)))\n",
779 | "\n",
780 | "# And then\n",
781 | "print(addTwo(multiplyByThree(2)))"
782 | ]
783 | },
784 | {
785 | "cell_type": "markdown",
786 | "metadata": {},
787 | "source": [
788 | "# Code (Applied Geo-example)\n",
789 | "\n",
790 | "For the last Geo-Example, let's revisit a couple of old exercises, combining them and making them a bit more sophisticated with the help of our newly acquired concept of functions.\n",
791 | "\n",
792 | "First, let's define some variables to contain data that we will then use with the functions."
793 | ]
794 | },
795 | {
796 | "cell_type": "code",
797 | "execution_count": null,
798 | "metadata": {},
799 | "outputs": [],
800 | "source": [
801 | "# London's total population\n",
802 | "london_pop = 7375000\n",
803 | "\n",
804 | "# list with some of London's borough. Feel free to add more! \n",
805 | "london_boroughs = {\n",
806 | " \"City of London\": {\n",
807 | " \"population\": 8072,\n",
808 | " \"coordinates\" : [-0.0933, 51.5151]\n",
809 | " },\n",
810 | " \"Camden\": {\n",
811 | " \"population\": 220338,\n",
812 | " \"coordinates\" : [-0.2252,1.5424]\n",
813 | " },\n",
814 | " \"Hackney\": {\n",
815 | " \"population\": 220338,\n",
816 | " \"coordinates\" : [-0.0709, 51.5432]\n",
817 | " },\n",
818 | " \"Lambeth\": {\n",
819 | " \"population\": 303086,\n",
820 | " \"coordinates\" : [-0.1172,51.5013]\n",
821 | " }\n",
822 | "}"
823 | ]
824 | },
825 | {
826 | "cell_type": "markdown",
827 | "metadata": {},
828 | "source": [
829 | "Now, fix the code in the next cell to use the variables defined in the last cell. The `calcProportion` function should return the proportion of the population that the `boro` borough composes of London. The `getLocation` function should return the coordinates of the `boro` borough. "
830 | ]
831 | },
832 | {
833 | "cell_type": "code",
834 | "execution_count": null,
835 | "metadata": {
836 | "solution2": "hidden",
837 | "solution2_first": true
838 | },
839 | "outputs": [],
840 | "source": [
841 | "def calcProportion(boro,city_pop=???):\n",
842 | " return ???['population']/???\n",
843 | "\n",
844 | "def getLocation(???):\n",
845 | " return boro[???]"
846 | ]
847 | },
848 | {
849 | "cell_type": "code",
850 | "execution_count": null,
851 | "metadata": {
852 | "solution2": "hidden"
853 | },
854 | "outputs": [],
855 | "source": [
856 | "#in this function definition we provide a default value for city_pop\n",
857 | "#this makes sense here because we are only dealing with london\n",
858 | "def calcProportion(boro,city_pop=7375000):\n",
859 | " return boro['population']/city_pop\n",
860 | "\n",
861 | "def getLocation(boro):\n",
862 | " return boro['coordinates'] #returns the value for the `coordinates` key from the value for the `Lambeth` key"
863 | ]
864 | },
865 | {
866 | "cell_type": "markdown",
867 | "metadata": {},
868 | "source": [
869 | "Write some code to print the **longitude of Lambeth**. This could be done in a single line but don't stress if you need to use more lines... "
870 | ]
871 | },
872 | {
873 | "cell_type": "code",
874 | "execution_count": null,
875 | "metadata": {
876 | "solution2": "hidden",
877 | "solution2_first": true
878 | },
879 | "outputs": [],
880 | "source": []
881 | },
882 | {
883 | "cell_type": "code",
884 | "execution_count": null,
885 | "metadata": {
886 | "solution2": "hidden"
887 | },
888 | "outputs": [],
889 | "source": [
890 | "#one-liner (see if you can understand how it works)\n",
891 | "print(getLocation(london_boroughs['Lambeth'])[0])\n",
892 | "\n",
893 | "# A longer but possibly more user-friendly way:\n",
894 | "coord = getLocation(london_boroughs['Lambeth'])\n",
895 | "long = coord[0]\n",
896 | "print(long)"
897 | ]
898 | },
899 | {
900 | "cell_type": "markdown",
901 | "metadata": {},
902 | "source": [
903 | "Write some code to print the **proportion of the London population that lives in the City of London**. Using the function defined above, this should take only one line of code."
904 | ]
905 | },
906 | {
907 | "cell_type": "code",
908 | "execution_count": null,
909 | "metadata": {
910 | "solution2": "hidden",
911 | "solution2_first": true
912 | },
913 | "outputs": [],
914 | "source": []
915 | },
916 | {
917 | "cell_type": "code",
918 | "execution_count": null,
919 | "metadata": {
920 | "solution2": "hidden"
921 | },
922 | "outputs": [],
923 | "source": [
924 | "print(calcProportion(london_boroughs['City of London']))"
925 | ]
926 | },
927 | {
928 | "cell_type": "markdown",
929 | "metadata": {},
930 | "source": [
931 | "Write code to loop over the `london_boroughs` dictionary, use the `calcProportion` and `getLocation` functions to then print proportions and locations of all the boroughs. "
932 | ]
933 | },
934 | {
935 | "cell_type": "code",
936 | "execution_count": null,
937 | "metadata": {
938 | "solution2": "hidden",
939 | "solution2_first": true
940 | },
941 | "outputs": [],
942 | "source": []
943 | },
944 | {
945 | "cell_type": "code",
946 | "execution_count": null,
947 | "metadata": {
948 | "solution2": "hidden"
949 | },
950 | "outputs": [],
951 | "source": [
952 | "for boro, data in london_boroughs.items():\n",
953 | " prop = calcProportion(data)\n",
954 | " location = getLocation(data)\n",
955 | " \n",
956 | " print(prop)\n",
957 | " print(location)\n",
958 | " print(\"\")\n",
959 | " \n",
960 | " #to print more nicely you could use string formatting:\n",
961 | " #print(\"Proportion is {0:3.3f}%\".format(prop*100))\n",
962 | " #print(\"Location of \" + boro + \" is \" + str(location))"
963 | ]
964 | },
965 | {
966 | "cell_type": "markdown",
967 | "metadata": {},
968 | "source": [
969 | "\n",
970 | "### Further references:\n",
971 | "\n",
972 | "General list or resources\n",
973 | "- [Awesome list of resources](https://github.com/vinta/awesome-python)\n",
974 | "- [Python Docs](https://docs.python.org/2.7/tutorial/introduction.html)\n",
975 | "- [HitchHiker's guide to Python](http://docs.python-guide.org/en/latest/intro/learning/)\n",
976 | "- [Python for Informatics](http://www.pythonlearn.com/book_007.pdf)\n",
977 | "- [Learn Python the Hard Way - Lists](http://learnpythonthehardway.org/book/ex32.html)\n",
978 | "- [Learn Python the Hard Way - Dictionaries](http://learnpythonthehardway.org/book/ex39.html)\n",
979 | "- [CodeAcademy](https://www.codecademy.com/courses/python-beginner-en-pwmb1/0/1)\n",
980 | "\n"
981 | ]
982 | },
983 | {
984 | "cell_type": "markdown",
985 | "metadata": {},
986 | "source": [
987 | "### Credits!\n",
988 | "\n",
989 | "#### Contributors:\n",
990 | "The following individuals have contributed to these teaching materials: \n",
991 | "- [James Millington](https://github.com/jamesdamillington)\n",
992 | "- [Jon Reades](https://github.com/jreades)\n",
993 | "- [Michele Ferretti](https://github.com/miccferr)\n",
994 | "\n",
995 | "#### License\n",
996 | "The content and structure of this teaching project itself is licensed under the [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 license](https://creativecommons.org/licenses/by-nc-sa/4.0/), and the contributing source code is licensed under [The MIT License](https://opensource.org/licenses/mit-license.php).\n",
997 | "\n",
998 | "#### Acknowledgements:\n",
999 | "Supported by the [Royal Geographical Society](https://www.rgs.org/HomePage.htm) (with the Institute of British Geographers) with a Ray Y Gildea Jr Award.\n",
1000 | "\n",
1001 | "#### Potential Dependencies:\n",
1002 | "This notebook may depend on the following libraries: None"
1003 | ]
1004 | }
1005 | ],
1006 | "metadata": {
1007 | "anaconda-cloud": {},
1008 | "kernelspec": {
1009 | "display_name": "Python 3",
1010 | "language": "python",
1011 | "name": "python3"
1012 | },
1013 | "language_info": {
1014 | "codemirror_mode": {
1015 | "name": "ipython",
1016 | "version": 3
1017 | },
1018 | "file_extension": ".py",
1019 | "mimetype": "text/x-python",
1020 | "name": "python",
1021 | "nbconvert_exporter": "python",
1022 | "pygments_lexer": "ipython3",
1023 | "version": "3.7.6"
1024 | }
1025 | },
1026 | "nbformat": 4,
1027 | "nbformat_minor": 4
1028 | }
1029 |
--------------------------------------------------------------------------------
/notebook-12-packages.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Notebook-12: Introduction to Packages"
8 | ]
9 | },
10 | {
11 | "cell_type": "markdown",
12 | "metadata": {},
13 | "source": [
14 | "## Functions vs. Packages\n",
15 | "\n",
16 | "We saw in a previous Code Camp Lesson how **functions** are a useful programming tool to enable us to re-use chunks of code. Basically, a function is a way to do something to something in a portable, easy-to-use little bundle of code. "
17 | ]
18 | },
19 | {
20 | "cell_type": "markdown",
21 | "metadata": {},
22 | "source": [
23 | "However, some steps in a program are done so many times by so many people that, eventually, someone writes a **package** that bundles up those operations into something easy to use that saves _you_ having to figure out the gory details. \n",
24 | "\n",
25 | "In this lesson we'll see how packages are useful in the context of tacking problems programmatically. The problem we will use here as an example is: _download a data file that we know is hosted on a web site and then do some analysis of those data_. \n",
26 | "\n",
27 | "In this example, we are going to access and analyse a data set that we created: [Cities from Wikipedia Data](https://raw.githubusercontent.com/kingsgeocomp/geocomputation/master/CitiesWithWikipediaData-simple.csv). Have a look at those data in your browser now by pasting the URL into a new tab. We'll use the `urlopen` package to help us access the data."
28 | ]
29 | },
30 | {
31 | "cell_type": "markdown",
32 | "metadata": {},
33 | "source": [
34 | "### Q: How is a package different from a function?\n",
35 | "\n",
36 | "**A**: A package is just a bundle of useful functions. \n",
37 | "\n",
38 | "There's a little more to it than this, but simplest way to think of this is that when we type `import foo` then we are including functions from the `foo` package in our code. You'll see how this works below.\n",
39 | "\n",
40 | "The point of the bundle-of-functions is that they can help us to achieve quite a lot very quickly since we don't need to reinvent the wheel and can just make use of someone else's code. In the same way that we won't mark you down for Googling the answer to a coding question, we won't mark you down for using someone else's package to help you get going with your programming. _**That's the whole point!**_\n",
41 | "\n",
42 | "Often, if you're not sure where to start Google (or StackOverflow) is the place to go:\n",
43 | "\n",
44 | "[`how to read text file on web server python`](https://www.google.co.uk/search?q=how+to+read+text+file+on+web+server+python&oq=how+to+read+text+file+on+web+server+python&aqs=chrome..69i57.629j0j7&sourceid=chrome&ie=UTF-8)\n",
45 | "\n",
46 | "Boom!"
47 | ]
48 | },
49 | {
50 | "cell_type": "markdown",
51 | "metadata": {},
52 | "source": [
53 | "### Q: But why have packages at all, why not just copy and paste all the functions that we need into our notebook directly? \n",
54 | "\n",
55 | "**A**: Because it keeps our code 'clean' and allows us to have functions with the *same name* that do different things. \n",
56 | "\n",
57 | "Imagine the following:\n",
58 | "\n",
59 | "- You want to write a program to read data from two different web sites (_e.g._ [NOMIS](https://www.nomisweb.co.uk/default.asp) and the [London Data Store](https://data.london.gov.uk/)) \n",
60 | "- It turns out that both already have handy Python packages that help you to read the data if you give them the URL of the page to read\n",
61 | "- Even better, *both* packages have a simple function called `read_data(...)` that takes a URL and extracts the data that you want from that page as a CSV file, except...\n",
62 | "- Because the web sites are _completely_ different the `read_data(...)` functions are _specific_ to that one web site!\n",
63 | "\n",
64 | "So, if both packages have functions callend `read_data()`, then after you have done:\n",
65 | "```python\n",
66 | "import nomis_reader\n",
67 | "import ldn_data_store_reader\n",
68 | "```\n",
69 | "How does Python know which `read_data` you want to use? \n",
70 | "\n",
71 | "The answer is the _namespace_: you can _only_ access the NOMIS `read_data` function by typing `nomis.read_data(...)` and you can _only_ access the Data Store `read_data` function by typing `ldn_data_store_reader.read_data(...)`. \n",
72 | "\n",
73 | "### Import... As...\n",
74 | "\n",
75 | "The length and awkwardness of that last line is why you can write this:\n",
76 | "```python\n",
77 | "import ldn_data_store_reader as ldn\n",
78 | "```\n",
79 | "Because the _alias_ (`\"...as ldn\"`) lets you type `ldn.read_data(...)` and Python will know exactly which function you mean!"
80 | ]
81 | },
82 | {
83 | "cell_type": "markdown",
84 | "metadata": {},
85 | "source": [
86 | "## Packages in Programming Context\n",
87 | "\n",
88 | "The first step to writing a program is thinking about your goal and the steps required to achieve that. We _**don't**_ write programs like we write essays: all at once by writing a whole lot of code and then hoping for the best when we hit 'submit'. \n",
89 | "\n",
90 | "When you're tackling a programming problem you break it down into separate, simpler steps, and then tick them off one by one. Doing this gets easier as you become more familiar with programming, but it remains crucial and, in many cases, good programmers in large companies spend more time on _design_ than they do on actual _coding_."
91 | ]
92 | },
93 | {
94 | "cell_type": "markdown",
95 | "metadata": {},
96 | "source": [
97 | "To a computer, reading data from a remote location (e.g. a web site halfway around the world) is not really any different from reading one that's sitting on your your local hard drive (e.g. on your desktop). To simplify things a great deal: the computer really just needs to know the location of the file and an appropriate _protocol_ for accessing that file (_e.g._ http, https, ftp, local...) and then a clever programming language like Python will typically have packages that can kind of take of the rest. \n",
98 | "\n",
99 | "In all cases -- local and remote -- you use the package to handle the hard bit of knowing how to actually 'read' data (because all files are just `1`s and `0`s of data) at the _device_ level and then Python gives you back a 'file handle' that helps you to achieve things like 'read a line' or 'close an open file'. You can think of a filehandle as something that gives you a 'grip' on a file-like object no matter where or what it is, and the package is the way that this magic is achieved."
100 | ]
101 | },
102 | {
103 | "cell_type": "markdown",
104 | "metadata": {},
105 | "source": [
106 | "Let's recall our problem: _download a data file that we know is hosted on a web site and then do some analysis of those data_.\n",
107 | "\n",
108 | "We need to break this seemingly hard problem down into something simpler and can do this by thinking about it as three separate steps:\n",
109 | "\n",
110 | "1. First we want to read a remote file (i.e. a text file somewhere the planet), \n",
111 | "2. Then we want to turn it into a local data structure (i.e a list or a dictionary), \n",
112 | "3. Finally we want to perform some calculations on the data (e.g. calculate the mean).\n",
113 | "\n",
114 | "We can tackle each of those in turn, getting the first bit working, then adding the second bit, etc. It's just like using lego to build something: you take the same pieces and assemble them in different ways to produce different things."
115 | ]
116 | },
117 | {
118 | "cell_type": "markdown",
119 | "metadata": {},
120 | "source": [
121 | "### Step 1: Reading a Remote File\n",
122 | "\n",
123 | "So, as I said, in Step \\#1 we are going to download a file hosted on a remote web site at [Cities from Wikipedia Data](https://raw.githubusercontent.com/kingsgeocomp/geocomputation/master/CitiesWithWikipediaData-simple.csv) (this can also be stored as a bit-link to make it easier to copy+paste and avoid _really_ long lines: http://bit.ly/2vrUFKi)\n",
124 | "\n",
125 | "We aren't going to to try to turn it into data or otherwise make 'sense' of it yet, we just want to **get** it. We are then going to build from this first step towards more substantial exercises and, eventually, you could easily request Megabytes of data in real-time according to flexibly-specified parameters!\n",
126 | "\n",
127 | "Because we're accessing data from a URL we will use the `urlopen` [function](https://docs.python.org/3.0/library/urllib.request.html?highlight=urlopen#urllib.request.urlopen) from the `urllib.request` [package](https://docs.python.org/3.0/library/urllib.request.html). \n",
128 | "\n",
129 | "If you're wondering how we know to use this function and package, you might google something like: _read remote csv file python 3_ which in turn might get you to a StackOverflow question and anwer like [this](https://stackoverflow.com/questions/36965864/opening-a-url-with-urllib-in-python-3). "
130 | ]
131 | },
132 | {
133 | "cell_type": "markdown",
134 | "metadata": {},
135 | "source": [
136 | "#### Getting help with packages\n",
137 | "\n",
138 | "Of course, just knowing that you need `urlopen` doesn't necessarily help you to actually _use_ it. In addition to finding example code on StackOverflow, you can also ask the package itself for help with `dir` and `help`.\n",
139 | "\n",
140 | "##### dir\n",
141 | "\n",
142 | "The 'Dive into Python' web site will tell you that \"dir returns a list of the attributes and methods of any object\". That introduces yet another term ('modules') that we don't want to get into right now, but _everything_ in Python is an object and so `dir` will give you help with packages, variables, functions... you name it.\n",
143 | "\n",
144 | "```python\n",
145 | "import math\n",
146 | "dir(math)\n",
147 | "```\n",
148 | "\n",
149 | "What **`dir`** gives you is information about things you can potentially do: it's like navigating the menu of a web site -- you aren't yet looking at the information you need, you're trying to figure out if the site even _has_ what you need. So `dir` on a package will give you a list of the functions (and any variables) that the person who created the package has provided.\n",
150 | "\n",
151 | "Typically, the information given by `dir` is highly abbreviated and is really just a prelude to using `help`.\n",
152 | "\n",
153 | "##### help\n",
154 | "\n",
155 | "The `help` function gives you the actual detail you need about how to use a particular function: what are the inputs, what are the outputs, and what will the function actually _do_ ?\n",
156 | "\n",
157 | "Both of these can be used on a package, a function, or a variable that you've created; for example: \n",
158 | "```python\n",
159 | "import math\n",
160 | "help(math.acos)\n",
161 | "```\n",
162 | "\n",
163 | "Let's see this in action!"
164 | ]
165 | },
166 | {
167 | "cell_type": "code",
168 | "execution_count": null,
169 | "metadata": {},
170 | "outputs": [],
171 | "source": [
172 | "from urllib.request import urlopen\n",
173 | "print(\"dir(urlopen) returns:\\n\")\n",
174 | "print(dir(urlopen))\n",
175 | "print(\"\\n\\n\")\n",
176 | "print(\"help(urlopen) returns:\\n\")\n",
177 | "print(help(urlopen)) # Notice!"
178 | ]
179 | },
180 | {
181 | "cell_type": "markdown",
182 | "metadata": {},
183 | "source": [
184 | "**Note**: you can also get help in Jupyter by typing `?urlopen` in a code block and then hitting 'run'."
185 | ]
186 | },
187 | {
188 | "cell_type": "code",
189 | "execution_count": null,
190 | "metadata": {},
191 | "outputs": [],
192 | "source": [
193 | "?urlopen"
194 | ]
195 | },
196 | {
197 | "cell_type": "markdown",
198 | "metadata": {},
199 | "source": [
200 | "#### A Challenge for you!\n",
201 | "\n",
202 | "Fix the code to set the `url` variable, then use it with the `urlopen` function to open it."
203 | ]
204 | },
205 | {
206 | "cell_type": "code",
207 | "execution_count": null,
208 | "metadata": {
209 | "solution2": "shown",
210 | "solution2_first": true
211 | },
212 | "outputs": [],
213 | "source": [
214 | "from urllib.request import urlopen\n",
215 | "\n",
216 | "# Given the info you were given above, what do you \n",
217 | "# think the value of 'url' should be? What\n",
218 | "# type of variable is it? int or string? \n",
219 | "url = ???\n",
220 | "\n",
221 | "# Read the URL stream into variable called 'response'\n",
222 | "# using the function that we imported above\n",
223 | "response = ???(url)\n",
224 | "\n",
225 | "#now read from the stream, decoding so that we get actual text\n",
226 | "datafile = response.read().decode('utf-8')\n",
227 | "\n",
228 | "print(\"datafile variable is of type: '\" + datafile.__class__.__name__ + \"'.\\n\")"
229 | ]
230 | },
231 | {
232 | "cell_type": "code",
233 | "execution_count": null,
234 | "metadata": {
235 | "solution2": "shown"
236 | },
237 | "outputs": [],
238 | "source": [
239 | "from urllib.request import urlopen\n",
240 | "\n",
241 | "url = \"http://bit.ly/2vrUFKi\"\n",
242 | "response = urlopen(url)\n",
243 | "datafile = response.read().decode('utf-8')\n",
244 | "\n",
245 | "print(\"datafile variable is of type: '\" + datafile.__class__.__name__ + \"'.\\n\")"
246 | ]
247 | },
248 | {
249 | "cell_type": "markdown",
250 | "metadata": {},
251 | "source": [
252 | "Note that the _datafile_ variable is of type `string` (because we decoded it as such). If we hadn't decoded it, the result would have been of type `bytes` which wouldn't be as easy for us (humans) to work with. \n",
253 | "\n",
254 | "Now that we've read our data as text, we can print it to check. "
255 | ]
256 | },
257 | {
258 | "cell_type": "code",
259 | "execution_count": null,
260 | "metadata": {},
261 | "outputs": [],
262 | "source": [
263 | "print(datafile)"
264 | ]
265 | },
266 | {
267 | "cell_type": "markdown",
268 | "metadata": {},
269 | "source": [
270 | "So this is definitely text, but even though it has been nicely formatted _visually_ in the notebook, right now it's actually not in a very convenient format to work with in our _code_. The `datafile` `str` object is actually a single string of text that the notebook is interpreting as having line breaks. We can see this by printing the 'raw' object (without jupyter notebook formatting for us) using the `repr` function:"
271 | ]
272 | },
273 | {
274 | "cell_type": "code",
275 | "execution_count": null,
276 | "metadata": {},
277 | "outputs": [],
278 | "source": [
279 | "print(repr(datafile))"
280 | ]
281 | },
282 | {
283 | "cell_type": "markdown",
284 | "metadata": {},
285 | "source": [
286 | "Note the `\\n` character that jupyter notebook is using to determine when to print a [new line](https://en.wikipedia.org/wiki/Newline) for us to read nicely.\n",
287 | "\n",
288 | "To split the text into individual lines ourselves (ready to work with in our code), we can use the handily named `.splitlines()` method (more on methods below):"
289 | ]
290 | },
291 | {
292 | "cell_type": "code",
293 | "execution_count": null,
294 | "metadata": {},
295 | "outputs": [],
296 | "source": [
297 | "url = \"http://bit.ly/2vrUFKi\"\n",
298 | "\n",
299 | "response = urlopen(url)\n",
300 | "datafile = response.read().decode('utf-8').splitlines()\n",
301 | "\n",
302 | "print(\"datafile variable is of type: '\" + datafile.__class__.__name__ + \"'.\\n\")"
303 | ]
304 | },
305 | {
306 | "cell_type": "markdown",
307 | "metadata": {},
308 | "source": [
309 | "Note now, that the data variable has type `list`. When we print this `datafile` `list` object (without `repr`) the `\\n` characters have gone and the list elements are split where those new line characters were previously (look carefully at where the `'` are): "
310 | ]
311 | },
312 | {
313 | "cell_type": "code",
314 | "execution_count": null,
315 | "metadata": {},
316 | "outputs": [],
317 | "source": [
318 | "print(datafile)"
319 | ]
320 | },
321 | {
322 | "cell_type": "markdown",
323 | "metadata": {},
324 | "source": [
325 | "We can see this more clearly if we use a for loop to print out each element of the list (each element being a row of the original online file):"
326 | ]
327 | },
328 | {
329 | "cell_type": "code",
330 | "execution_count": null,
331 | "metadata": {},
332 | "outputs": [],
333 | "source": [
334 | "for row in datafile:\n",
335 | " print(row)"
336 | ]
337 | },
338 | {
339 | "cell_type": "markdown",
340 | "metadata": {},
341 | "source": [
342 | "The last row should be `10,Sheffield,10,-163545.3257,7055177.403,685368`.\n",
343 | "\n",
344 | "If you've managed to get the code above to run and have received 11 rows of text in response to your `urlopen` query then, congratulations, you've now read a text file sitting on a server in, I think, Alberta, Canada and Python _didn't care_. "
345 | ]
346 | },
347 | {
348 | "cell_type": "markdown",
349 | "metadata": {},
350 | "source": [
351 | "### Step 2: Turning Text into Data\n",
352 | "\n",
353 | "We now need to work on turning the response we got to our `urlopen` request into useful data. You'll notice that we are dealing with a _CSV_ (Comma-Separated Value) file and that the format is quite simple since none of the rows have fields that *themselves* contain commas. So to turn this into data we just need to _split_ the row into separate fields using the commas.\n",
354 | "\n",
355 | "In the code below, `dir('string')` lists the available function for strings (because `'string'` is itself a _String_; we could just as easily written `dir('foo')` or `dir('supercalifragilisticexpialidocious')` because 'foo' and 'supercalifragilisticexpialidocious' are also strings and so have the _same_ functions available. \n",
356 | "\n",
357 | "In the output below, the functions that start and end with `__` are generally considered private, so you can skip over these and focus on the ones further down that are designed to be useful to programmers. Can you spot the method that is most likely to be useful?"
358 | ]
359 | },
360 | {
361 | "cell_type": "markdown",
362 | "metadata": {},
363 | "source": [
364 | "### A Brief Musical Interlude\n",
365 | "\n",
366 | "Just in case you need help pronouncing supercalifragilisticexpialidocious:\n",
367 | "\n",
368 | "[](https://youtu.be/uZNRzc3hWvE)"
369 | ]
370 | },
371 | {
372 | "cell_type": "markdown",
373 | "metadata": {},
374 | "source": [
375 | "Remember that you can find out what _methods_ are supported by a string using `dir()`:\n",
376 | "```python\n",
377 | "dir('supercalifragilisticexpialidocious')\n",
378 | "```\n",
379 | "I'm going to save you some time (_this_ time!) and tell you that we're interested in the `split` method. Why not use the `help` function to figure out how to make use of it?"
380 | ]
381 | },
382 | {
383 | "cell_type": "code",
384 | "execution_count": null,
385 | "metadata": {},
386 | "outputs": [],
387 | "source": [
388 | "help('supercalifragilisticexpialidocious'.split)"
389 | ]
390 | },
391 | {
392 | "cell_type": "markdown",
393 | "metadata": {},
394 | "source": [
395 | "Now, using the output of the `help` command, how would you use `split` to turn that word into a list like this: \n",
396 | "```python\n",
397 | "['sup','rcalifragilisticexpialidocious']\n",
398 | "```\n",
399 | "If you replace the `???` with the right bits of code then running the block below will print out \"You got it!\". You *only* need to change the `???` and nothing else!"
400 | ]
401 | },
402 | {
403 | "cell_type": "code",
404 | "execution_count": null,
405 | "metadata": {
406 | "solution2": "shown",
407 | "solution2_first": true
408 | },
409 | "outputs": [],
410 | "source": [
411 | "if ['sup','rcalifragilisticexpialidocious']=='supercalifragilisticexpialidocious'.split(???, maxsplit=???):\n",
412 | " print(\"You got it!\")\n",
413 | "else:\n",
414 | " print(\"Not yet!\")"
415 | ]
416 | },
417 | {
418 | "cell_type": "code",
419 | "execution_count": null,
420 | "metadata": {
421 | "solution2": "shown"
422 | },
423 | "outputs": [],
424 | "source": [
425 | "if ['sup','rcalifragilisticexpialidocious']=='supercalifragilisticexpialidocious'.split('e', maxsplit=1):\n",
426 | " print(\"You got it!\")\n",
427 | "else:\n",
428 | " print(\"Not yet!\")"
429 | ]
430 | },
431 | {
432 | "cell_type": "code",
433 | "execution_count": null,
434 | "metadata": {},
435 | "outputs": [],
436 | "source": [
437 | "# Some other string methods\n",
438 | "print('supercalifragilisticexpialidocious'.upper())\n",
439 | "print('supercalifragilisticexpialidocious'.title())"
440 | ]
441 | },
442 | {
443 | "cell_type": "markdown",
444 | "metadata": {},
445 | "source": [
446 | "OK, so you've tracked down the way to split a `string` using a delimiter and _even_ how to limit the number of 'words' that come out of the split operation. And you already saw another of these methods above (i.e. `splitlines`). We work a lot with `string`s, so it's handy to get to know the readily-available methods well.\n",
447 | "\n",
448 | "Let's test `string` splitting using our sample data (the last line of the 'simple' CSV file) to make sure it works the way we think it does... We want to turn the `string` below into a list like this:\n",
449 | "```python\n",
450 | "['10', 'Sheffield', '10', '-163545.3257', '7055177.403', '685368']\n",
451 | "```\n",
452 | "Again, we only need to change the `???`."
453 | ]
454 | },
455 | {
456 | "cell_type": "code",
457 | "execution_count": null,
458 | "metadata": {},
459 | "outputs": [],
460 | "source": [
461 | "test = datafile[-1].split(???)\n",
462 | "print(test)"
463 | ]
464 | },
465 | {
466 | "cell_type": "code",
467 | "execution_count": null,
468 | "metadata": {},
469 | "outputs": [],
470 | "source": [
471 | "test = datafile[-1].split(',')\n",
472 | "print(test)"
473 | ]
474 | },
475 | {
476 | "cell_type": "markdown",
477 | "metadata": {
478 | "solution2": "hidden",
479 | "solution2_first": true
480 | },
481 | "source": [
482 | "#### Consider\n",
483 | "\n",
484 | "A question: why do you think that I consider a) to be data and not b)? \n",
485 | "\n",
486 | "a)\n",
487 | "```python\n",
488 | "['10', 'Sheffield', '10', '-163545.3257', '7055177.403', '685368']\n",
489 | "```\n",
490 | "\n",
491 | "b)\n",
492 | "```python\n",
493 | "\"10,Sheffield,10,-163545.3257,7055177.403,685368\"\n",
494 | "```\n",
495 | "\n",
496 | "**Decide what you think the answer is _before_ clicking for the solution**"
497 | ]
498 | },
499 | {
500 | "cell_type": "markdown",
501 | "metadata": {
502 | "solution2": "hidden"
503 | },
504 | "source": [
505 | "Hopefully you can see that a) is a `list` and b) is a `str` (string). Because a) is a list we can easily access each element. For example try the following code yourself: \n",
506 | "\n",
507 | "Here's a clue:\n",
508 | "```python\n",
509 | "print(\"The population of \" + myList[1] + \" is \" + myList[5])\n",
510 | "```\n",
511 | "\n",
512 | "It is much more difficult to access the individual pieces of information from the string... "
513 | ]
514 | },
515 | {
516 | "cell_type": "markdown",
517 | "metadata": {},
518 | "source": [
519 | "You can hopefully see how we're breaking a complex problem down into a set of _increments_ , each of which is a bit easier to write and understand. "
520 | ]
521 | },
522 | {
523 | "cell_type": "markdown",
524 | "metadata": {},
525 | "source": [
526 | "### Step 3 Analysis"
527 | ]
528 | },
529 | {
530 | "cell_type": "code",
531 | "execution_count": null,
532 | "metadata": {},
533 | "outputs": [],
534 | "source": [
535 | "total = 0\n",
536 | "count = 0\n",
537 | "\n",
538 | "for idx,row in enumerate(datafile):\n",
539 | " \n",
540 | " if(idx > 0):\n",
541 | " total = total + int(row[-1])\n",
542 | " count += 1\n",
543 | " #print(row)\n",
544 | "mean = total / count \n",
545 | "\n",
546 | "print(mean)"
547 | ]
548 | },
549 | {
550 | "cell_type": "markdown",
551 | "metadata": {},
552 | "source": [
553 | "### Credits!\n",
554 | "\n",
555 | "#### Contributors:\n",
556 | "The following individuals have contributed to these teaching materials: \n",
557 | "- [James Millington](https://github.com/jamesdamillington)\n",
558 | "- [Jon Reades](https://github.com/jreades)\n",
559 | "\n",
560 | "#### License\n",
561 | "The content and structure of this teaching project itself is licensed under the [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 license](https://creativecommons.org/licenses/by-nc-sa/4.0/), and the contributing source code is licensed under [The MIT License](https://opensource.org/licenses/mit-license.php).\n",
562 | "\n",
563 | "#### Acknowledgements:\n",
564 | "Supported by the [Royal Geographical Society](https://www.rgs.org/HomePage.htm) (with the Institute of British Geographers) with a Ray Y Gildea Jr Award.\n",
565 | "\n",
566 | "#### Potential Dependencies:\n",
567 | "This notebook may depend on the following libraries: None"
568 | ]
569 | }
570 | ],
571 | "metadata": {
572 | "anaconda-cloud": {},
573 | "kernelspec": {
574 | "display_name": "Python 3",
575 | "language": "python",
576 | "name": "python3"
577 | },
578 | "language_info": {
579 | "codemirror_mode": {
580 | "name": "ipython",
581 | "version": 3
582 | },
583 | "file_extension": ".py",
584 | "mimetype": "text/x-python",
585 | "name": "python",
586 | "nbconvert_exporter": "python",
587 | "pygments_lexer": "ipython3",
588 | "version": "3.7.6"
589 | }
590 | },
591 | "nbformat": 4,
592 | "nbformat_minor": 4
593 | }
594 |
--------------------------------------------------------------------------------
/notebook-13-classes.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Notebook-13: Inheritance and Classes"
8 | ]
9 | },
10 | {
11 | "cell_type": "markdown",
12 | "metadata": {},
13 | "source": [
14 | "# A (Semi-Brief) Discourse on Families & Inheritance\n",
15 | "\n",
16 | "GeoPandas objects are deliberately designed to resemble Pandas objects. There are two good reasons for this: \n",
17 | "\n",
18 | "1. Since Pandas is well-known, this makes it easier to learn how to use GeoPandas.\n",
19 | "2. GeoPandas _inherits_ functionality from Pandas. \n",
20 | "\n",
21 | "The concept of **inheritance** is something we've held off from mentioning until now, but it's definitely worth understanding if you are serious about learning how to code. In effect, geopandas '_imports_' pandas and then _extends_ it so that the more basic class (pandas in this case) learns how to work with geodata... pandas doesn't know how to read shapefiles or make maps, but geopandas does. Same for GeoJSON.\n",
22 | "\n",
23 | "## The 'Tree of Life'\n",
24 | "\n",
25 | "Here's a simple way to think about inheritance: think of the 'evolutionary trees' you might have seen charting the evolution of organisms over time. At the bottom of the tree is the single-celled animal, and at the other end are humans, whales, wildebeest, etc. We all _inherit_ some basic functionality from that original, simple cell. In between us and that primitive, however, are a whole series of branches: different bits of the tree evolved in different directions and developed different 'functionality'. Some of us have bones. Some have cartilege. Some are vegetarian, and some are carnivorous. And so on. When you get to the primates we all share certain common 'features' (binocular vision, grasping hands, etc.), but we are _still_ more similar to gorillas than we are to macaques. So gorillas and humans _extend_ the primitive 'primate functionality' with some bonus features (bigger brains, greater strength, etc.) that are useful, while macaques extend it with a slightly different set of features (tails, etc.).\n",
26 | "\n",
27 | "\n",
28 | "\n",
29 | "## The 'Tree of Classes'\n",
30 | "\n",
31 | "Inheritance in code works in a similar way: *all* Python classes (lists, pandas, plots, etc.) inherit their most basic functionality from a single primitive 'object' class that itself does very little except to provide a template for what an object should look like. As you move along the inheritance tree you will find more and more complex objects with increasingly advanced features: GeoPandas inherits from Pandas, Bokeh and Seaborn inherit from matplotlib, etc. \n",
32 | "\n",
33 | "I can't find an image of Python base class inheritance, but I've found an equally useful example of how _anything_ can be modelled using this 'family tree' approach... consider the following:\n",
34 | "\n",
35 | "\n",
36 | "\n",
37 | "If we were trying to implement a vehicle registration scheme in Python, we would want to start with the most basic category of all: _vehicle_. The vehicle class itself might not do much, but it gives us a template for _all_ vehicles (e.g. it must be registered, it must have a unique license number, etc.). We then _extend_ the functionality of this 'base class' with three intermediate classes: two-wheeled vehicles, cars, and trucks. These, in turn, lead to eight actual vehicle types. These might have _additional_ functionality: a bus might need have a passenger capacity associated with it, while a convertible might need to be hard- or soft-top. All of this could be expressed in Python as:\n",
38 | "\n",
39 | "```python\n",
40 | "class vehicle(object): # Inherit from base class\n",
41 | " def __init__(self):\n",
42 | " ... do something ...\n",
43 | "\n",
44 | "class car(vehicle): # Inherit from vehicle\n",
45 | " def __init__(self):\n",
46 | " ... do other stuff ...\n",
47 | "\n",
48 | "class sedan(car): # Inherit from car\n",
49 | " def __init__(self):\n",
50 | " ... do more stuff ...\n",
51 | "```\n",
52 | "\n",
53 | "This way, when we create a new `sedan`, it automatically 'knows' about vehicles and cars, and can make use of functions like `set_unique_id()` even if that function is _only_ specified in the base vehicle class! The thing to remember is that programmers are _lazy_: if they can avoid reinventing the wheel, they will. Object-Oriented Programming using inheritance is a good example of _constructive_ laziness: it saves us having to constantly copy and paste code (for registering a new vehicle or reading in a CSV file) from one class to the next since we can just import it and _extend_ it! \n",
54 | "\n",
55 | "### Advantages of Inheritance \\#1\n",
56 | "\n",
57 | "This also means that we are less likely to make mistakes: if we want to update our vehicle registration scheme then we don't need to update lots of functions all over the place, we just update the base class and _all_ inheriting classes automatically gain the update because they are making use of the base class' function. \n",
58 | "\n",
59 | "So if pandas is updated with a new 'load a zip file' feature then geopandas automatically benefits from it! The _only_ thing that doesn't gain that benefit immediately is our ability to make use of specifically geographical data because pandas doesn't know about that type of data, only 'normal' tabular data.\n",
60 | "\n",
61 | "### Advantages of Inheritance \\#2\n",
62 | "\n",
63 | "Inheritance also means that you can always use an instance of a 'more evolved' class in place of one of its ancestors: simplifying things a _bit_, a sedan can automatically do anything that a car can do and, by extension, anything that a vehicle can do. \n",
64 | "\n",
65 | "Similarly, since geopandas inherits from pandas if you need to use a geopandas object _as if_ it's a pandas object then that will work! So everything you learned last term for pandas can still be used in geopandas. Kind of cool, right?\n",
66 | "\n",
67 | "### Designing for Inheritance\n",
68 | "\n",
69 | "Finally, looking back at our example above: what about unicycles? Or tracked vehicles like a tank? This is where _design_ comes into the picture: when we're planning out a family tree for our work we need to be careful about what goes where. And there isn't always a single right answer: perhaps we should distinguish between pedal-powered and motor-powered (in which case unicycles, bicycles and tricycles all belong in the same family)? Or perhaps we need to distinguish between wheeled and tracked (in which case we're missing a pair of classes [wheeled, tracked] between 'vehicle' and 'two-wheel, car, truck')? These choices are tremendously important but often very hard to get right.\n",
70 | "\n",
71 | "OK, that's enough programming theory, let's see this in action..."
72 | ]
73 | },
74 | {
75 | "cell_type": "markdown",
76 | "metadata": {},
77 | "source": [
78 | "## Classes\n",
79 | "\n",
80 | "Here is a simple demonstration of how classes work and why they're useful in programming. Building on last week's 'volume of a sphere' question here's how we'd create and work with a 'shape' class in Python:"
81 | ]
82 | },
83 | {
84 | "cell_type": "code",
85 | "execution_count": 1,
86 | "metadata": {},
87 | "outputs": [
88 | {
89 | "name": "stdout",
90 | "output_type": "stream",
91 | "text": [
92 | "Sphere\n",
93 | "\tVolume is: 4188.79\n",
94 | "\tDiameter is: 20.00\n",
95 | "\n",
96 | "Cube\n",
97 | "\tVolume is: 1000.00\n",
98 | "\tDiameter is: 14.14\n",
99 | "\n",
100 | "Triangular Pyramid\n",
101 | "\tVolume is: 117.85\n",
102 | "\tPhew, no mummies!\n"
103 | ]
104 | },
105 | {
106 | "ename": "Exception",
107 | "evalue": "Unimplmented method error.",
108 | "output_type": "error",
109 | "traceback": [
110 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
111 | "\u001b[0;31mException\u001b[0m Traceback (most recent call last)",
112 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 90\u001b[0m \u001b[0;31m# is not implemented in either triangular pyramid, pyramid,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 91\u001b[0m \u001b[0;31m# or shape, only for spheres.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 92\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"\\tDiameter is: {0:5.2f}\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdiameter\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 93\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
113 | "\u001b[0;32m\u001b[0m in \u001b[0;36mdiameter\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdiameter\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 11\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Unimplmented method error.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 12\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
114 | "\u001b[0;31mException\u001b[0m: Unimplmented method error."
115 | ]
116 | }
117 | ],
118 | "source": [
119 | "from math import pi\n",
120 | "\n",
121 | "class shape(object): # Inherit from base class \n",
122 | " def __init__(self): \n",
123 | " return \n",
124 | " \n",
125 | " def volume(self):\n",
126 | " raise Exception(\"Unimplmented method error.\")\n",
127 | " \n",
128 | " def diameter(self):\n",
129 | " raise Exception(\"Unimplmented method error.\")\n",
130 | " \n",
131 | " def type(self):\n",
132 | " return(self.shape_type)\n",
133 | " \n",
134 | "class cube(shape): # Inherit from shape \n",
135 | " def __init__(self, e):\n",
136 | " self.shape_type = 'Cube'\n",
137 | " self.edge = e\n",
138 | " return\n",
139 | " \n",
140 | " def volume(self):\n",
141 | " return self.edge**3\n",
142 | " \n",
143 | " def diameter(self):\n",
144 | " return (self.edge**2 + self.edge**2)**(1/2)\n",
145 | "\n",
146 | "class sphere(shape): # Inherit from shape \n",
147 | " def __init__(self, r):\n",
148 | " self.shape_type = 'Sphere'\n",
149 | " self.radius = r\n",
150 | " return\n",
151 | " \n",
152 | " def volume(self):\n",
153 | " return (4/3) * pi * self.radius**3\n",
154 | " \n",
155 | " def diameter(self):\n",
156 | " return self.radius*2\n",
157 | "\n",
158 | "class pyramid(shape): # Inherit from shape\n",
159 | " \n",
160 | " has_mummies = True # This is for *all* regular pyramids\n",
161 | " \n",
162 | " def __init__(self, e):\n",
163 | " self.shape_type = 'Regular Pyramid'\n",
164 | " self.edge = e\n",
165 | " return \n",
166 | "\n",
167 | "class t_pyramid(pyramid): # Inherit from pyramid (this is a triangular pyramid)\n",
168 | " \n",
169 | " has_mummies = False # This is for all triangular pyramids\n",
170 | " \n",
171 | " def __init__(self, e):\n",
172 | " self.shape_type = 'Triangular Pyramid'\n",
173 | " self.edge = e\n",
174 | " return \n",
175 | " \n",
176 | " def area(self):\n",
177 | " return (3**(1/2)/4) * self.edge**2\n",
178 | " \n",
179 | " def height(self):\n",
180 | " # https://www.youtube.com/watch?v=ivF3ndmkMsE\n",
181 | " return (6**(1/2) * self.edge/3)\n",
182 | " \n",
183 | " def volume(self):\n",
184 | " # V = 1/3 * A * h\n",
185 | " return (1/3) * self.area() * self.height()\n",
186 | "\n",
187 | "s = sphere(10)\n",
188 | "print(s.type())\n",
189 | "print(\"\\tVolume is: {0:5.2f}\".format(s.volume()))\n",
190 | "print(\"\\tDiameter is: {0:5.2f}\".format(s.diameter()))\n",
191 | "print(\"\")\n",
192 | "\n",
193 | "c = cube(10)\n",
194 | "print(c.type())\n",
195 | "print(\"\\tVolume is: {0:5.2f}\".format(c.volume()))\n",
196 | "print(\"\\tDiameter is: {0:5.2f}\".format(c.diameter()))\n",
197 | "print(\"\")\n",
198 | "\n",
199 | "p = t_pyramid(10)\n",
200 | "print(p.type())\n",
201 | "print(\"\\tVolume is: {0:5.2f}\".format(p.volume()))\n",
202 | "if p.has_mummies is True:\n",
203 | " print(\"\\tMummies? Aaaaaaaaagh!\")\n",
204 | "else:\n",
205 | " print(\"\\tPhew, no mummies!\")\n",
206 | "\n",
207 | "# The error here is deliberate -- note that diameter\n",
208 | "# is not implemented in either triangular pyramid, pyramid, \n",
209 | "# or shape, only for spheres.\n",
210 | "print(\"\\tDiameter is: {0:5.2f}\".format(p.diameter()))\n",
211 | "print(\"\")"
212 | ]
213 | },
214 | {
215 | "cell_type": "markdown",
216 | "metadata": {},
217 | "source": [
218 | "**Have a really good think about how this kind of behaviour is useful!**\n",
219 | "\n",
220 | "### Test Your Understanding \n",
221 | "\n",
222 | "Based on the above examples of classes and methods, I have two challenges for you to implement in the code below:\n",
223 | "1. Try fully-implementing the *regular* pyramid class with a square base that we skipped over above!\n",
224 | "2. Try adding an `area` method that returns the surface area of each shape and then add that information to the output below."
225 | ]
226 | },
227 | {
228 | "cell_type": "code",
229 | "execution_count": null,
230 | "metadata": {},
231 | "outputs": [],
232 | "source": [
233 | "from math import pi\n",
234 | "\n",
235 | "class shape(object): # Inherit from base class \n",
236 | " def __init__(self): \n",
237 | " return \n",
238 | " \n",
239 | " def volume(self):\n",
240 | " raise Exception(\"Unimplmented method error.\")\n",
241 | " \n",
242 | " def diameter(self):\n",
243 | " raise Exception(\"Unimplmented method error.\")\n",
244 | " \n",
245 | " def type(self):\n",
246 | " return(self.shape_type)\n",
247 | " \n",
248 | "class cube(shape): # Inherit from shape \n",
249 | " def __init__(self, e):\n",
250 | " self.shape_type = 'Cube'\n",
251 | " self.edge = e\n",
252 | " return\n",
253 | " \n",
254 | " def volume(self):\n",
255 | " return self.edge**3\n",
256 | " \n",
257 | " def diameter(self):\n",
258 | " return (self.edge**2 + self.edge**2)**(1/2)\n",
259 | "\n",
260 | "class sphere(shape): # Inherit from shape \n",
261 | " def __init__(self, r):\n",
262 | " self.shape_type = 'Sphere'\n",
263 | " self.radius = r\n",
264 | " return\n",
265 | " \n",
266 | " def volume(self):\n",
267 | " return (4/3) * pi * self.radius**3\n",
268 | " \n",
269 | " def diameter(self):\n",
270 | " return self.radius*2\n",
271 | "\n",
272 | "class pyramid(shape): # Inherit from shape\n",
273 | " \n",
274 | " has_mummies = True # This is for *all* regular pyramids\n",
275 | " \n",
276 | " def __init__(self, e):\n",
277 | " self.shape_type = 'Regular Pyramid'\n",
278 | " self.edge = e\n",
279 | " return \n",
280 | "\n",
281 | "class t_pyramid(pyramid): # Inherit from pyramid (this is a triangular pyramid)\n",
282 | " \n",
283 | " has_mummies = False # This is for all triangular pyramids\n",
284 | " \n",
285 | " def __init__(self, e):\n",
286 | " self.shape_type = 'Triangular Pyramid'\n",
287 | " self.edge = e\n",
288 | " return \n",
289 | " \n",
290 | " def area(self):\n",
291 | " return (3**(1/2)/4) * self.edge**2\n",
292 | " \n",
293 | " def height(self):\n",
294 | " # https://www.youtube.com/watch?v=ivF3ndmkMsE\n",
295 | " return (6**(1/2) * self.edge/3)\n",
296 | " \n",
297 | " def volume(self):\n",
298 | " # V = 1/3 * A * h\n",
299 | " return (1/3) * self.area() * self.height()\n",
300 | "\n",
301 | "# How would you test these changes?"
302 | ]
303 | },
304 | {
305 | "cell_type": "markdown",
306 | "metadata": {},
307 | "source": [
308 | "### Credits!\n",
309 | "\n",
310 | "#### Contributors:\n",
311 | "The following individuals have contributed to these teaching materials: \n",
312 | "- [James Millington](https://github.com/jamesdamillington)\n",
313 | "- [Jon Reades](https://github.com/jreades)\n",
314 | "\n",
315 | "#### License\n",
316 | "The content and structure of this teaching project itself is licensed under the [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 license](https://creativecommons.org/licenses/by-nc-sa/4.0/), and the contributing source code is licensed under [The MIT License](https://opensource.org/licenses/mit-license.php).\n",
317 | "\n",
318 | "#### Acknowledgements:\n",
319 | "Supported by the [Royal Geographical Society](https://www.rgs.org/HomePage.htm) (with the Institute of British Geographers) with a Ray Y Gildea Jr Award.\n",
320 | "\n",
321 | "#### Potential Dependencies:\n",
322 | "This notebook may depend on the following libraries: None"
323 | ]
324 | }
325 | ],
326 | "metadata": {
327 | "anaconda-cloud": {},
328 | "kernelspec": {
329 | "display_name": "Python 3",
330 | "language": "python",
331 | "name": "python3"
332 | },
333 | "language_info": {
334 | "codemirror_mode": {
335 | "name": "ipython",
336 | "version": 3
337 | },
338 | "file_extension": ".py",
339 | "mimetype": "text/x-python",
340 | "name": "python",
341 | "nbconvert_exporter": "python",
342 | "pygments_lexer": "ipython3",
343 | "version": "3.7.6"
344 | }
345 | },
346 | "nbformat": 4,
347 | "nbformat_minor": 4
348 | }
349 |
--------------------------------------------------------------------------------
/notebook-14-style-and-summary.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Wrapping Up (A Matter of Style)"
8 | ]
9 | },
10 | {
11 | "cell_type": "markdown",
12 | "metadata": {},
13 | "source": [
14 | "### Lesson Content \n",
15 | "\n",
16 | "- Style\n",
17 | " - Why style matters\n",
18 | " - Python style\n",
19 | "- Why???\n",
20 | " - Why did I enter this world of pain?\n",
21 | " - Where am I going?\n",
22 | "\n",
23 | "Welcome to _last_, Code Camp notebook! In this lesson we'll cover a number of things that don't fit anywhere else but which are essential to getting you on your way as a beginner programmer in Geocomputation and Spatial Data Science.\n"
24 | ]
25 | },
26 | {
27 | "cell_type": "markdown",
28 | "metadata": {},
29 | "source": [
30 | "## Style\n",
31 | "\n",
32 | "As with any other type of creative writing -- make no mistake, writing code is a creative activity! -- different people have different styles of writing. Extending the language metaphor, different programming languages have different styles as well and you can often tell what programming language someone learned first (or best) by the way they write their code in _any_ programming language. While it's _nice_ to work in the style that is considered 'best' in each language, what is more important is that you are consistent in how you write: that way, another person reading your code can get used to the way that you write and make better sense of what you're trying to do.\n",
33 | "\n",
34 | "### Variable Names\n",
35 | "\n",
36 | "So here are some common styles for variable names:\n",
37 | "\n",
38 | "| Variable Name | Language | Rationale |\n",
39 | "|:--------------|:---------|:----------|\n",
40 | "| my_variable | Python, Perl | Underscores (\"\\_\") make it easy to read 'words' in a variable name |\n",
41 | "| MyVariable | Java | Mixed Caps makes for shorter variable names that are easier to type |\n",
42 | "| my.variable | R | R allows full-stops in variable names, no other language does |\n",
43 | "\n",
44 | "### Comments\n",
45 | "\n",
46 | "Commenting code that they've written is something that programmers normally hate doing, because it's not _doing_ work after all... until you have to try to understand what you've done and why two days or two months later. You'd be surprised how quickly your memory of _why_ you did something one way, and not another, will fade and, with it, the risks that you were trying to avoid. It doesn't matter how clear your variable names are (_e.g._ `url_to_get` or `do_not_do_this`) you will eventually forget what is going on. Comments help you to avoid this, and they also allow you to write more concise, efficient code because your variable and function names don't have to do all the heavy lifting of explanation as well!\n",
47 | "\n",
48 | "How to comment well:\n",
49 | "\n",
50 | "- There are as many ways of commenting well as there are ways of writing code well -- the point is to be clear and consistent.\n",
51 | "- You might think of adding a _short_ comment on the end of a line of code if that line is doing something complex and not immediately obvious:\n",
52 | "```python\n",
53 | "import random\n",
54 | "random.seed(123456789) # For reproducibility\n",
55 | "```\n",
56 | "- You might think of adding a one-line or multi-line comment at the start of a _block_ of code that is achieving something specific:\n",
57 | "```python\n",
58 | "# Create a new data frame to \n",
59 | "# hold the percentage values\n",
60 | "# and initialise it with only\n",
61 | "# the 'mnemonic' (i.e. GeoCode)\n",
62 | "d_pct = pd.concat(\n",
63 | " [d[t]['mnemonic']], \n",
64 | " axis=1, \n",
65 | " keys=['mnemonic'])\n",
66 | "```\n",
67 | "- You might think of using some formatting for a long _section_ of code that forms a key stage of your analysis:\n",
68 | "```python\n",
69 | "##############################\n",
70 | "# This next section deals with\n",
71 | "# extracting data from the \n",
72 | "# London Data Store and tidying\n",
73 | "# up the values so that they work\n",
74 | "# with pandas.\n",
75 | "##############################\n",
76 | "```\n",
77 | "- Finally, you can also use multi-line strings as a kind of comment format:\n",
78 | "```python\n",
79 | "\"\"\"\n",
80 | "=========================================================\n",
81 | "Extract pd2-level data from the price paid medians data\n",
82 | "=========================================================\n",
83 | "\"\"\"\n",
84 | "```\n",
85 | "\n",
86 | "Many programmers will mix all of these different styles together, but they will do so in a consistent way: the more complex the formatting and layout of the comment, the larger the block of code to which the comment applies.\n",
87 | "\n",
88 | "### Why Style Matters\n",
89 | "\n",
90 | "As we said at the start, style matters because it makes your code _intelligible_ to you, and to others. For professional programmers that is incredibly important because almost no one works on their own any more: applications are too complex for one programmer to get very far. So programmers need others to be able to read and interpret the code that they've written. As well, you might be asked to (or want to) go back to make changes to some code that you wrote some time ago -- if you've not commented your code or used a consistent style you're much more likely to break things. You might have heard of the Facebook motto \"Move fast and break things\" -- that doesn't refer to Facebook's web application, it refers to _paradigms_. \n",
91 | "\n",
92 | "\n"
93 | ]
94 | },
95 | {
96 | "cell_type": "markdown",
97 | "metadata": {},
98 | "source": [
99 | "## Why???\n",
100 | "\n",
101 | "We'd like to wrap up Code Camp by revisiting the question of 'why' -- why invest a lot of time and energy in learning to code? There are a host of reasons, but we'd like to pick out a few of the ones that we think are most important for undergraduate students:\n",
102 | "\n",
103 | "1. Coding teaches logical thinking: right from the start, coding reinforces logical thinking. A computer will try to do _exactly_ what you say, regardless of how inappropriate or illogical that statement might be. To become a better programmer you will need to develop a good idea of what you're trying to achieve, the steps involved in getting there, and you'll need an eye for detail as well since `my_variable` is not the same as `myvariable`.\n",
104 | "\n",
105 | "2. Coding teaching abstraction: this might seem a bit of a strange point coming right after the one about an 'eye for detail', but it's true! Writing code is not just about solving for one row of data, or 1,000, it's about solving the problem in a _general_ way: mapping and analysis tools like [PySAL](http://pysal.readthedocs.io/en/latest/) or [Folium](https://github.com/python-visualization/folium) weren't create to solve one mapping or spatial analysis problem, but a _range_ of them! Well-written code is easy to adapt and re-use (and in follow-on classes we'll look at how to encapsulate useful snippets of code in functions so that it's even easier) because you aren't just thinking about \"I have to process population data for 20 English cities\" but about \"How do I manage population data in many different formats about cities anywhere in the world?\". You also need to have, in your head, a high-level _model_ of how the computer is processing the data so that you can frame your solution appropriately. Again, that's abstraction.\n",
106 | "\n",
107 | "3. Coding is scalable: there's a lot of 'start-up cost' involved with a coding solution, but once you get over this hump it's highly scalable. One of us has been working on a Machine Learning problem to try to predict gentrification in London from 2011-2021 using the period from 2001-2011. This involves not only downloading more than 30 data sets from the [ONS/NOMIS](https://www.nomisweb.co.uk/) and the [London Data Store](https://data.london.gov.uk/) in order to build a model using 168 variables, it also involves making difficult choices about how to handle highly skewed data which makes accurate prediction much more difficult. Using Python, we can try _each_ of the options available and see which one work best, even though this involves reprocessing 168 variables, re-mapping 15 maps, and re-outputting 3 data files for use in QGIS. \n",
108 | "\n",
109 | "4. Coding is replicable: the 'crisis of replicability' has enormous implications for medicine, psychology, policy, planning, and environmental science. Without the ability to check how data was collected, processed, and analysed we are basically 'flying blind'. You might have heard about the [Excel error used to justify austerity](http://theconversation.com/economists-an-excel-error-and-the-misguided-push-for-austerity-13584) or about the [crisis in replicability](http://www.newyorker.com/tech/elements/the-crisis-in-social-psychology-that-isnt)? Or that only [positive trials](https://www.scientificamerican.com/article/trial-sans-error-how-pharma-funded-research-cherry-picks-positive-results/) of a drug are published? These are parts of a larger debate within the sciences about how much of the scientific process should be conducted 'in the open' -- is it just data? or is it code as well? Increasingly, journals and scientists are arguing for it to be both.\n",
110 | "\n",
111 | "5. Coding is empowering: if you stick with it you'll probably find that, one afternoon, you start coding around 3pm and suddenly realise that it's dark out, you're hungry, and you've been coding non-stop all the way to 9pm! We don't recommend this as everyday practice, but it's a sign of how engaging coding can be, how easy it is to enter '[flow](https://en.wikipedia.org/wiki/Flow_(psychology))', and how exciting it can be to solve real problems and achieve a real output. Ultimately, we think that 'bending' a computer to your will -- and, as importantly, figuring out how to frame a human problem in logical terms that a computer can handle -- is a tremendously empowering experience: that day you click 'run' or hit 'enter' and the computer chews away at a data set for 10 seconds or 10 hours **and** comes back to you with a solution to your problem is profoundly thrilling because it means that you've done work that _works_ on multiple levels simultaneously (it works at the fine-scale of syntax _and_ at the conceptual level of design).\n",
112 | "\n",
113 | "We hope you'll get there and we hope you'll stick it out until you do! If you invest the time and effort then we're pretty confident that _any_ of you can become a competent programmer: but don't take our word for it, ask some of this year's Third Years what they think! And here is some more food for thought:\n",
114 | "\n",
115 | "1. The National Academy's [Understanding the Changing Planet: Strategic Directions for the Geographical Sciences](https://www.nap.edu/read/12860/chapter/18#118)\n",
116 | "2. The RGS's [Data Skills in Geography](http://www.rgs.org/OurWork/Schools/Data+skills+in+geography/Data+skills+in+geography.htm) (especially [Quantitative Skills in Geography](http://www.rgs.org/OurWork/Research+and+Higher+Education/Learning+teaching+and+research+in+higher+education/Quantitative+Teaching+and+Learning+in+Geography/Quantitative+Methods+in+Geography.htm)).\n",
117 | "\n",
118 | "\n",
119 | "**Good luck!**"
120 | ]
121 | },
122 | {
123 | "cell_type": "markdown",
124 | "metadata": {},
125 | "source": [
126 | "### Credits!\n",
127 | "\n",
128 | "#### Contributors:\n",
129 | "The following individuals have contributed to these teaching materials: \n",
130 | "- [James Millington](https://github.com/jamesdamillington)\n",
131 | "- [Jon Reades](https://github.com/jreades)\n",
132 | "\n",
133 | "#### License\n",
134 | "The content and structure of this teaching project itself is licensed under the [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 license](https://creativecommons.org/licenses/by-nc-sa/4.0/), and the contributing source code is licensed under [The MIT License](https://opensource.org/licenses/mit-license.php).\n",
135 | "\n",
136 | "#### Acknowledgements:\n",
137 | "Supported by the [Royal Geographical Society](https://www.rgs.org/HomePage.htm) (with the Institute of British Geographers) with a Ray Y Gildea Jr Award.\n",
138 | "\n",
139 | "#### Potential Dependencies:\n",
140 | "This notebook may depend on the following libraries: None"
141 | ]
142 | }
143 | ],
144 | "metadata": {
145 | "kernelspec": {
146 | "display_name": "Python 3",
147 | "language": "python",
148 | "name": "python3"
149 | },
150 | "language_info": {
151 | "codemirror_mode": {
152 | "name": "ipython",
153 | "version": 3
154 | },
155 | "file_extension": ".py",
156 | "mimetype": "text/x-python",
157 | "name": "python",
158 | "nbconvert_exporter": "python",
159 | "pygments_lexer": "ipython3",
160 | "version": "3.7.6"
161 | }
162 | },
163 | "nbformat": 4,
164 | "nbformat_minor": 4
165 | }
166 |
--------------------------------------------------------------------------------
|