{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# More Quick Lessons & Attendent Exercises" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Have 15 minutes? Work through some of these little lessons. Finish them up? Write some of your own. You don't have to share them with anyone (although you could): Creating your own problems to solve is easily one of the most effective ways to gain confidence in approaching data science through computation.\n", "\n", "Some of this will be new, some of it familiar. Work on the parts that are most interesting to you, and trust that the others will become interesting in time." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### LOOPS\n", "\n", "OK: Let's review a bit as we move into more new stuff. Let's start by counting from 0 to 9." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n", "1\n", "2\n", "3\n" ] } ], "source": [ "#Here's one way:\n", "\n", "a = 0\n", "print(a)\n", "a = 1\n", "print(a)\n", "a = 2\n", "print(a)\n", "a = 3\n", "print(a)" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n", "1\n", "2\n", "3\n" ] } ], "source": [ "# Here's a second way:\n", "\n", "a = 0\n", "print(a)\n", "a = a + 1\n", "print (a)\n", "a = a + 1\n", "print (a)\n", "a = a + 1\n", "print (a)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n", "1\n", "2\n", "3\n", "4\n", "5\n", "6\n", "7\n", "8\n", "9\n" ] } ], "source": [ "# Not much better. Here's a third way:\n", "\n", "for n in range(0,10):\n", "\tprint(n)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### So how'd that work? \n", "\n", "The `n` in the statement `for n in range(0,10):` works as our counter: It is going to increase ('increment') by 1 every time this line is repeated. (It turns out that this basic act -- adding 1 to a variable -- is one of the most important features of most programming languages).\n", "\n", "In our statement, the `range()` function defines a range of numbers. In this case, a range with a low of 0 and an upper limit of 10. \n", "\n", "So one other thing we ought to make clear then: Look back at the output from the `for` loop: Do you notice that we didn't count from one to ten? Instead, we counted from 0 to 9. That seems... weird, right?\n", "\n", "Right: It started at 0, like you'd expect, but then it ended at 9. Why is that? Didn't we say \"10\" in our range? Well, yes. Sigh. But that's the way things work: `range(0,10)` means \"from 0 and up to but not including 10.\"" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2\n", "3\n", "4\n", "5\n", "6\n", "7\n" ] } ], "source": [ "# Self-evidently, perhaps:\n", "for n in range(2,8):\n", "\tprint(n)\n", "\n", "# Again: We use 8 as the upper-most bound,\n", "# but we never actually get to that number." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Arguments in Range()\n", "\n", "Fine. But hold on a moment, cowpoke. The upper- and lower-bound part makes sense. But there's always room for another argument: Let's suppose we add another number to that range of yours? What do you suppose this will do?\n", "\n", " for n in range(1,10,3):\n", " print(n)\n", "\n", "It turns out that the range() function allows UP TO 3 arguments:\n", " range (from, up to, step)\n", "\n", "**From** is the starting number, **up to** tells us the upper bounds (which we'll never reach), and **step** describes the number by which we will increment along the way. By default, **step is assumed to be 1**. That's why we often don't include it in our code: We are lazy, and the computer fills it in for us. In fact, **the same thing goes for the first argument**, `from`: We almost always just assume `from` is going to be a zero.\n", "\n", ">REMEMBER: You begin to count with the number \"1\", but computers almost always start counting with the number \"0\". Why? Because they are sneaky and they want to finish counting before we do. \n", "\n", "Let's take a look at some examples of how these default values work: \n", "\n", "This loop, for instance:\n", "\n", " for n in range(0,5,1):\n", " print(n)\n", "\n", "Is exactly the same as this:\n", "\n", " for n in range(0,5):\n", " print(n)\n", "\n", "Is exactly the same as this:\n", "\n", " for n in range(5):\n", " print(n)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n", "1\n", "2\n", "3\n", "4\n" ] } ], "source": [ "for n in range(5):\n", "\tprint(n)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Fine. `From`, `Up to`, and `Step` are always there, even if they aren't *always there*. But I can include them and use less conventional values to do cool things.\n", "\n", "Let's say I've built some kind of super-villain compound and I am going to launch the missiles (... or I'm counting down the final seconds of the end of the year... or I'm holding my breath in an underwater-breath-holding-Olympic-contest, whatever, I don't know, use your imagination): \n", "\n", "My point: If a step of 1 went up, shouldn't a `step` of -1 go... down?" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5\n", "4\n", "3\n", "2\n", "1\n" ] } ], "source": [ "# Gateau sec: We set the lower bound as the upper, \n", "# set the upper bound as the lower, \n", "# and set the increment to -1 so that it decrements:\n", "\n", "for n in range(5,0,-1):\n", "\tprint(n)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2\n", "6\n", "10\n" ] } ], "source": [ "# And remember: Variables are every bit as meaningful as digits.\n", "# Below, I've used variables in place of digits. Variables are\n", "# generally more flexible: They provide relative values, where\n", "# digits only provide absolute ones.\n", "\n", "low = 2\n", "high = 11\n", "step = 4\n", "for n in range(low,high,step):\n", " print(n)\n", "\n", "# See how it stops before it hits the high bound, no matter how close it was?" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "# What's more, bear this in mind: The n that we're getting out of \n", "# that counter function will resolve to a numeric value: \n", "# We can plug it right back into some other expression.\n", "\n", "# In this case, I've done just that, and I've built \n", "# something called a \"nested loop\".\n", "# Think of nested-loops as being like Russian nesting dolls\n", "# -- one doll inside another doll. Here, one loop\n", "# runs inside another loop." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Please make ready room: 110\n", "Please make ready room: 111\n", "Please make ready room: 112\n", "Please make ready room: 113\n", "Please make ready room: 114\n", "Please make ready room: 210\n", "Please make ready room: 211\n", "Please make ready room: 212\n", "Please make ready room: 213\n", "Please make ready room: 214\n", "Please make ready room: 310\n", "Please make ready room: 311\n", "Please make ready room: 312\n", "Please make ready room: 313\n", "Please make ready room: 314\n", "Please also clean Floor 4\n" ] } ], "source": [ "# cleaning staff schedule:\n", "# example 'nested loop' code\n", "groundfloor = 1\n", "penthouse = 4\n", "room_low = 10\n", "room_high = 15\n", "for floor in range(groundfloor, penthouse):\n", " for suite in range(room_low, room_high):\n", " assignment = str(floor) + str(suite)\n", " print(\"Please make ready room: \", assignment)\n", "print(\"Please also clean Floor\", penthouse)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "See how well that worked? \n", "\n", "Well, worked well with one possible exception: If our Penthouse Suite is on level 4, then our cleaning bots will never get there: By assigning a range(1,4), we guaranteed that they never go up past 3. So I just added a final print statement to clean up our loose ends.\n", "\n", "It's worth pointing out that the never-reach-the-topmost-number thing seems weird -- but on the other side of the number line, there is more weirdness: We never count from 1, but always start at 0. Both of those standards cause a lot of confusion -- but in the end, they tend to balance one another out. So don't overthink this: It often takes care of itself." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "# In any event, the building metaphor is still a good way to \n", "# think about 'nested loops'. If you're going to go through \n", "# every room in a building, you're probably not going to do\n", "# it all willy-nilly and randomly: You'll probably do one\n", "# floor at a time, and while you're on that floor, you\n", "# visit each room in turn. \n", "\n", "# And THAT is a nested loop:\n", "# Go to a floor. Go through each room. Go to the next\n", "# floor. Go through each room. Etc." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that as before, we can run things backwards this time, high to low, or even add steps to skip through some rooms or some floors. Let's start from the top this time, and just assign our cleaning-bots the even-numbered rooms." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Please prepare room: 310\n", "Please prepare room: 312\n", "Please prepare room: 314\n", "Please prepare room: 210\n", "Please prepare room: 212\n", "Please prepare room: 214\n", "Please prepare room: 110\n", "Please prepare room: 112\n", "Please prepare room: 114\n" ] } ], "source": [ "# cleaning staff schedule 2:\n", "# even rooms, top to bottom\n", "basement = 0\n", "penthouse = 3\n", "room_low = 10\n", "room_high = 16\n", "# now add a step of -1\n", "# Outermost Loop (Floor by floor loop)\n", "for floor in range(penthouse, basement, -1):\n", " \n", " # Innermost Loop (Room by room loop)\n", " # add a step of positive 2 (for even numbers)\n", " for suite in range(room_low, room_high, 2):\n", " assignment = str(floor) + str(suite)\n", " print(\"Please prepare room: \", assignment)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Printing Output Without LF" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "**\n", "*****\n", "********\n", "***********\n", "**************" ] } ], "source": [ "low = 2\n", "high = 15\n", "step = 3\n", "\n", "# Outermost loop\n", "for n in range(low,high,step):\n", " print('') #<-- print('nothing')\n", " \n", " # Inner loop\n", " for c in range(0, n):\n", " print('*', end='')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "OK, let me take a moment and provide some explanation for how we used those `print()` statements, above. (Although you shouldn't hesitate to start by making small changes to the code above and see what happens for yourself: Always a better way to learn than to hear me explain it).\n", "\n", "By default, a `print()` function prints your variable, and then it adds a single additional character: a LineFeed -- in effect, like I've struck *return* or *enter*. (Truth be told, when we write it out, it *looks* like two characters (`\\n`)... and I suppose when we say it outloud, it *sounds* like three syllables (\"escape-N\"). But it is actually just one 8-bit character to the computer.\n", " \n", "So to make the lines above, I need to tell Python to knock it off with the LineFeeds. I do that (in a surprisingly clunky way) by just appending (adding) a **second argument** to my print statement. I say \"Print this\", and then I add \"Oh, and by the way, you should end a line by printing nothing.)\" But I could say \"Oh, and could you add a \",\" and a space at the end, please?\" Or whatever. The point being I ask it to print one thing **instead** of a LineFeed (`\\n`).\n", " \n", "Here's what happens when I take that approach an apply it to the code above:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "****************************************" ] } ], "source": [ "low = 2\n", "high = 15\n", "step = 3\n", "for n in range(low,high,step):\n", " for c in range(0, n):\n", " print('*', end='')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Wait**, you'll say. **That's not what we was promised!** 'Tis true, says I. Let's change the code around a bit and see if we can figure out why. (And I'm changing some of the numbers a bit in order to make it easier to follow)." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0120123012340123450123456" ] } ], "source": [ "low = 3\n", "high = 8\n", "step = 1\n", "for n in range(low,high,step):\n", " \n", " for c in range(0, n):\n", " print(c, end='')" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "# Ah-ha! Let me break the line above apart:\n", "# 012 <-- n is 3\n", "# 0123 <-- n is 4\n", "# 01234 <-- n is 5, etc.\n", "\n", "# The problem is that after each cycle, it wasn't adding a return:\n", "# That last print statement, on line 7, told it never to do so. end=''\n", "# is the same as \"Don't Print a Return\".\n", "\n", "# BUT if we sneak a blank line INSIDE the repetition of the \n", "# first loop but OUTSIDE the repetition of the second loop,\n", "# then we could separate the lines again. To wit:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "012\n", "0123\n", "01234\n", "012345\n", "0123456" ] } ], "source": [ "low = 3\n", "high = 8\n", "step = 1\n", "\n", "for n in range(low,high,step):\n", " \n", " print('') # <-- prints nothing, but will add a \n", " # after each new group of digits prints.\n", " # Note also that it is part of the first cycle (for n)\n", " # but it is outside the range of the second cycle (for c)\n", " \n", " for c in range(0, n):\n", " print(c, end='')" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3: 012\n", "4: 0123\n", "5: 01234\n", "6: 012345\n", "7: 0123456\n" ] } ], "source": [ "# We could also do something like this:\n", "low = 3\n", "high = 8\n", "step = 1\n", "for n in range(low,high,step):\n", " print(n, end = ': ')\n", " for c in range(0, n):\n", " print(c, end='')\n", " print('')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this case, there are actually three lines that contribute to put information to the screen. The first\n", "\n", " print(n, end=': ')\n", "\n", "only gets fired (gets executed, gets run) immediately before the second loop; it only runs 5 times. I set that to print the top bound (`n`), followed by a colon and a space -- and because I set that as the `end`, Python won't add in its normal `LineFeed` code.\n", " \n", "In the second loop, there is a print command that is called (fired, run, executed) much more frequently. But it does as little as possible:\n", "\n", " print(c, end='')\n", "\n", "It only prints the value of `c` and moves on. It NEVER prints a `LineFeed`, even when it has finished.\n", "\n", "The last `print` statement is trickier: Because of its placement, *it is part of the n-loop, but not part of the c-loop*: That is, it is part of the outermost loop, but not part of the innermost loop. If it were intended to be part of the c-loop, it would've been indented one more column to the right. It would've looked like this:\n", "\n", " for c in range(0, n):\n", " print(c, end='')\n", " print('')\n", "\n", "Instead it looked like this:\n", "\n", " for c in range(0, n):\n", " print(c, end='')\n", " print('')\n", "\n", "(REMEMBER: Python, unlike most languages, is very picky about indenting: Indentation is part of how it interprets code.) As it was written, the indentation tells Python that the last `print` statement is part of the n-loop -- that is, it is \"inside\" the n-loop -- so that it will get executed once AFTER the c-loop is done running. \n", "\n", "And for all that -- what does it do? It seems to print nothing -- but remember that by default, it is adding a Line Feed. Thus, it pushes everything that follows one line down.\n", " \n", "Here's another illustration of how the lines above would fire:\n", "\n", "5:\n", "6, 78, 78, 78, 9\n", "6, 78, 78, 78, 78, 9\n", "6, 78, 78, 78, 78, 78, 9\n", "6, 78, 78, 78, 78, 78, 78, 9\n" ] } ], "metadata": { "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.6.3" } }, "nbformat": 4, "nbformat_minor": 2 }