<jupyter_start><jupyter_text>Introduction to Python What are Jupyter Notebooks and why are we using them?Jupyter Notebooks allow you to interactively write code, visualize results, and enhance the output via markdown, html, latex ($c = \sqrt{a^2 + b^2}$), and other rich content streams (see Python image!) ![caption][1]We are using them because they provide an excellent means to iteratively and interactively explore and visualize data in one place. They are also great for showing examples and providing space for students to complete exercises. Getting Started with Jupyter Notebooks - the basics All notebooks are composed of cellsJupyter Notebooks are composed of **cells**. Every bit of content within a Jupyter Notebook, including this text, resides within a **cell**. Markdown and Code cellsThere are two primary types of cells - **Markdown** and **Code** cells. This current cell is a markdown cell. Markdown is a simple plain-text language that allows you to add some basic styles and links with little effort. This class will not cover markdown, but it is easy to learn.For nearly all of the class, you will be working inside a **Code** cell. Code cells are where you write and execute your Python code.![][2] Executing CellsTo execute the contents of a code cell with Python inside of it press **shift + enter**. If the last line of code in your cell produces any output, then this value will be displayed in the **`Out`** cell directly below it. If your last line of code produces no output, then there will be no **`Out`** cell. Execute your first cellLet's practice this by executing the cell directly below. It is code cell that adds two numbers together. Place your cursor in the cell so that it is in edit mode and press **shift + enter** to execute it. The statements following the hash sign (****) are comments and skipped.[1]: images/monte_python.jpg[2]: images/code_cell.png<jupyter_code># a code cell that adds two numbers together
# press shift + enter to execute
5 + 7<jupyter_output><empty_output><jupyter_text>Executing a cell with no output<jupyter_code># This will not produce any output
a = 7<jupyter_output><empty_output><jupyter_text>For output, only the last line of the cell matters<jupyter_code># Execute three Python statements.
# Only the last statement will result in an output
5 + 7
3 + 6
4 - 2<jupyter_output><empty_output><jupyter_text>An exception with the `print` statementIt is possible to produce output from lines that are not the last with the print statement. Notice how the results of the first two statements are printed below the cell with the output of the last line still in its own **`Out`** cell.<jupyter_code>print(5 + 7)
print(3 + 6)
4 - 2<jupyter_output><empty_output><jupyter_text>What actually is Python?For a computer, Python code is simply characters on a screen. These characters have no idea what to do with themselves unless some other computer program translates them to a language that a machine can understand. Its important to understand that Python is a programming language and that Python the programming language has many different implementations that turn those characters to executable machine code. The original implementation of Python is called **CPython** written in C by the ['Benevolent Dictator for Life'][1] <NAME> who started the project in the 80's and released it in 1991. Why use Python?Python is a **high-level** language. High-level as, it is a programming language that is easy to learn, with simple and compact syntax, as well as being quite powerful to complete nearly any task.It is a general purpose programming languages allowing you to develop a wide range of applications from the web to full scale data science pipelines. A small sacrifice in inefficiency allows for great productivity as a developer. Don't use a calculator, use Python!To kick things off, we can start off using Python as a calculator. All the standard operators work as expected. Execute the expressions in the code cells below.[1]: https://en.wikipedia.org/wiki/Benevolent_dictator_for_life[2]: https://github.com/python/cpython[3]: https://github.com/gvanrossum<jupyter_code># add, sub, mul, divide all happen as expected
7 * 7 - 50 + 100 / 6<jupyter_output><empty_output><jupyter_text>Comments in code blocksTo insert a comment, use the hash () character. Everything written after the hash will be commented out. The keyboard shortcut to comment out a line is **ctrl + /** or **cmd + /** on a mac<jupyter_code># Integer division is done with //
12 // 7
# Use ** to raise to a power
4 ** 3
# % is the modulus operator to get a remainder
89 % 13<jupyter_output><empty_output><jupyter_text>Problem 1How many seconds are there in a century?<jupyter_code># your code here<jupyter_output><empty_output><jupyter_text>Assignment Statements - Assigning values to variable names<jupyter_code># assign values to the variables a, b, and c
a = 10
b = 2
c = b ** 3
a
b
c<jupyter_output><empty_output><jupyter_text>Outputting more than one variable at a same time<jupyter_code># output multiple variables
a, b, c<jupyter_output><empty_output><jupyter_text>Problem 2Use three different variables to store a 15% sales tax on a $70 meal to get the total and output all of them at the same time<jupyter_code># your code here<jupyter_output><empty_output><jupyter_text>Every value in Python has a typeOne of the most important and fundamental concepts to internalize when learning Python is that every value that you see has a type. Before we get into Python types, it might be helpful to think about types outside the context of programming languages.Everything that we see in the world around us is also some **type** of object. For instance, when we look in our closet, we see shirts, pants, dresses, shoes, socks, belts, etc... Each one of these objects is a type. When we walk outside we see cars, roads, birds, trees, grass, people, etc... Again, each one these objects is a type. The type of an object tells you much about what it is and what it is capable of doing. A car is able to drive and a cup is useful to hold liquid that is easily consumable. The `type` functionWhen working in Python, it is extremely important to know the type of value you are working with. In the above paragraph, I used the term **object** to refer to each thing that was inside my closet or outside. Technically, everything is an **object** in Python.For now, it's important to realize that every value and variable you see in Python has a specific type. The **`type`** function is used to definitively check the type. Let's see some examples below.<jupyter_code>type(10)<jupyter_output><empty_output><jupyter_text>Numeric typesThere are two primary numeric types - **`int`** and **`float`**. **`int`** is short for integer. Integers are whole numbers and floats are numbers with decimals. Let's see an example of a float.<jupyter_code>type(12.4)<jupyter_output><empty_output><jupyter_text>All variables have typesWhen we assign a variable a value, that variable has the same type as the value. We can use the **`type`** function directly on that variable.<jupyter_code>a = 10
type(a)<jupyter_output><empty_output><jupyter_text>Boolean typePython has two reserved words, **`True`** and **`False`**, that are used for the boolean type. **`bool`** is the official word used.<jupyter_code>type(True)
type(False)<jupyter_output><empty_output><jupyter_text>StringsPreviously, we briefly looked at the basic numeric types, **`int`** and **`float`**. These types are quite simple and straightforward. They behave how you would expect them. We now turn to **strings** which are a more complex type. Python uses the keyword **`str`** to represent strings. Built-in Types* `bool`* `int`* `float`* `str`* `tuple`* `list`* `set`* `dict`There are several more built-in types such as **`complex`**, **`frozenset`**, **`bytes`**, and others but these are used far less frequently than the list above. To see all the built-in types see this (very long) [page from the official Python documentation][1] The term data typeYou will often see the term **data type** used when discussing **types**. They refer to the same concept. Back to strings - what are they?Technically, a Python string is a sequence of characters. So what are characters?A character is the smallest possible component of text, usually a letter of an alphabet or punctuation that you can output with one key from your keyboard. There is no separate type for characters. A single character is just a string with length of one.  Creating our first stringStrings are created by writing a sequence of characters surrounded by either single or double quotes.[1]: https://docs.python.org/3/library/stdtypes.html[2]: https://docs.python.org/3/howto/unicode.htmldefinitions<jupyter_code># Define a string
my_string = 'my own personal string'<jupyter_output><empty_output><jupyter_text>Output the string to the notebook<jupyter_code>my_string<jupyter_output><empty_output><jupyter_text>Using double quotes<jupyter_code>my_string2 = "yet another string"
my_string2<jupyter_output><empty_output><jupyter_text>Quotes inside of quotes<jupyter_code>singleq = "There's a single quote in here"
singleq
doubleq = '<NAME> once said, "We made too many wrong mistakes"'
doubleq<jupyter_output><empty_output><jupyter_text>Multiline Strings with triple quotes<jupyter_code>tripleq = """Use triple
quotes for some 
very long
multiline strings
"""

tripleq<jupyter_output><empty_output><jupyter_text>Single and double quotes in the same string<jupyter_code>my_string_w_2_quote_types = '''My friend said, "I'm only a mediocre pythonista". I got mad! '''
my_string_w_2_quote_types<jupyter_output><empty_output><jupyter_text>Using the print functionYou can use the **`print`** function to output your strings. The output will have the escape characters replaced by the values they represent.<jupyter_code>print(tripleq)
print(my_string_w_2_quote_types)<jupyter_output><empty_output><jupyter_text>Many more "abilities" with stringsStrings have far more "abilities" than their numeric counterparts. Some of these abilities include the following:* Capitalizing the first letter of each word* Counting the frequency of a particular letter* Splitting the string into multiple other strings* Finding the location a particular substring* And much moreThe abilities I am referring to are called **methods**. The way methods are invoked in Python is by placing a dot at the end of the variable name followed by the method name and a set of parentheses. Capitalizing each character in a stringFor instance, we can use the **`upper`** method to capitalize each character in a string.<jupyter_code>capitalize_me = "this string will soon be all caps"
capitalize_me.upper()<jupyter_output><empty_output><jupyter_text>Counting character occurrencesThe **`count`** method capitalizes the unique occurrences of a substring. Place the substring you would like to count inside of the parentheses.<jupyter_code>capitalize_me.count('i')
capitalize_me.count('will')<jupyter_output><empty_output><jupyter_text>Calling methods with parametersThe **`count`** method from above is an example of a method with **parameters**. A parameter is a variable whose value changes the functionality of the method. The strings **`i`** and **`will`** are both parameter values. Some methods, such as **`upper`** do not have any parameters and therefore can be called with nothing inside their parentheses. Methods and their parameters will be discussed in greater detail in future notebooks.  Split a string into multiple substrings<jupyter_code>my_string.split()<jupyter_output><empty_output><jupyter_text>Splitting a string by a given value<jupyter_code>my_string.split('so')<jupyter_output><empty_output><jupyter_text>How do I know what methods are available?The above examples only covered a few of the available string methods. The official Python documentation lists all the [string methods][1]. While thorough, its not that convenient. [1]: https://docs.python.org/3/library/stdtypes.htmlstring-methods  Use tab completion to get help while codingAn even better method for finding the available list of methods is with the tab completion functionality of IPython. To make use of tab completion, write the name of your variable followed by a dot and then press **tab**. A drop down menu will appear with a list of the methods. It will look like this: ![][1][1]: images/string_methods.pngReproduce this drop down menu in the below cell:<jupyter_code># Place your cursor at the end of the next line and press tab
my_string.<jupyter_output><empty_output><jupyter_text>Getting help while coding with `shift + tab + tab`One of my favorite tricks is to have the docstrings appear in a pop-up window as I am coding. It will look like this:![][1]To make the docstrings pop-up in-place, type out your method and **hold shift + tab + tab**[1]: images/string_docstring.png<jupyter_code># place the cursor at the end of the line then hold shift and press tab twice
replace_string.replace<jupyter_output><empty_output><jupyter_text>Problem 3Replace each occurrence of 'in' with 'out' in the following string.<jupyter_code>replace_string = 'it is starting to rain on the inside'
# your code here<jupyter_output><empty_output><jupyter_text>Problem 4Find and use a method that will strip away all the exclamation points from either end of the following string.<jupyter_code>s = '!!!!a string with a dull message!!!!'
# your code here<jupyter_output><empty_output><jupyter_text>Problem 5Find and use a method that will find the position of the first occurrence of the letter `t` in the following string.<jupyter_code>s = 'a data scientist'
# your code here<jupyter_output><empty_output><jupyter_text>Getting the length of a string: Is there a method for this?The builtin **function** **`len`** returns the string length.<jupyter_code># Getting the length of a string using the len function
test_string = 'yet another test string'
len(test_string)<jupyter_output><empty_output><jupyter_text>Concatenating stringsAgain, there is no direct method to concatenate strings together. There isn't even a function to do this either. Instead, we use the **plus operator**. See some examples below:<jupyter_code>'abcde' + 'fghijk' + 'lmnop'
string1 = 'mac'
string2 = 'hine'

string1 + string2<jupyter_output><empty_output><jupyter_text>Repeat a string with the multiplication operatorInterestingly, the multiplication operator is available to use with strings. Multiply any string by an integer and you will produce a new string concatenated to itself that many times.<jupyter_code># The string is repeatedly concatenated to itself via multiplication
'some test words | ' * 5<jupyter_output><empty_output><jupyter_text>String Interpolation**String interpolation** refers to the substitution of variable values inside of strings. Python recently upgraded its string interpolation in 3.6 to make it easier and more intuitive with something called **f-strings** which is short for formatted literal strings. f-string basicsSubstituting variable values into strings is quite easy with f-strings. Here are the two steps that you must follow:* Prefix the string with the letter **`f`*** Surround the variable with curly braces within the stringIn the example below, we create three variables and replace them in the sentence with an f-string. Notice the **`f`** prefix.<jupyter_code>name = 'Ted'
occupation = 'data scientist'
salary = 3

# f-string - substitute name, occupation, and salary
f'Employee {name} is a {occupation} and earns {salary} dollars per year'<jupyter_output><empty_output><jupyter_text>Selecting substringsWe first introduce the index operator (square brackets), **`[ ]`**, which is fundamental to scientific computing in Python. The **`[ ]`** operator has the ability to select item(s) from a sequence in a wide variety of manners. Since strings are sequences of characters, the **`[ ]`** operator provides lots of functionality for strings.  The index of each characterThe **index** of each character is defined as the **integer location** of each character. The integer location starts at 0 from the beginning of the string.  Selecting a single characterTo select a single character from a string, append the index operator to the string and place the integer location of the desired character with the brackets. Let's select the first character from the following string.<jupyter_code>test_string = 'make sure you complete the entire precourse'
test_string[0]<jupyter_output><empty_output><jupyter_text>Selecting other charactersAll characters of the string may be selected individually in this manner. Let's select index 4.<jupyter_code># An empty space
test_string[4]<jupyter_output><empty_output><jupyter_text>Using negative indices to select from the endIt is possible to use negative integers to select from the end of a string. Let's select the last element.<jupyter_code>test_string[-1]
test_string[-9]<jupyter_output><empty_output><jupyter_text>Slice notation to select a substringIn addition to selecting single characters, we can easily select substrings (multiple characters) from our string with **slice notation**. Slice notation is composed of three integers, **start**, **stop**, and **step**. These three integers are separated by a colon. The **step** integer is optional and is always defaulted to 1. The generic form for slice notation looks like this: `start:stop:step`Slice notation is placed within the square brackets following the string. Let's select a substring from index 5 to index 13.<jupyter_code>test_string[5:13]<jupyter_output><empty_output><jupyter_text>The stop index is not includedThe above slice notation, **`5:13`**, begins at index 5 and selects all characters up to but NOT including index 13. Let's verify this by selecting index 12 and index 13.<jupyter_code>test_string[12]
test_string[13]<jupyter_output><empty_output><jupyter_text>The step integer is optionalWe could have included the step integer 1 like so below, but it is not necessary.<jupyter_code>test_string[5:13:1]<jupyter_output><empty_output><jupyter_text>Stepping by something other than 1We can step by any integer we want. Let's select every other letter from that same substring.<jupyter_code>test_string[5:13:2]<jupyter_output><empty_output><jupyter_text>Both start and stop integers are optional as wellLet's say we want to select the first 5 characters of the string. It is not necessary to use the integer 0 as the start position and instead it is much more common to see the following:<jupyter_code>test_string[:5]<jupyter_output><empty_output><jupyter_text>Omit the stop value to slice to the endIf the stop value is not provided, then the selection will continue until the end of the string.<jupyter_code>test_string[10:]<jupyter_output><empty_output><jupyter_text>Problem 6Select the last three letters of the following string.<jupyter_code>test_string = 'make sure you complete the entire precourse'
# your code here<jupyter_output><empty_output><jupyter_text>A trick to reverse a stringAn easier, albeit unintuitive method exists to reverse a string. Use the slice notation **`::-1`**.<jupyter_code>test_string[::-1]<jupyter_output><empty_output><jupyter_text>Problem 7Slice this string from index 5 to the end by every 4th element<jupyter_code>s = 'the astros will win the world series again in 2018'
# your code here<jupyter_output><empty_output><jupyter_text>Problem 8Select every third element starting from the last character and ending with the first.<jupyter_code>s = 'the astros will win the world series again in 2018'
# your code here<jupyter_output><empty_output><jupyter_text>Changing the characters of a stringYou might be surprised to find that once a string is created, nothing about it can change. Technically, strings are **immutable** and cannot be changed once created. Many objects in Python are **mutable** such as lists, which we will cover soon, but not strings.For instance, if we try and change index 7 to **`z`** we will get the following error.> `TypeError: 'str' object does not support item assignment`<jupyter_code>test_string[7] = 'z'
test_string[7:20:-1] = 'z'<jupyter_output><empty_output><jupyter_text>Mutable and Immutable ObjectsPython objects are either mutable or immutable. Mutable objects can have their value's changed after creation. Immutable objects are those whose values cannot be changed after creation.Strings, ints, floats, booleans are types of objects that are immutable (unable to be changed after creation).We will soon learn mutable types like lists, dictionaries and sets.  Didn't we have some strings from above that were mutated?In some of the above examples strings were concatenated together to form a new string but the original strings were never changed. Take a look at the following which concatenates two strings and prints out that value. The original strings are left unchanged.<jupyter_code># Concatenation does not mutate the underlying string
a = 'string 1 '
b = 'string 2'
print(a + b)
print(a)
print(b)<jupyter_output><empty_output><jupyter_text>Simple test whether a string contains a substringThere is a simple test to determine whether or not one string is a substring of another. Place the **`in`** operator between two strings as follows:<jupyter_code>s = "executing a string assignment"
'cut' in s
'z' in s<jupyter_output><empty_output><jupyter_text>Reversing the condition with `not`You can also test whether a string is not a substring with the **`not in `** operator.<jupyter_code>'z' not in s<jupyter_output><empty_output><jupyter_text>Alternatively, use the `find` methodThe find method returns the value -1 if the passed string is not a substring, otherwise it gives the index of the first character where the substring was found.<jupyter_code># you can use the index method, which gives you the position of the substring if found
s.find('cut')
s.find('z')<jupyter_output><empty_output><jupyter_text>Objects Everything is an Object in PythonOne of the most popular sayings in Python is that **everything is an object**. While not quite literally true, any value that can be assigned to a variable is an object. All integers, floats, booleans, strings, lists, sets, dictionaries, etc... and even functions are **objects**.<jupyter_code>s = "world's best data scientist"<jupyter_output><empty_output><jupyter_text>The type of an objectAs we discussed previously, the type of an object is an extremely important piece of information and let's us know what is possible with it. Let's use the **`type`** function to definitively tell us what type of object we have.<jupyter_code>type(s)<jupyter_output><empty_output><jupyter_text>Use your objects with attributes and methodsThe power and usefulness of objects in Python come from their **attributes** and **methods**. All objects in Python have **attributes** and **methods**.Attributes are descriptions of the objects and are sometimes referred to as **properties**. Typically, they are nouns or adjectives. They are a single piece of information about the object.**Methods** are actions that the objects can take and are typically verbs. Methods execute a block of code while attributes typically retrieve one specific value.![][1][1]: images/object_diag.png  Use Dot Notation to access attributes and methods**Dot notation** is how you make use of your object and how you directly access the attributes and methods. Dot notation simply means placing a dot after your object followed by the attribute or method name. No parentheses for an attribute```>>> type(mercedes)Car>>> mercedes.year   attribute that retrieves a specific piece of information2011>>> mercedes.drive_forward(miles=10)   method that drives the car forward 10 miles``` Are there only methods for strings?In the last notebook on strings, we only called methods with our strings. Strings are an example of an object that only has methods and no attributes. When we start working with Pandas, you will see that the primary object, the DataFrame, has some attributes to go along with many methods. What isn't an objectThere are about 30 reserved words that are not objects. Some of these include **`if, return, def, except`**. A [full list][1] is available in the documentation. Take care that **`True`**, **`False`**, and **`None`** are in that list but are objects.Other symbols such as commas, parentheses, braces, and periods are not objects. Operators such as plus, minus, multiplication, and division signs, are also not objects. More formally, Python uses the terminology **`delimiters`** and **`operators`** for these two classes of symbols. A complete list is found in the [documentation][4] and printed below as well. Delimiters![][2] Operators![][3]If it can be stored as a variable then it is an object. For instance, let's try and store the keyword **`continue`** to a variable.[1]: https://docs.python.org/3/reference/lexical_analysis.htmlkeywords[2]: images/delimiters.png[3]: images/operators.png[4]: https://docs.python.org/3/reference/lexical_analysis.htmloperators  ListsOne of Python's most powerful built-in types is the **`list`**. In generic programming terminology, lists are considered [data structures][1], or more simply put, a collection of data. Lists are very versatile, capable of many tasks, are very popular, and something you will use frequently when writing Python. Even crude data exploration and matrix algebra can be done using lists. Lists are mutable sequences of objects. Anything can go inside lists, even other lists. Lists are declared by the brackets **`[ ]`** operator followed by comma separated values.Let's declare a list with some integer, string, and float objects.[1]: https://en.wikipedia.org/wiki/Data_structure<jupyter_code># a list object with integers, strings and floats. Each element is separated by a comma
my_list = [1, 2, 3, 4, 'one', 'two', 4.9]

my_list<jupyter_output><empty_output><jupyter_text>Lists may contain heterogeneous data Accessing list elementsEach element of a list is accessed with the index operator, **`[ ]`**, in the same manner as strings. Lists are indexed beginning at 0.<jupyter_code>my_list[2]<jupyter_output><empty_output><jupyter_text>Mutating ListsLists are objects that are mutable, meaning that their value may be changed after creation. Each element in the list can be changed and new elements can be added and other elements can be deleted. This is in contrast to strings where no character can be modified, added, or deleted.Let's change the element at index 3 to **`changed`**.<jupyter_code># Change the 4th element of the list to 'changed'
print(my_list)
my_list[3] = 'changed'
my_list<jupyter_output><empty_output><jupyter_text>------------- Finding List Methods<jupyter_code># like we did with strings lets see all the methods that are available to lists
my_list = [1, 2, 3, 4, 'one', 'two', 4.9]
# press tab
my_list.<jupyter_output><empty_output><jupyter_text>Experimenting with list methodsA series of examples will follow that show some list methods. Remember that methods use the dot notation and always end in parentheses. Let's begin by appending an item to our list.<jupyter_code>my_list.append(22)<jupyter_output><empty_output><jupyter_text>Why was there was no output?When a method returns no output in Python, it does *not* mean that nothing happened. Something almost always *happens*, but you might not see it immediately. In this case, the list was **mutated in place**. 22 was appended to the list with the object **`None`** being returned. Output the list to verify that the append took place.<jupyter_code># See that 22 was appended
my_list<jupyter_output><empty_output><jupyter_text>More list methods<jupyter_code># find first occurence of an item in list
my_list = [1, 2, 3, 4, 'one', 'fifth', 'two', 4.9]
my_list.index('one')
#insert item after fifth index. operation happens in-place
my_list.insert(5, 'fifth')
my_list
# reverse a list in place
my_list.reverse()
my_list<jupyter_output><empty_output><jupyter_text>Getting the length of a list<jupyter_code># return the number of elements in a list with the len function
len(my_list)<jupyter_output><empty_output><jupyter_text>Sorting a listLists can be sorted in-place using the sort method as long the element have types that are orderable.Since `my_list` has both numeric and string types that do not have a natural ordering with respect to one another an error will be raised.<jupyter_code>#sort the list
my_list.sort()<jupyter_output><empty_output><jupyter_text>Lists must have objects that know how to be orderedStrings and numbers don't have a natural ordering so an error is produced above. A list consisting of integers and floats is orderable. See the following example:<jupyter_code># sortable list
sortable_list = [8, 12, 90.8, -87, 0]
sortable_list.sort() # operation happens in place so
# The sort method happens in place and None is returned so there is no output
# We must use a separate line to output the updated list
sortable_list<jupyter_output><empty_output><jupyter_text>Selecting items from lists<jupyter_code># Select the first item
my_list = [1, 2, 3, 4, 'one', 'two', 4.9]
my_list[0]
# Select the last item
my_list[-1]<jupyter_output><empty_output><jupyter_text>Selecting a subset of the list with slice notationSlice notation also works the same with lists as it does with strings. When slicing, even if it is a one element slice, a new list will always be returned.<jupyter_code># from the start up to but not including index 3
my_list[:3]
# from index 3 to the end
my_list[3:]<jupyter_output><empty_output><jupyter_text>Start : Stop : Step<jupyter_code># lets first output my_list
my_list
# slice from index 1 to index 5 with step size of 2
my_list[1:5:2]
# Slice from index 2 up to but not including the second to last element
my_list[2:-2]
# slice with defaul start, stop and step. Looks strange but is legal
my_list[::]
# Same thing as above
my_list[:]<jupyter_output><empty_output><jupyter_text>List ConcatenationThere are two ways to concatenate two lists together.* Use the plus operator* Use the **`extend`** methodThe plus operator produces a new list object, while **`extend`** mutates the **calling list** in-place. The calling list is the one that is invoking the method, the one using dot notation. Concatenation of multiple lists with the plus operatorSee the below examples of concatenation with the plus operator. Notice how the original lists are not mutated.<jupyter_code>list_1 = [1,2,3]
list_2 = [4, 5, 6]
list_1 + list_2
list_1
list_2
# add lists without first assigning to a variable
[4, 5, 6] + [1, 2, 3]
# you can add any number of lists together
[1, 4, 5] + [10] + ['a', 'b', 'c'] + [True, [1, 2, 3]]
# save the new list to a variable to reuse it later
new_list = [1, 4, 5] + [10] + ['a', 'b', 'c'] + [True, [1, 2, 3]]
new_list<jupyter_output><empty_output><jupyter_text>Use the `extend` method to mutate the calling list in placeNotice that you must pass a list (or a list-like object) to the **`extend`** method<jupyter_code># there is also the extend method, which concatenates lists in-place
list_1 = ['a', 'b', 'c']
list_2 = [99, 100]
list_1.extend(list_2) # Returns None (no output) and the operation happens in-place
# output the list to see extend worked properly
list_1
# the second list is unmodified
list_2
# extend with a new list that is not assigned to a variable
list_1 = [1, 2]
list_1.extend(['end'])

list_1<jupyter_output><empty_output><jupyter_text>Problem 9Create a list then use the `extend` method to add four elements to it<jupyter_code># your code here<jupyter_output><empty_output><jupyter_text>Problem 10In words, without actually programming, what will my_list * 5 do?<jupyter_code>#### Change this cell to markdown and answer the question here 

#### verify the results in this code cell<jupyter_output><empty_output><jupyter_text>Lists of ListsList elements may be of any type, meaning that we can create lists of lists, which are similar to n-dimensional arrays in scientific computing.<jupyter_code># create a list of lists
list_list = [[1, 2, 3], [5,6], [90, 100, 109]]
# What happens when we access the first element?
list_list[0]<jupyter_output><empty_output><jupyter_text>Select a single element from a list within a listUse two consecutive square bracket selections when selecting an element from a list of a list. The first selection below selects the list **`[1, 2, 3]`** and the second selects the integer 3.<jupyter_code># How to access nested lists
list_list[0][2]
# Use different indices to get the same item
list_list[-1][-2]
# get a  slice of a list of a list
list_list[2][1:]
# mutate this list of list
list_list[2][1:] = [900, 909]
list_list<jupyter_output><empty_output><jupyter_text>Check item is in listTo determine if an element is contained within a list, the **`in`** or **`not in`** operator is used. This is done in the same way as checking for a substring in a string.<jupyter_code># check list membership
my_list = [6, 'word', 2]
6 in my_list
12 in my_list
5 not in my_list<jupyter_output><empty_output><jupyter_text>Control FlowControl flow refers to the order that statements are executed in Python. All previous cells had statements execute one after another in the order that they were written. This part will cover **`if`** statements and iteration, the two most basic categories of how control flow gets manipulated by the programmer. Indenting and code blocks in PythonPython's syntax is so simple that it relies upon **indentation** (and not curly braces like in other languages) beneath code blocks to mark their beginning and end. `if, for, while, def, with` are all statements that have indented code blocks underneath them. A block is ended when the indentation for the current line returns to where it was before the indentation began.  BooleansPython has a boolean type with keyword values **`True`** or **`False`**. Python has six standard comparison operators* **`>`** &nbsp;&nbsp;- greater than* **`>=`** - greater than or equal to* **`<`** &nbsp; -  less than* **`<=`** - less than or equal to* **`==`** - equal to* **`!=`** - not equal toAll of these evaluate expressions as **`True`** or **`False`**. A single equals sign (**`=`**) is the assignment operator. Evaluate the following comparisons:<jupyter_code>5 > 6 
5 >= 5
0 < 0
0 <= 0
9 == 9
10 != 10<jupyter_output><empty_output><jupyter_text>`if` statements**`if`** statements determine whether the subsequent block of indented code will be executed or not. A boolean expression always follows the **`if`** keyword. These boolean statements are also called **conditions**.**`if`** statements use simple syntax with no parentheses or curly braces. A colon is placed at the end of the statement. Below it, are indented lines that get executed if the condition is true. The general format for **`if`** statements is as follows:```if condition:    indented code block    with one or    more lines```The condition must evaluate to a boolean. Below is a simple condition testing whether a number is positive. If the condition evaluates to **`True`** then the number is halved and printed out. If not, no further code is run.<jupyter_code>x = 90
if x >= 0:
    half = x / 2
    print(f'Half of {x} is {half}')<jupyter_output><empty_output><jupyter_text>`else` statementsIf the condition in the **`if`** statement evaluates to **`False`**, an alternative set of commands may be executed with the **`else`** statement. Notice that **`else`** is at the same indentation level as **`if`** and another colon and code block follow underneath. Statements resume their linear order of execution after the else block ends.<jupyter_code>x = -90
if x >= 0:
    half = x / 2
    print(f'Half of {x} is {half}')
else:
    print('Cannot take the square root of a negative number')
    
print('The code block ends after indentation returns to previous spot. '
      'This print statement gets executed no matter what')<jupyter_output><empty_output><jupyter_text>`elif` statementsInstead of automatically executing the **`else`** block after a false if condition, an additional condition can be tested with an **`elif`** statement. It is possible to have any number of **`elif`** statements to check any number of specific conditions. The below code checks whether an integer is divisible by several other integers using the modulus (%) operator which returns the remainder from integer division.<jupyter_code>x = 47

if x % 2 == 0:
    print(f'{x} is divisible by 2 and not a prime')
elif x % 3 == 0:
    print(f'{x} is divisible by 3 and not a prime')
elif x % 5 == 0:
    print(f'{x} is divisible by 5 and not a prime')
elif x % 7 == 0:
    print(f'{x} is divisible by 7 and not a prime')
elif x % 11 == 0:
    print(f'{x} is divisible by 11 and not a prime')
else:
    print(f'{x} is not divisible by 2,3,5,7,11. It might be prime!')<jupyter_output><empty_output><jupyter_text>Multiple boolean conditionsIt is possible to have any number of boolean conditions evaluated in the same expression by combining them together with **`and`** and **`or`**. For instance, all the **`if`** and **`elif`** statements in the above code can be merged into one statement. It's good practice to wrap each condition in parentheses for readability and accuracy.<jupyter_code># multiple conditions
x = 47
(x % 2 == 0) or (x % 3 == 0) or (x % 5 == 0) or (x % 7 == 0) or (x % 11 == 0)
# multiple conditions
x = 48
(x % 2 == 0) or (x % 3 == 0) or (x % 5 == 0) or (x % 7 == 0) or (x % 11 == 0)<jupyter_output><empty_output><jupyter_text>`not`  keywordThe `not` keyword reverses any boolean expression<jupyter_code>not True
not 5 > 4<jupyter_output><empty_output><jupyter_text>Problem 12Create a string that will execute the second print statement.<jupyter_code># your code here
test_string = '' # change this string so the second print statement below is triggered
# trigger the second print statement
if test_string.count('a') > 4:
    print("There are more than 4 a's in your string")
elif test_string.find('k') > 10:
    print("The first k occurs after the 11th element")
else:
    print("My super intelligent responses did not find any info on your string. Please change it")<jupyter_output><empty_output><jupyter_text>Problem 13Create a different string that will execute the third print statement from above.<jupyter_code># your code here
test_string = '' # change this string so the third print statement below is triggered
# trigger the third print statement
if test_string.count('a') > 4:
    print("There are more than 4 a's in your string")
elif test_string.find('k') > 10:
    print("The first k occurs after the 11th element")
else:
    print("My super intelligent responses did not find any info on your string. Please change it")<jupyter_output><empty_output><jupyter_text>Problem 14Write an expression that returns true if an integer is either greater than 10 or divisible by 7.<jupyter_code># your code here<jupyter_output><empty_output><jupyter_text>Problem 15Write an expression that returns true if the last character of a string is not 's'<jupyter_code>test_string = 'asfdaskfjsafl'
# your code here<jupyter_output><empty_output><jupyter_text>LoopingThere are two types of loops, **`for`** and **`while`**. Just as with `if` statements, `for` and `while` statements end in a colon and are followed by indented code blocks. The code block gets continually executed until all elements in the sequence have been iterated through (`for` loop) or until a condition is no longer true (`while` loop). `for` LoopsAll **`for`** loops have the following general structure:```>>> for item in object:        do something        in this        code block```The code block gets executed for every item in the iterable object. Let's see a basic example of looping through each character in a string with a **`for`** loop.[1]: https://docs.python.org/3/glossary.htmlterm-iterable<jupyter_code># loop through a string
my_string = 'data'

for char in my_string:
    print(char)<jupyter_output><empty_output><jupyter_text>The loop variable**`char`** is known as the **loop variable** and implicitly refers to the next character in the string. Name of loop variableThe name of the loop variable is entirely up to you. See the following two examples where different loop variable names are used.<jupyter_code># you can choose any variable name for the looping variable of your sequence
for character in my_string:
    print(character)<jupyter_output><empty_output><jupyter_text>Looping through a listLoop iteration with a list happens in the exact same manner as it does with strings. The loop variable can be any name you choose and references each consecutive member of the list inside the code block.<jupyter_code># looping through a list
my_list = [1, 10, 'asf', True]

for element in my_list:
    print(element)<jupyter_output><empty_output><jupyter_text>More interesting loops<jupyter_code>a_list = [3, -2, 8, 5.3, 7]
squares = []  # initialize an empty list
for x in a_list:
    squares.append(x * x)
print(squares)<jupyter_output><empty_output><jupyter_text>Problem 16Calculate the total of the squared value of each of the first 100 positive integers.<jupyter_code>#your code here<jupyter_output><empty_output><jupyter_text>Problem 17Use a for loop to iterate over the first 10 positive integers, printing out their squared value only if the value is greater than 50<jupyter_code>#your code here<jupyter_output><empty_output><jupyter_text>`while` Loops**`while`** loops are the other looping construct that Python provides and work with the following syntax. A code block continually executes until a boolean condition evaluates to **`False`**. The condition is re-checked at the beginning of each iteration.  A while statement ends in a colon and the code block is indented.```>>> while condition:        do something in this        code block as long as condition        evaluates as True```Let's see a trivial example below:<jupyter_code># Countdown from a given x
x = 5
while x >= 0:
    if x == 0:
        print("Launch!")
    else:
        print(f'{x} seconds to launch')
    x -= 1<jupyter_output><empty_output><jupyter_text>Problem 18Use a while loop to find the sum of all integers between a given postive integer and 0.<jupyter_code>x = 20
total = 0
# your code here<jupyter_output><empty_output><jupyter_text>Infinite Loops**`while`** loops have the potential to run indefinitely if their condition always evaluates as true. More looping control with `continue` and `break`**`continue`** forces execution to return immediately to the top of the loop for the next iteration. **`break`** exits the loop immediately without any other execution.Let's see a simple example where we sum the numbers 0 through 99 if they are not divisible by 2 or 3.<jupyter_code># Add up only integers from 1 to 99 that are not divisible by 2 or 3
total = 0
for i in range(100):
    if (i % 2 == 0) or (i % 3 == 0):
        continue
    total += i

print(f"Total is {total}")<jupyter_output><empty_output><jupyter_text>Nested LoopsOccasionally it is necessary to execute a loop within a loop. The inner loop is indented further to denote its code block. When the outer loop execution reaches the inner loop, the inner loop will iterate completely before returning execution to the outer loop.<jupyter_code># create a multiplication table
for row in range(1, 11):
    for col in range(1, 11):
        print(f'{row * col:4}', end='')
    print()<jupyter_output><empty_output><jupyter_text>FunctionsFunctions are fundamental to all programming languages. A function is a group of the same reusable programming statements that can be repeatedly called with a reference to the function name. Built-in FunctionsPython comes standard with about [70 builtin functions][1] for the most common and important tasks. These are functions that are ready to use without any need to import them from other modules. Calling Basic FunctionsFunctions are referenced by their name. To execute the code they are referencing, you must append an open and close parentheses to them. Some functions have parameters that are used to modify the functionality within them. Values for these parameters must be placed within the parentheses separated by commas.[1]: https://docs.python.org/3/library/functions.html<jupyter_code># take the absolute value of a number
abs(-5)
# get the max of two values
max(5, 8)
# get the max value of a list
max([11, 4, 6, 8])
# Get the length of a list
my_list = [8, 0, 'asf', True]
len(my_list)<jupyter_output><empty_output><jupyter_text>Shift + tab + tab for helpMy favorite way of getting help is to write the name of the function and then press tab twice while holding down the shift key. This pops up the docstring in the cell.<jupyter_code># Place the cursor at the end of the function then press shift + tab + tab
max
# pass max an iterable - here a list of numbers
max([1, 2, 5, -9])
# pass max a different iterable - a string
max('lkjhweruih')<jupyter_output><empty_output><jupyter_text>User defined functionsThus far we have only used functions built into the language. You can define your own functions as well. Functions are defined using the **`def`** keyword followed by the function name and a set of parentheses. If the function takes any parameters, their names are written separated by commas within the parentheses.The first line of a function definition always ends with a colon. The body of the function is indented. The function ends when the indentation returns to where it was before the function was defined. Function with no parametersLet's begin by writing a simple user-defined function with no parameters. The **`hello`** function has a single line of code in its body. It prints a short message.<jupyter_code># define function

def hello():
    print('Hello, this is a function')
# execute function
hello()<jupyter_output><empty_output><jupyter_text>Function that returns a valueAll functions in Python return a value. To explicitly return a value, the **`return`** statement is used. Notice how the **`hello`** function does not have a **`return`** statement. If no return statement is present, Python will return the object **`None`**.Let's assign the returned value of the **`hello`** function to a variable and verify that is indeed **`None`**.<jupyter_code># capture returned value
x = hello()
# verify it is None
x is None<jupyter_output><empty_output><jupyter_text>Defining a function that returns a valueBelow, we create a function that returns the square root of 5 using [Newton's method][1].[1]: https://en.wikipedia.org/wiki/Newton%27s_methodSquare_root_of_a_number<jupyter_code>def sqrt_5():
    guess = 2
    diff = 5 - guess ** 2
    while abs(diff) > .0001:
        guess = guess - (guess ** 2 - 5) / (2 * guess)
        diff = 5 - guess ** 2
    return guess<jupyter_output><empty_output><jupyter_text>Defining a function with a parameterUser defined functions may have any number of parameters. The following function has a single parameter **`name`**.<jupyter_code>def hello2(name):
    print(f'Hello {name}, this is a function')<jupyter_output><empty_output><jupyter_text>Calling a function with a parameterThere are two separate but equal ways for calling functions with parameters. You can supply the parameter name or just the value itself. Let's take a look at both ways:<jupyter_code># Using the parameter name
hello2(name='Penelope')
# Use just the value itself
hello2('Niko')<jupyter_output><empty_output><jupyter_text>Defining a function with multiple parameters<jupyter_code>def sqrt_newton(num, guess, error):
    diff = num - guess ** 2
    while abs(diff) > error:
        guess = guess - (guess ** 2 - num) / (2 * guess)
        diff = num - guess ** 2
    return guess<jupyter_output><empty_output><jupyter_text>Calling a function with multiple parametersAs before, let's call this function by both with and without the parameters.<jupyter_code># estimate the square root of 100 with initial guess of 8.
sqrt_newton(num=100, guess=8, error=.01)
# without parameter names
sqrt_newton(100, 8, .01)<jupyter_output><empty_output><jupyter_text>Default values for parametersIt is possible to assign default values to function parameters during the definition. This allows for the function to be called without explicitly assigning that parameter a value.We redefine our square root function to have a default maximum error of .1.<jupyter_code>def sqrt_newton2(num, guess, error=.01):
    diff = num - guess ** 2
    while abs(diff) > error:
        guess = guess - (guess ** 2 - num) / (2 * guess)
        diff = num - guess ** 2
    return guess
# We no longer have to specify the error
sqrt_newton2(100, 8)
# You can still specify the error
sqrt_newton2(100, 8, .00001)<jupyter_output><empty_output><jupyter_text>Problem 19Create a function that takes a string argument and returns True or False whether the string is a palindrome (spelled the same foward and backwards). Test your function on the word 'racecar'<jupyter_code># your code here<jupyter_output><empty_output><jupyter_text>Problem 20Create a function `concat_sort_list` that takes two lists and concatenates them together, sorts the list and then returns the sorted list.<jupyter_code># your code here
# test code with
concat_sort_list([4, 5, 2], [9, 0, -8]) == [-8, 0, 2, 4, 5, 9]<jupyter_output><empty_output><jupyter_text>General Rules of Thumb for FunctionsGenerally speaking, most professional Python code is written inside of a function or a method. Functions should do one task and do it well. If you find yourself writing a function that is doing more than one task it's probably best to break it up into multiple functions. Although you can write functions with any number of lines, functions greater than about 20 lines of code can signal that they need to be broken up into multiple different functions. [Heres a good Stack Exchange answer](http://softwareengineering.stackexchange.com/questions/133404/what-is-the-ideal-length-of-a-method-for-you/133406133406) on writing functions.  Tuples, Sets, and Dictionaries TuplesTuples are very similar to lists. They contain any number of objects of any type. The major difference is that tuples are immutable and thus cannot be changed once created. No elements can be added, deleted or changed from them.  Syntax for declaring a tupleTuples are declared with a sequence of objects separated by commas wrapped in **parentheses**.Let's declare some tuples below:<jupyter_code># tuple with three integers
a = (1, 2, 10)

a
# tuple with multiple different types
b = (1, 'one', True, [9, 10])

b<jupyter_output><empty_output><jupyter_text>Selecting items from a tupleSelecting one or more items from a tuple is done in the exact same manner as with a list. The integer location of the desired item is placed in the brackets, **`[]`**. You can also use slice notation to select multiple items that are returned in a new tuple.Let's create a tuple and then select one or more items from it.<jupyter_code># create a tuple with a variety of types including other tuples
t = ['phone', 12, 4.44, True, [3, 4, 5], (5, 4, 2)]
t
# select the first element
t[0]
# select elements from integer location 3 to 6
t[3:6]
# select the first 4 elements
t[:4]<jupyter_output><empty_output><jupyter_text>Concatenating tuplesConcatenating tuples happens with the plus operator. This creates a new tuple object.<jupyter_code>(1, 2) + (3, 4, 5)
a = (1, 2)
b = ('some', 'other', 'tuple')

a + b<jupyter_output><empty_output><jupyter_text>Duplicating tuplesYou can duplicate each item in the tuple by multiplying it by an integer.<jupyter_code>(1, 2, 3) * 5<jupyter_output><empty_output><jupyter_text>SetsSets in Python are similar but slightly different than lists or tuples. Python sets are **unordered** sequences of objects that only contain **unique** elements. Sets are **mutable** and you may modify them after creation.  Syntax for creating a setSets are created with curly braces followed by comma separated values. Let's see some examples of creating a set.<jupyter_code>a = {1, 2, 3, 4}
a
b = {'sdf', 'er', 1, 3}

b<jupyter_output><empty_output><jupyter_text>Cannot have duplicates when creating a setSets only contain unique elements, so if we try and create a set with repeated values, then only a single copy of the element will remain in the set.<jupyter_code>{1, 1, 1, 1, 2, 2, 2}<jupyter_output><empty_output><jupyter_text>Sets can only contain immutable objectsOne major difference between sets and other data structures is that the elements must be immutable objects. For instance, if we try and add a list to our set we get the following error:<jupyter_code>{1, 2, ['some', 'list']}<jupyter_output><empty_output><jupyter_text>Accessing items in a setSince sets are unordered, there is no way to access a specific element within the set like with tuples/lists. You cannot use the brackets and place an integer index within. You will get the following error:<jupyter_code>s = {1, 2, 3}

s[0]<jupyter_output><empty_output><jupyter_text>How are sets useful?One of the primary uses for sets is to keep a unique collection of items. For instance, you are rolling a pair of dice and want to keep track of all the unique rolls. Another primary use of sets is to quickly test membership of a particular item. For instance, if we want to test whether a particular integer is a member of a set we can do so very quickly as compared to a list.Multiple sets may be combined analogously to mathematical sets as unions, intersections, and differences. Speed of setsSets are implemented using hash tables which allow for extremely fast membership checking. This is different than checking membership in a list where all the elements must be checked one at a time. Using %timeit magic functioniPython comes with some nifty extra functionality called 'magic' functions. The %timeit magic function allows you to time a code block in your notebook. The example below shows how much faster membership checking is for sets than it is for lists. A list and a set are created with the exact same one million elements. The number 900,000 will be checked for membership in each object. To time a single line of code, proceed the line by `%timeit`. Check for membershipWe will first create both a set and a list of one million integers and check for membership with one particular integer. We will time this operation on both the list and set.<jupyter_code>n = 1000000
a_set = set(range(n))
a_list = list(range(n))
num = 900000
%timeit num in a_set
%timeit num in a_list<jupyter_output><empty_output><jupyter_text>Unbelievable time difference!On my machine, the membership check took 65 nanoseconds (one nanosecond is a billionth of a second) vs the list's 11 milliseconds (one thousandth of a second). That is over 100,000 times faster for the set than the list.One astonishing fact about sets is that regardless of large the set becomes, membership checking will be very fast and remain just about the same amount of time.  Set OperationsSee examples below on basic operations of sets. Let's first define two sets.<jupyter_code># define two sets
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}<jupyter_output><empty_output><jupyter_text>Membership checking with `in`Determine whether or not a set contains an element. Use **`not in`** to reverse the condition.<jupyter_code>5 in a
6 not in b<jupyter_output><empty_output><jupyter_text>Find the union of two setsThere is no concatenation of sets, instead we use the term **union** to represent all the unique elements of two sets. You can either use the pipe symbol, **`|`**, or the method **`union`**.<jupyter_code>a | b
a.union(b)<jupyter_output><empty_output><jupyter_text>Intersection of two setsThe intersection of two sets is all the elements they have in common. Use the ampersand symbol, **`&`**, or the **`intersection`** method.<jupyter_code>a & b
a.intersection(b)<jupyter_output><empty_output><jupyter_text>Difference of two setsThe difference between two sets is a non-commutative (meaning that the order matters) operation that removes all elements of the first second that are in common with the second. You can use the subtraction sign or the **`difference`** method. Notice how **`a - b != b - a`**<jupyter_code>a - b
b - a
a.difference(b)<jupyter_output><empty_output><jupyter_text>Symmetric differenceThe symmetric difference is a commutative operation that returns a set with all elements unique to each one. You can use the caret symbol, **`^`** or the **`symmetric_difference`** method.<jupyter_code>a ^ b
b ^ a
a.symmetric_difference(b)
a ^ b<jupyter_output><empty_output><jupyter_text>Mutating setsAll of the above examples created new sets but did not change the underlying sets. Sets are mutable and we can add or remove elements as we please.The **`add`** method will permanently add a new element to our set, granted it is not already there.<jupyter_code># add an element to set 
a.add(10) # operation happens in place
b.add(6) # 6 is already a member so no item is added
print(a)
print(b)<jupyter_output><empty_output><jupyter_text>Problem 21Define a set and think of a function that will output the number of elements in that set.<jupyter_code># your code here<jupyter_output><empty_output><jupyter_text>DictionariesDictionaries are powerful and flexible data structures similar to lists, tuples, and sets. Dictionaries consist of a **pair** of objects. Each dictionary item is a mapping from a **`key`** to a **`value`**, often called **key value pairs**. Every key has exactly one value that is associated with it.Dictionaries are very similar to their lexical counterparts where each word is mapped to a definition. In Python terms, the word would be the **`key`** and the definition the **`value`**. Dictionary SyntaxDictionaries are defined using the same curly braces as sets, but each item in a dictionary consists of a key value pair separated by a colon. Each item in a dictionary is separated by a comma. As with sets, the keys of a dictionary must be immutable (ints, strings, tuples, etc...). The values however, may be an object of any type.Let's see some examples of dictionaries defined using curly braces<jupyter_code># Defining dictionaries
letter_dict = {'a': 1, 'b': 2, 'z': 26}

num_to_word_dict = {1:'one', 2:'two', 234:'two-hundred thirty four'}

city_coord_dict = {(29, 95):'Houston', (29, 90):'New Orleans'}<jupyter_output><empty_output><jupyter_text>Dictionary Values can be AnythingDictionaries are key value pairs where the key is a hashable object. The value can be any Python object including lists or even other dictionaries.Let's say we are teachers with students that have test score grades. A dictionary is an excellent data structure to keep track of the scores. Let's manually create some data with 3 students that each have 3 test scores<jupyter_code>students = {'Sally': [87, 76, 65], 'Jane' : [45, 98, 77], 'Adeline' : [65, 22, 10]}
students<jupyter_output><empty_output><jupyter_text>Selecting values of a dictionaryThe most common dictionary operation is to select one particular value from it. To do this we place the key inside of the brackets. The associated value is returned.Let's select some values with their associated key from a few dictionaries.<jupyter_code># redefine some dictionaries from above
letter_dict = {'a': 1, 'b': 2, 'z': 26}

num_to_word_dict = {1:'one', 2:'two', 234:'two-hundred thirty four'}

city_coord_dict = {(29, 95):'Houston', (29, 90):'New Orleans'}
# retreiving items
letter_dict['a']
letter_dict['z']
city_coord_dict[(29, 95)]
num_to_word_dict[234]<jupyter_output><empty_output><jupyter_text>KeyErrorIf you try and select a value from your dictionary with a key that does not exist, you will get a **`KeyError`**.<jupyter_code># first see what happens when a key is not in the dictionary
letter_dict['c']<jupyter_output><empty_output><jupyter_text>There is no integer location in dictionariesLists and tuples are ordered sequences of objects with syntax that allows you to select elements by their integer location. Technically, as of Python 3.7, dictionaries are also ordered, but you still cannot select values by integer location.Attempting to select the first value with **`0`** like with lists is going to yield a **`KeyError`** (unless the dictionary actually has **`0`** as one of its keys).<jupyter_code>num_to_word_dict[0]<jupyter_output><empty_output><jupyter_text>Dictionary Membership CheckingCheck membership (of the key) the same way as with sets with the `in` operator. Speed is just as fast as with sets.<jupyter_code># Test whether 'Tom' is a student
'Tom' in students<jupyter_output><empty_output><jupyter_text>Get just the keys and values separatelyThe below methods retrieve the keys and values.<jupyter_code># Get just the keys
students.keys()
# get just the values
students.values()<jupyter_output><empty_output><jupyter_text>Mutating DictionariesDictionaries are mutable and new key:value pairs can be added, deleted, and updated at any time after creation.<jupyter_code># Define a dictionary
students = {'Sally': [87, 76, 65], 'Jane' : [45, 98, 77], 'Adeline' : [65, 22, 10]}
# add a new student key:value pair
students['Penelope'] = [100, 98, 90]

students
# delete a student
del students['Sally']

students
# Change all of the scores of a single student
students['Adeline'] = [87, 56, 88]

students
# change a single test score of a student
students['Penelope'][2] = 99
students
# add a key:value pair of completely different type
students[0] = 'zero'

students<jupyter_output><empty_output><jupyter_text>Iterate through dictionariesOne of the most common operations on a dictionary is to loop (iterate) through each key, value pair. There are multiple ways to iterate through dictionaries. You can iterate through just the keys, just the values, and both the keys and the values simultaneously.Our first example will loop through only the keys. The **`for`** loop is written exactly how it is with a list or a tuple. The values are not accessible in this manner.<jupyter_code># redefine dictionary
test_dict = {'Sally': [87, 76, 65], 'Jane' : [45, 98, 77], 'Adeline' : [65, 22, 10]}

for key in test_dict:
    print(key)<jupyter_output><empty_output><jupyter_text>Looping through key value pairsThe above example is the default iteration that Python provides for dictionary. You are only given access to the **`key`**. To get access to both the key and the value we must use the **`items`** method like this:<jupyter_code>for key, value in test_dict.items():
    print(key)
    print(value)<jupyter_output><empty_output><jupyter_text>Looping to find the average scoreWe can use the above example to find the average score for each student. In the below example, the variable **`student`** is assigned to the key and **`scores`** is assigned to the value (a list in this case). Remember that the loop variables can be any name of your choice.<jupyter_code># define students again with test scores
students = {'Sally': [87, 76, 65], 'Jane' : [45, 98, 77], 'Adeline' : [65, 22, 10]}

for student, scores in students.items():
    avg_score = sum(scores) / len(scores)
    print("{}'s average score is {}".format(student, avg_score))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08041825095057034, 'fraction_numerical': 0.013418884664131813, 'mean_word_length': 5.3329988963579815, 'pattern_counts': {'":': 0, '<': 543, '<?xml version=': 0, '>': 570, 'https://': 12, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '134', 'dir': 'jupyter-structured-clean-dedup', 'id': '454446', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 18378, 'n_tokens_neox': 17341, 'n_words': 8529, 'path': 'Intro to Python Tutorial.ipynb', 'repo_name': 'jamocko/Intro-to-Python', 'size': '63134'}