{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Notebook-9: Loops and Iteration" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### Lesson Content \n", "\n", "- Two Ways to Iterate\n", "- While Statement\n", " - While Loop\n", " - Break and Continue\n", " - Loop over a list \n", "- For Loop \n", "- Code (Applied Geo-example)\n", "\n", "In this lesson we cover the concept of *iteration*, which is basically the idea of *repeating the same set of instructions until a certain condition is met*. This sequence of instructions is also called a *loop*. \n", "\n", "This is an extremely important and powerful concept, as it allows us to finally *automate* tasks! Remember that a fundamental feature of programmers is that they are *lazy* (refresh [Larry Wall's *\"Three Virtues\"*](www.threevirtues.com) that we saw in the first notebook!). The more you can delegate to the machine and avoid repeating boring and repetitive tasks yourself, the better!\n", "\n", "Of course, as the fantastic [Randall Munroe](https://xkcd.com/about/) likes to remind us, reality sometimes challenges this idea...\n", "![xkcd-automation](img/automation.png)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Two Ways to Iterate \n", "\n", "The two most common ways to iterate (or repeat) a set of commands are the _while loop_ and the _for loop_. That's why in Python the words `while`, `for` (and `in`) are reserved words and you can't use them for variable names. \n", "\n", "The video below is a nice (if slightly scary) intro to each of the approaches; you might remember them the next time you're in the gym:\n", "\n", "[![Demonstration of While and For Loops](http://img.youtube.com/vi/9AJ0uoxtdCQ/0.jpg)](https://www.youtube.com/watch?v=9AJ0uoxtdCQ)\n", "\n", "\n", "The 'right' approach you use usually depends on [what sort of data structure you are iterating over](http://stackoverflow.com/questions/920645/when-to-use-while-or-the-for-in-python) and [whether we know before we start how many times we want to repeat the commands](https://www.reddit.com/r/learnprogramming/comments/3bqzpf/python_for_loops_vs_while_loops/). That might sound a little vague at this point, but come back and have a think about it once you have a better understanding of how loops work.\n", "\n", "So let's first see how while loops work, then we'll look at actual loops in Python." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# WHILE Loops\n", "\n", "Remember `if` statements? When the Python interpreter finds an `if` statement in your code, it checks if the specified condition evalutes to `True`. If the condition is `True` then it runs the remainder of the *indented* code block following the `if`. The 'true' block of code is only run once.\n", "\n", "Using the `while` statement, the 'true' block is run for as long as the condition evalutes to `True`. So if the statment continues to evaluation to `True` then the block of code is run again and again and again and again... and again until some stopping condition is reached (usually the while condition becomes `False` for 'some reason').\n", "\n", "This allows us to finally do some interesting stuff in code:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "counter = 1 # Starting condition: counter is at 1\n", "\n", "print(\"The while loop is ready to begin.\")\n", "\n", "while counter <= 10: # Test the condition\n", " print(\"The counter is at: \" + str(counter))\n", " counter += 1 # Increment the counter\n", " \n", "print(\"The while loop has ended.\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If that looks confusing, don't worry! It's perfectly normal as that's your first example of *iteration*. \n", "\n", "Let's take a deeper look:\n", "- First, we defined a variable `counter` and we _initialised_ it with a value of 1. \n", "- Then we used the `while` statement to check if the value of `counter` was less than or equal to 10. \n", "- Since that condition evaluated to `True`, we printed the value of `counter` and then added 1 to the `counter`. \n", "- The indendented block of code was then run again and again by Python *until* the `while` statement returned a `False` (this happened because the value of counter went all the way to 11).\n", "- After that Python simply continued to execute the code outside the `while` *block* (i.e. the last line of non-indented code)\n", "\n", "The fundamental idea is thus: *while this condition holds, repeat the instructions in the indented code block*.\n", "\n", "Many beginner programmers get confused by the fact that the value of `counter` is increasing without the code advancing to the point where in prints out 'The while loop has ended'. Remeber: we are starting the instruction from the beginning of the *while block*. So the first time that we hit it, `counter` has value of 1 because this is what we set it to _outside_ of the *while loop*. \n", "\n", "But the _second_ time the while conditional is evaluted, `counter` has been incremented to 2 because the last line of the `while` block is `counter += 1`! So we increased its value while inside the *while block*. At the beginning of the third iteration it will have a value of 3, while at its end it will be incremented to 4. And so on...\n", "\n", "_CAVEAT_: pay attention to how you write your `while` *loops*! They can potentially run forever (for as long as the condition they are evaluating is `True`) maxing out your machine's memory. For example:\n", "\n", "```python\n", "# don't run this!\n", "# or if you do, save everything first\n", "# and then be prepared to stop the code execution manually \n", "# (usually by pressing CTRL+D or Cmd+D)\n", "# in the terminale/console\n", "while True:\n", " print(\"Forever Loop! Yeeee!\")\n", "```\n", "\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### A challenge for you!\n", "\n", "Complete the code below to run a while loop that prints only odd numbers under 10. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "solution2": "hidden", "solution2_first": true }, "outputs": [], "source": [ "otherCounter = 1\n", "\n", "while ??? <= ???:\n", " print(\"This is an odd number: \" + str(???))\n", " otherCounter += 2" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "solution2": "hidden" }, "outputs": [], "source": [ "otherCounter = 1\n", "\n", "while otherCounter <= 10:\n", " print(\"This is an odd number: \" + str(otherCounter))\n", " otherCounter += 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also run `while` loops _decrementally_ until they meet a certain condition (run the code block to see how):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "myThirdCounter = 10\n", "\n", "while myThirdCounter >= 0:\n", " print(\"Decrementing counter: \" + str(myThirdCounter))\n", " myThirdCounter -= 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Break and Continue\n", "\n", "To prematurely interrupt the execution of a while loop (before the `while` condition become false) you can use the **`break`** statement" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "myFourthCounter = 1\n", "\n", "while myFourthCounter < 10:\n", " \n", " print(str(myFourthCounter))\n", " myFourthCounter += 1\n", " \n", " if myFourthCounter == 5:\n", " print(\"Time to escape this madness!\")\n", " break" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Nesting Conditions\n", "\n", "That last example shows that you can 'nest' an if (`if myFourthCounter`) inside of a `while` loop -- this approach allows us to add much more complex logic to our code than we could before. Here's an example, but see if you can figure out what it will print out (and how) _before_ you run the code block! " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "i=0\n", "while True:\n", " i += 1\n", "\n", " if i % 2 != 0:\n", " print( str(i) + \" is odd.\")\n", " if i == 9:\n", " print(\"\\t i is 9\")\n", " else:\n", " print(\"\\t i is not 9\")\n", " else:\n", " print( str(i) + \" is even.\")\n", " if i % 4 == 0:\n", " print(\"\\t i is divisible by 4.\")\n", " elif i == 8:\n", " print(\"\\t i is 8\")\n", "\n", " if i == 21:\n", " break" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In terms of explanation:\n", "\n", "1. Python is going to keep running the code (`while True`) until it is told to stop by a `break` (which happens at `i == 21`). In fact, if you remove the `break` then you will crash jupyter because the computer will print out every even number to infinity (which the computer can’t handle because it runs out of memory).\n", "\n", "2. 'Inside' the `while` loop there is a _main_ `if`/`else` block:\n", "```if i % 2 != 0:\n", " ... do something with odd numbers...\n", "else:\n", " ... do something else with even numbers...\n", "```\n", "\n", "3. 'Inside' the odd numbers section we now have a second `if`/`else` block:\n", "```if i == 9:\n", " ... do something if the odd number is 9...\n", "else:\n", " ... do something else if the odd number is not 9...\n", "```\n", "\n", "4. And it’s a _similar_ story in the even numbers section:\n", "```if i % 4 == 0:\n", " ... do something if the even number is divisible by 4...\n", "elif i == 8\n", " ... do something else if the even number is 8...\n", "```\n", "We'll not give you the answer to how to print out `WOOT` in place of 7 below, but at this point you all the clues you need. It’s the concept that is the hard part and following what’s going on when you start to nest conditions inside of conditions. We can talk this through more if anyone needs more help getting to grips with this!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `i` and `j` in Loops\n", "\n", "If you want to skip to the next iteration then you can _continue_ the exection of a `while` loop using the `continue` statement. In the following example we are going to skip all even numbers and print `WOOT!` if we hit a lucky 7 (or any number divisible by 7). We'll `break` out of the loop after hitting the 21st iteration.\n", "\n", "One other thing to notice is that we've switched from _looooong_ counter names like `myFourthCounter` to `i`. A common programming trick (which is well-known and so actually _increases_ the legibility of your code to others) is to use `i` and `j` for counters in loops (you can add `k` if you ever need a third level loop)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# So use i and j as counters because \n", "# this is a stylistic convention and \n", "# helps you to to write more concise code\n", "# (and be constructively lazy).\n", "\n", "i = 0\n", "\n", "while True:\n", " \n", " i += 1\n", " \n", " if i % 2 != 0:\n", " print(i)\n", " else:\n", " continue\n", " \n", " if i % 7 == 0:\n", " print(\"WOOT!\")\n", " \n", " if i == 25:\n", " break\n", " \n", "print(\"Done!\")" ] }, { "cell_type": "markdown", "metadata": { "solution2": "hidden", "solution2_first": true }, "source": [ "How would you change the code above so that it printed _only_ the odd number _or_ 'WOOT!', but not both? In other words, change the code so that it prints:\n", "\n", "`\n", "1\n", "3\n", "5\n", "WOOT!\n", "9\n", "11\n", "13\n", "15\n", "17\n", "19\n", "WOOT!\n", "`" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "solution2": "hidden" }, "outputs": [], "source": [ "i = 0\n", "\n", "while True:\n", " \n", " i += 1\n", " \n", " if i % 2 != 0:\n", " if i % 7 == 0:\n", " print(\"WOOT!\")\n", " else:\n", " print(i)\n", " \n", " if i == 25:\n", " break\n", " \n", "print(\"Done!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### A challenge for you!\n", "\n", "Now, replace the `???` in the code below and use it to print only even numbers less than 22." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "solution2": "hidden", "solution2_first": true }, "outputs": [], "source": [ "i = 0\n", "while True:\n", " i += 1\n", " \n", " if i % 2 != 0:\n", " continue\n", " if i == ???:\n", " break\n", " print(???)\n", "\n", "print(\"Done!\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "solution2": "hidden" }, "outputs": [], "source": [ "i = 0\n", "while True:\n", " i += 1\n", " \n", " if i % 2 != 0:\n", " continue\n", " if i == 22:\n", " break\n", " print(i)\n", "\n", "print(\"Done!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Iterating over a List\n", "\n", "What you just saw with the `while` statement is a way of iterating: a way of repeating a certain set of instruction until a given condition is met. We can use to our advantage not only to print stuff, but also to '_iterate over_' the elements in a list:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# remember our friends, the british computer scientists?\n", "britishCompList = [\"babbage\", \"lovelace\", \"turing\"]\n", "\n", "# this is the condition python is going to check against\n", "stoppingCondition = len(britishCompList)\n", "\n", "counter = 1\n", "while counter < stoppingCondition:\n", " print(britishCompList[counter] + \" was a british computer scientist\")\n", " # don't forget to increment the counter!!!\n", " counter += 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wow, lot of stuff in that chunk of code, eh? Well, once again, take a deep breath and go through it line by line.\n", "\n", "The important bits are:\n", "- notice that this time we used the `len` of `britishCompList` as stopping condition, instead of specifying ourselves a number.\n", "- we accessed the items in the list with a regular index, like we have done in the past. The difference is that this time the index was the variable `counter`, as at each iteration `counter` assumes the value of 0, 1 ... until the stopping condition is met. This is equivalent to writing :\n", "\n", "```python\n", "print(britishCompList[0]) # on the first iteration\n", "print(britishCompList[1]) # on the second iteration\n", "```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### A challenge for you!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But wait a second... what about the great Babbage? Why isn't his name displayed? Certainly not because he's not worth a mention! Can you spot the reason why the iteration skipped him? Can you fix the code to include Babbage?\n", "\n", "*Hint*: check (using `print`) the values of `counter` and `britishCompList`. What is the condition we are asking Python to use?" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "solution2": "hidden", "solution2_first": true }, "outputs": [], "source": [ "#your code here" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "solution2": "hidden" }, "outputs": [], "source": [ "counter = 0 # To include Babbage, we need to start counter from 0\n", "while counter < len(britishCompList):\n", " print(britishCompList[counter] + \" was a british computer scientist\")\n", " # don't forget to increment the counter!!!\n", " counter += 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Other Overlooked Computers\n", "\n", "If you think that being skipped over in the loop above was tough for old Babbage, then perhaps you might be interested to hear about the history of the [NASA 'computers' who helped put a man on the moon](https://www.washingtonpost.com/lifestyle/style/the-nearly-forgotten-story-of-the-black-women-who-helped-land-a-man-on-the-moon/2016/09/12/95f2d356-7504-11e6-8149-b8d05321db62_story.html) and are only getting a film made about them in 2016.\n", "\n", "[![The hidden history of NASA's computers](http://img.youtube.com/vi/RK8xHq6dfAo/0.jpg)](http://www.youtube.com/watch?v=RK8xHq6dfAo)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### A challenge for you!\n", "\n", "Complete the following code:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "solution2": "hidden", "solution2_first": true }, "outputs": [], "source": [ "counter = ???\n", "nonBritishProgrammers = [\"Torvald\", \"Knuth\", \"Swartz\"]\n", "stoppingCondition = len(???)\n", "\n", "while counter < stoppingCondition :\n", " print(\"This is a computer genius too: \" + nonBritishProgrammers[counter])\n", " # always remember to increment the counter!!!\n", " counter += 1" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "solution2": "hidden" }, "outputs": [], "source": [ "counter = 0\n", "nonBritishProgrammers = [\"Torvald\", \"Knuth\", \"Swartz\"]\n", "stoppingCondition = len(nonBritishProgrammers)\n", "\n", "while counter < stoppingCondition :\n", " print(\"This is a computer genius too: \" + nonBritishProgrammers[counter])\n", " # always remember to increment the counter!!!\n", " counter += 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*CAVEAT*: An important condition to remember when iterating over a list is thus that lists are *zero-indexed*! If if you start you counter from 1 you will certainly miss the first item in the list (which has an index of 0). \n", "\n", "But watch out! There's more:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Another challenge for you!\n", "\n", "Can you guess why I needed to subtract -1 to the list's `len`? \n", "\n", "_[Hint: Check the condition again. Is the same as before? (Run the code below before continuing)]_" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "solution2": "hidden", "solution2_first": true }, "outputs": [], "source": [ "counter = 0\n", "nonBritishProgrammers = [\"Torvald\", \"Knuth\", \"Swartz\"]\n", "stoppingCondition = len(nonBritishProgrammers) -1\n", "\n", "while counter <= stoppingCondition :\n", " print(\"These are geniuses too! \" + nonBritishProgrammers[counter])\n", " # always remember to increment the counter!!!\n", " counter +=1" ] }, { "cell_type": "markdown", "metadata": { "solution2": "hidden" }, "source": [ "We can see that from the code above the while condition is slightly different:\n", " `while counter < stoppingCondition :` versus `while counter <= stoppingCondition`\n", "Because `len` counts the number of elements, if we use `<=` we will access the variable `counter` at each iteration `counter` with the value of 0, 1, 2, 3. This would result in an `indexError` since we only have three variables." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# FOR Loop\n", "\n", "We've just seen that we can use a `while` loop to iterate over a list, but it's kind of clunky and inelegant. All those counters and things makes for a lot of extra typing, especially when you consider how terse Python's language usually is! Surely there must be another way?\n", "\n", "You guessed right, my friend! Let me introduce you to the `'for ... in:'` statement:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "for programmer in britishCompList:\n", " print(programmer)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see, the for loop statement is much more concise: you simply tell Python to repeat a certain instruction (print the list item in this example) *for EVERY ITEM in A SEQUENCE*. The sequence here is the list of British computer scientists `britishCompList` created in the code block above.\n", "\n", "Now, Python will stop automatically when the sequence is finished without you having to worry about specifying the stopping condition (which you have to do when using a `while` loop).\n", "\n", "Notice also that we didn't have to initialise the `counter` value!\n", "\n", "So, the biggest difference between a `while` and a `for` loop is thus not _merely_ stylistic, it's also conceptual!\n", "\n", "Let's recap with another example:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "# WHILE LOOP\n", "whileCounter = 0\n", "myList = [0,1,2,3,4]\n", "stoppingCondition = len(myList)\n", "while whileCounter < stoppingCondition:\n", " print(\"Element number: \" + str(myList[whileCounter]))\n", " whileCounter +=1 " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "# FOR LOOP\n", "for element in myList:\n", " print(\"Element number: \" + str(element))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "SIDENOTE: See how the value of `myList[whileCounter]` and that of `element` in the two loops are the same? That's because Python is doing the indexing job for you behind the scenes." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### A challenge for you!\n", "\n", "Print only the odd numbers in the list. HINT: remember the modulo operator?" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "solution2": "hidden", "solution2_first": true }, "outputs": [], "source": [ "numbers = [1,2,3,4,5,6,7,8,9,10]\n", "for n in numbers:\n", " if (???):\n", " print(n)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "solution2": "hidden" }, "outputs": [], "source": [ "numbers = [1,2,3,4,5,6,7,8,9,10]\n", "for n in numbers:\n", " if (n % 2 != 0):\n", " print(n)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, as we are *lazy* programmers, let's repeat the above example combining the `range` function with a `for` loop. It will save us the hassle of typing all those numbers!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for i in range(10):\n", " if (i % 2 != 0):\n", " print(i)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cool, we have seen how to iterate over a list, but what about a dictionary? Well, if you remember we said that you might think of a dictionary as a kind of list where each element isn't indexed by an integer, but rather by a *unique identifier (key)*.\n", "\n", "Hence, as with lists where we iterate over the indexes, with dictionaries we are going to iterate over the keys!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "programmers = {\n", " \"Charles\": \"Babbage\",\n", " \"Ada\": \"Lovelace\",\n", " \"Alan\":\"Turing\"\n", "}\n", "for k in programmers:\n", " print(k)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*NOTE:* I've used the variable `k`. This is simply an arbitrary word that I've choosen and not some kind of special variable. You could have used `anyRandomNameForWhatMatters`. \n", "\n", "What if you want to retrieve the values? In that case you should use not only two variables, (the first for the keys and the second for the values) but also invoke the method `items()` on the dictionary, like so:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for k,v in programmers.items():\n", " print(\"this is the value: \\'\" + v + \"\\' for the key: \\'\" + k +\"\\'\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### A Challenge for you!\n", "\n", "Iterate over the GeoJSON marker and print its \"properties\"." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "solution2": "hidden", "solution2_first": true }, "outputs": [], "source": [ "KCL_marker = {\n", " \"type\": \"Feature\",\n", " \"properties\": {\n", " \"marker-color\": \"#7e7e7e\",\n", " \"marker-size\": \"medium\",\n", " \"marker-symbol\": \"\",\n", " \"name\": \"KCL\"\n", " },\n", " \"geometry\": {\n", " \"type\": \"Point\",\n", " \"coordinates\": [\n", " -0.11630058288574219,\n", " 51.51135999349117\n", " ]\n", " }\n", " }\n", "for ???,v in KCL_marker[\"???\"].items():\n", " print(\"KCL_marker has a property: '\" + ??? + \"' for the key: \" + k)\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "solution2": "hidden" }, "outputs": [], "source": [ "KCL_marker = {\n", " \"type\": \"Feature\",\n", " \"properties\": {\n", " \"marker-color\": \"#7e7e7e\",\n", " \"marker-size\": \"medium\",\n", " \"marker-symbol\": \"\",\n", " \"name\": \"KCL\"\n", " },\n", " \"geometry\": {\n", " \"type\": \"Point\",\n", " \"coordinates\": [\n", " -0.11630058288574219,\n", " 51.51135999349117\n", " ]\n", " }\n", " }\n", "for k,v in KCL_marker[\"properties\"].items():\n", " print(\"KCL_marker has a property: '\" + v + \"' for the key: \" + k)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Very good! Let's summarise some facts about loops:\n", "\n", "- you can increment the counter\n", "- but also decrement it (effectively counting down!)\n", "- the increment doesn't need to be 1 every the time (you can increment by 2, 50, whatever..)\n", "- don’t forget to to indent the *block* of code after the colon!\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Code (Applied Geo-example)\n", "\n", "The geo-excercise I'll give you this time is a real-world problem that you might face one day in your career as geospatial professionals. In _principle_ it is possible to work out everything below based on what we've done up to this point; however, this exercise is also _hard_ since it is a big jump conceptually that mixes up the ideas in a new way. So if you can't quite make out the answer don't worry, just try to understand the concepts above and see if you can solve _parts_ of the problem. \n", "\n", "Let's say a colleague of yours used a GPS to survey at regular intervals the dispersion of pollutants in a patch of terrain. Unfortunately, after a good start they forgot to record all the remaining points! \n", "\n", "But that's not a terrible problem, as the transect has a perfect West-East orientation and direction, and all the points are spaced by a the same value dX (short for 'delta-X', the change in X between each point) of 0.03 degrees longitude, i.e.:\n", "\n", "(0.0102, 51.592)-----(X+dX,Y)-----(X+2dX,Y)-----(X+3dX,Y)--->\n", " \n", "Using what we've seen so far, try to create a `GeoJSON featureCollection` of points. To give you a head start, I've provided some *scaffolding*.\n", "\n", "HINT: Being the skilled geographer that you are, you immediately realise that actually you've got all the coordinates that you need, even for the missing points (i.e. the latitude values will remain constant...)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# define a new featureCollection: it is basically a very fancy dictionary\n", "# to which we are going to add new 'features' (which are points on a map\n", "# but represented as *data* by a dictionary). We need to add one feature\n", "# at a time when building our transect...\n", "# initial coordinate list\n", "init_coords = [-0.0200, 51.592]\n", "# dX delta \n", "dx = 0.03\n", "gap = 0\n", "\n", "transect = {\n", " \"type\": \"FeatureCollection\",\n", " \"features\": [\n", " {\n", " \"type\": \"Feature\",\n", " \"properties\": {},\n", " \"geometry\": {\n", " \"type\": \"Point\",\n", " \"coordinates\": init_coords\n", " }\n", " }\n", "# -------------------------------------------------------------\n", "# here is where the remaining three points have to be\n", "# added using *code* and not manually\n", "# -------------------------------------------------------------\n", " ]\n", "}\n", "\n", "# new empty list where I'm going to put all the new dictionaries \n", "# a.k.a. all the new points\n", "three_new_points = []\n", "\n", "for i in range(3):\n", "# define a new point \n", " new_point = {\n", " \"type\": \"Feature\",\n", " \"properties\": {},\n", " \"geometry\": {\n", " \"type\": \"Point\",\n", " \"coordinates\": []\n", "\n", " }\n", " }\n", " \n", " # increment the longitude\n", " gap += dx\n", " \n", " # create a new list with the updated coordinates\n", " new_coordinates = [init_coords[0] + gap, init_coords[1]]\n", "\n", " # assign the new coordinates to the coordinates key\n", " # in the new point dictionary\n", " new_point[\"geometry\"][\"coordinates\"] = new_coordinates\n", " new_point[\"properties\"][\"name\"] = \"Point \" + str(i+1)\n", "\n", " # append the new point dictionary to the list of new points\n", " three_new_points.append(new_point)\n", "\n", "\n", "# append to the feature list the three new points\n", "# that we created\n", "transect[\"features\"].extend(three_new_points)\n", "print(transect)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This output on its own makes _very_ little sense, but it's actually a real world data structure called JSON. Below we use a handy library (that you might not yet have installed on your computer) to turn that JSON into something easier to read, and _then_ we'll see how you can use it!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import json \n", "parsed = json.loads(str(transect).replace(\"\\'\", \"\\\"\"))\n", "print(json.dumps(parsed, indent=4))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from ipyleaflet import Map, GeoJSON, basemaps\n", "m = Map(center = (51.51, -0.10), zoom=10, min_zoom=5, max_zoom=20, \n", " basemap=basemaps.OpenTopoMap)\n", "geo = GeoJSON(data=parsed)\n", "m.add_layer(geo)\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### Further references:\n", "\n", "General list of resources\n", "- [Awesome list of resources](https://github.com/vinta/awesome-python)\n", "- [Python Docs](https://docs.python.org/2.7/tutorial/introduction.html)\n", "- [HitchHiker's guide to Python](http://docs.python-guide.org/en/latest/intro/learning/)\n", "- [Python for Informatics](http://www.pythonlearn.com/book_007.pdf)\n", "- [Learn Python the Hard Way - Lists](http://learnpythonthehardway.org/book/ex32.html)\n", "- [Learn Python the Hard Way - Dictionaries](http://learnpythonthehardway.org/book/ex39.html)\n", "- [CodeAcademy](https://www.codecademy.com/courses/python-beginner-en-pwmb1/0/1)\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Credits!\n", "\n", "#### Contributors:\n", "The following individuals have contributed to these teaching materials: \n", "- [James Millington](https://github.com/jamesdamillington)\n", "- [Jon Reades](https://github.com/jreades)\n", "- [Michele Ferretti](https://github.com/miccferr)\n", "- [Zahratu Shabrina](https://github.com/zarashabrina)\n", "\n", "#### License\n", "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", "\n", "#### Acknowledgements:\n", "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", "\n", "#### Potential Dependencies:\n", "This notebook may depend on the following libraries: None" ] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.5" } }, "nbformat": 4, "nbformat_minor": 4 }