class: title-slide, left, bottom # Lecture 13 ---- ## **DANL 100: Programming for Data Analytics** ### Byeong-Hak Choe ### October 13, 2022 --- # Announcement ### <p style="color:#00449E"> Schedules up to Midterm </p> .panelset[ .panel[.panel-name[Online Class on Oct. 18] - On Tuesday, October 18, 2022, we will have an **asynchronous** online class. - On Canvas, I will leave the web-link for the class recording on Monday, October 17, 2022. - Feel free to email me if you have any questions from the online class on Tuesday, October 18, 2022. - You can also ask me any questions in person on Thursday, October 20, 2022 during our class time. ] .panel[.panel-name[Office Hours] - I will not have office hours on Monday and Wednesday, October 17 and 19, 2022. - Instead, I will have office hours on **Friday, October 21, 2022**, from 1:30 P.M. to 5:30 P.M at my office, South Hall 117B. ] .panel[.panel-name[Homework 1 & 2] - Evaluation criteria for Homework Assignment 1 include ... - How relevant your Python code is. - Whether or not your Python codes/comments use required components. - Whether or not your Python code works. - Do not leave your answer empty: - There is always a plenty of partial credits for homeworks and exams! - Homework Assignment 2 will be posted during this weekend. - Due is October 24, 11:59 P.M., Eastern Time. ] .panel[.panel-name[Midterm] - The Midterm Exam will be on Thursday, October 27, 2022, class time. - Please email me as soon as possible if you need to re-schedule the midterm exam. ] ] --- # Workflow ### <p style="color:#00449E"> Shortcuts for Switching Apps</p> .pull-left[ **Mac** - **command + Tab** allows us to switch between apps quickly. - If we use the full-screen mode of an app, the trackpad gesture of "three finger drag" switches across the screens. ] .pull-right[ **Windows** - **Alt + Tab** allows us to switch between apps quickly. - Trackpad gestures might be supported depending on laptops or versions of Windows. ] --- # Workflow ### <p style="color:#00449E"> Shortcuts </p> - **F9** runs a current line (where the blinking cursor bar is) or selected lines. - **Home/End** moves the blinking cursor bar to the beginning/end of the line. - **Fn + **
/
works too. - **PgUp/PgDn** moves the blinking cursor bar to the top/bottom line of the script on the screen. - **Fn + **
/
works too. --- # Workflow ### <p style="color:#00449E"> Shortcuts </p> - **Ctrl + Enter/Return** runs a current cell, defined by `# %%`. - Incidentally, **Ctrl + Enter/Return** is the keyboard shortcut for running the **selection** (or **current line**) in R Studio, not Spyder, which may be where I sometimes got confused. - If you prefer using **Ctrl** (or **command**) **+ Return**, you can go to Tools -> Preferences -> Keyboard shortcuts. Search for 'run selection', double click and set **Ctrl/command+Return** as the 'New shortcut'. - In my Spyder on Mac, I set **command + Return** for *running selection*, and **F9** for *running cell*. --- # Workflow ### <p style="color:#00449E"> Shortcuts </p> .pull-left[ ### <p style="color:#00449E"> Mac </p> - **command + N** opens a new script. - **command + 1** is the shortcut for `#`. - **command + 4** is the shortcut for block comment. ] .pull-right[ ### <p style="color:#00449E"> Windows </p> - **Ctrl + N** opens a new script. - **Ctrl + 1** is the shortcut for `#`. - **Ctrl + 4** is the shortcut for block comment. ] - `# %%` defines a coding block in Spyder IDE. --- # Workflow ### <p style="color:#00449E"> More Shortcuts </p> - **Ctrl** (**command** for Mac Users) **+ Z** undoes the previous action. - **Ctrl** (**command** for Mac Users) **+ Shift + Z** redoes when undo is executed. - **Ctrl** (**command** for Mac Users) **+ F** is useful when finding a phrase in the script. - **Ctrl** (**command** for Mac Users) **+ R** is useful when replacing a specific phrase with something in the script. - **Ctrl** (**command** for Mac Users) **+ D** deletes a current line. --- class: inverse, center, middle # Lists <html><div style='float:left'></div><hr color='#EB811B' size=1px width=796px></html> --- # Lists ### <p style="color:#00449E"> </p> .panelset[ .panel[.panel-name[`=`] ### Assign with `=` - When we assign one list to more than one variable, changing the list in one place also changes it in the other: .pull-left[ ```python a = [1, 2, 3] b = a b a[0] = 'suprise' a ``` - So what’s in `b` now? ] .pull-right[ ```python b[0] = 'I hate suprises' b ``` - So what’s in `a` now? ] ] .panel[.panel-name[copy] ### Copy with copy(), list(), or a Slice We can copy the values of a list to an independent, fresh list by using (1) the list `copy()` method, (2) the `list()` conversion function, or (3) the list slice `[:]`: .pull-left[ ```python a = [1, 2, 3] b = a.copy() c = list(a) d = a[:] a[0] = 'integer lists' ``` ] .pull-right[ - `b`, `c`, and `d` are copies of `a`: - They are new objects with their own values and no connection to the original list object [1, 2, 3] to which `a` refers. - What are `b`, `c`, and `d` now? ] ] .panel[.panel-name[deepcopy] ### Copy with copy(), list(), or a Slice - Using (1) the list `copy()` method, (2) the `list()` conversion function, or (3) the list slice `[:]` works well if the list values are all *immutable*. .pull-left[ ```python a = [1, 2, [8, 9]] b = a.copy() c = list(a) d = a[:] a[2][1] = 10 ``` ] .pull-right[ - `b`, `c`, and `d` are copies of `a`. - The value of `a[2]` is now a list, and its elements can be changed. - What are `b`, `c`, and `d` now? ] ] .panel[.panel-name[`deepcopy()`] ### Copy with copy(), list(), or a Slice - To fix these *shallow* copies, we need to use the `deepcopy()` function: .pull-left[ ```python import copy a = [1, 2, [8, 9]] b = copy.deepcopy(a) a[2][1] = 10 a b ``` ] .pull-right[ - `deepcopy()` can handle deeply nested lists, dictionaries, and other objects. - We'll discuss more about `import` soon. ] ] ] --- # Lists ### <p style="color:#00449E"> Iterate with `for` and `in` </p> .panelset[ .panel[.panel-name[`for`] - `for` loop is quite common to iterate over lists. ```python cheeses = ['brie', 'gjetost', 'havarti'] for cheese in cheeses: print(cheese) ``` ] .panel[.panel-name[`break`] - `break` ends the for loop and `continue` steps to the next iteration: ```python cheeses = ['brie', 'gjetost', 'havarti'] for cheese in cheeses: if cheese.startswith('g'): print("I won't eat anything that starts with 'g'") break else: print(cheese) ``` ] .panel[.panel-name[`else` (1)] - We can still use the optional `else` if the for completed without a `break`: ```python cheeses = ['brie', 'gjetost', 'havarti'] for cheese in cheeses: if cheese.startswith('x'): print("I won't eat anything that starts with 'x'") break else: print(cheese) else: print("Didn't find anything that started with 'x'") ``` ] .panel[.panel-name[`else` (2)] - If the initial `for` never ran, control goes to the `else`: ```python cheeses = [] for cheese in cheeses: print('This shop has some lovely', cheese) break else: # no break means no cheese print('This is not much of a cheese shop, is it?') ``` ] ] --- # Lists ### <p style="color:#00449E"> Iterate Multiple Sequences with `zip()` </p> .panelset[ .panel[.panel-name[`for` and `zip()`] - There’s one more nice iteration trick: iterating over multiple sequences in parallel by using the `zip()` function. ```python days = ['Monday', 'Tuesday', 'Wednesday'] fruits = ['banana', 'orange', 'peach'] drinks = ['coffee', 'coffee', 'coffee'] desserts = ['tiramisu', 'ice cream', 'pie', 'pudding'] for day, fruit, drink, dessert in zip(days, fruits, drinks, desserts): print(day, ": drink", drink, "- eat", fruit, "- enjoy", dessert) ``` ] .panel[.panel-name[`zip()`, `list()`, and `dict()`] - We can use `zip()` to walk through multiple sequences and make tuples from items at the same offsets. ```python english = 'Monday', 'Tuesday', 'Wednesday' french = 'Lundi', 'Mardi', 'Mercredi' list( zip(english, french) ) dict( zip(english, french) ) ``` - We will discuss soon how the `dict()` function can create dictionaries from two-item sequences like tuples, lists, or strings. ] ] --- # Lists ### <p style="color:#00449E"> Create a List with a Comprehension </p> .panelset[ .panel[.panel-name[basic] - *List comprehensions* are a convenient and widely used Python language feature. - The simplest form of list comprehension looks like this: <img src="../lec_figs/int-py-list-comprehension.png" width="66%" style="display: block; margin: auto;" /> ] .panel[.panel-name[example 1] ```python number_list = [number for number in range(1,6)] number_list ``` - In the first line, we need the first `number` variable to produce values for the list: that is, to put a result of the loop into `number_list`. - The second `number` is part of the for loop. ] .panel[.panel-name[example 2] - To show that the first `number` in example 1 is an expression, try this variant: ```python number_list = [number - 1 for number in range(1,6)] number_list ``` - The list comprehension moves the loop inside the square brackets. ] .panel[.panel-name[with if] - A list comprehension can include a conditional expression, looking something like this: <img src="../lec_figs/int-py-list-comprehension-if.png" width="50%" style="display: block; margin: auto;" /> - Let’s make a new comprehension that builds a list of only the odd numbers between 1 and 5. ```python a_list = [number for number in range(1,6) if number % 2 == 1] ``` ] .panel[.panel-name[traditional `for`] - Now, the comprehension is a little more compact than its traditional counterpart: ```python a_list = [] for number in range(1,6): if number % 2 == 1: a_list.append(number) ``` ] ] --- # Lists ### <p style="color:#00449E"> Create a List with a Comprehension </p> .panelset[ .panel[.panel-name[nested `for`] - Just as there can be nested loops, there can be more than one set of `for ...` clauses in the corresponding comprehension. ```python rows = range(1,4) cols = range(1,3) for row in rows: for col in cols: print(row, col) ``` ] .panel[.panel-name[comprehension using nested `for`] - Let’s use a comprehension and name it as `cells`, making it a list of `(row, col)` tuples: ```python rows = range(1,4) cols = range(1,3) cells = [(row, col) for row in rows for col in cols] for cell in cells: print(cell) ``` ] .panel[.panel-name[tuple unpacking] - We can also use *tuple unpacking* to get the `row` and `col` values from each tuple as we iterate over the `cells` list. ```python rows = range(1,4) cols = range(1,3) cells = [(row, col) for row in rows for col in cols] for row, col in cells: print(row, col) ``` ] ] --- # Lists ### <p style="color:#00449E"> Lists of Lists </p> - Lists can contain elements of different types, including other lists: ```python small_birds = ['hummingbird', 'finch'] extinct_birds = ['dodo', 'passenger pigeon', 'Norwegian Blue'] carol_birds = [3, 'French hens', 2, 'turtledoves'] all_birds = [small_birds, extinct_birds, 'macaw', carol_birds] ``` - So what does `all_birds`, a list of lists, look like? ```python all_birds all_birds[0] all_birds[1] all_birds[1][0] # [0] refers to the first item in that inner list. ``` --- # Tuples and Lists ### <p style="color:#00449E"> Tuples Versus Lists </p> - We can often use tuples in place of lists, but tuples have many fewer functions—there is no `append()`, `insert()`, and so on—because they can’t be modified after creation. - Why not just use lists instead of tuples everywhere? - Tuples use less space. - We can’t clobber tuple items by mistake. - We can use tuples as dictionary keys, which we will cover soon. - In everyday programming, we would use *lists* and *dictionaries* more. --- # Tuples and Lists ### <p style="color:#00449E"> Class Exercises 1 </p> 1. Create a list called `years_list`, starting with the year of your birth, and each year thereafter until the year of your fifth birthday. For example, if you were born in 2003, the list would be years_list = [2003, 2004, 2005, 2006, 2007, 2008]. 2. In which of these years was your third birthday? Remember, you were 0 years of age for your first year. 3. In which year in `years_list` were you the oldest? --- # Tuples and Lists ### <p style="color:#00449E"> Class Exercises 2 </p> 1. Make a list called `things` with these three strings as elements: `"mozzarella"`, `"cinderella"`, `"salmonella"`. 2. Capitalize the element in `things` that refers to a person and then print the list. Did it change the element in the list? 3. Make the cheesy element of `things` all uppercase and then print the list. 4. Delete the disease element of `things`, and then print the list. --- # Tuples and Lists ### <p style="color:#00449E"> Class Exercises 3 </p> 1. Create a list called `surprise` with the elements `"Groucho"`, `"Chico"`, and `"Harpo"`. 2. Lowercase the last element of the `surprise` list, reverse it, and then capitalize it. --- # Tuples and Lists ### <p style="color:#00449E"> Class Exercises 4 </p> 1. Use a list comprehension to make a list called `even` of the even numbers in `range(10)`. --- class: inverse, center, middle # Dictionaries and Sets <html><div style='float:left'></div><hr color='#EB811B' size=1px width=796px></html> --- # Dictionaries - A *dictionary* is similar to a list, but the order of items doesn’t matter, and they aren’t selected by an offset such as 0 or 1. - Instead, we specify a unique *key* to associate with each value. - This *key* is often a string, but it can actually be any of Python’s immutable types: boolean, integer, float, tuple, string, and others. - Dictionaries are mutable, so we can add, delete, and change their key-value elements. --- # Dictionaries ### <p style="color:#00449E"> Create with `{}` </p> - To create a dictionary, we place curly brackets (`{}`) around comma-separated **key : value** pairs. ```python empty_dict = {} empty_dict ``` Let’s make a small dictionary with quotes from Ambrose Bierce’s *The Devil’s Dictionary*: ```python bierce = {"day": "A period of twenty-four hours, mostly misspent", "positive": "Mistaken at the top of one's voice", "misfortune": "The kind of fortune that never misses"} bierce ``` --- # Dictionaries ### <p style="color:#00449E"> Create with `dict()` </p> - We can also create a dictionary by passing named arguments and values to the `dict()` function. ```python acme_customer = {'first': 'Wile', 'middle': 'E', 'last': 'Coyote'} acme_customer acme_customer = dict(first = "Wile", middle = "E", last = "Coyote") acme_customer ``` - The argument names in `dict()` need to be legal variable names (no spaces, no reserved words): ```python x = dict(name="Elmer", def="hunter") ``` --- # Dictionaries ### <p style="color:#00449E"> Convert with `dict()` </p> - We can use `dict()` to convert two-value sequences into a dictionary. - We might run into such key-value sequences at times, such as "Geneseo, 20, Brockport, 14." - The first item in each sequence is used as the key and the second as the value. .panelset[ .panel[.panel-name[lol] - *lol* (a list of two-item lists): ```python lol = [ ['a', 'b'], ['c', 'd'], ['e', 'f'] ] dict(lol) ``` ] .panel[.panel-name[lot] - *lot* (a list of two-item tuples): ```python lot = [ ('a', 'b'), ('c', 'd'), ('e', 'f') ] dict(lot) ``` ] .panel[.panel-name[tol] - *tol* (a tuple of two-item lists): ```python tol = ( ['a', 'b'], ['c', 'd'], ['e', 'f'] ) dict(tol) ``` ] .panel[.panel-name[los] - *los* (a list of two-character strings): ```python los = [ 'ab', 'cd', 'ef' ] dict(los) ``` ] .panel[.panel-name[tos] - *tos* (a tuple of two-character strings): ```python tos = ( 'ab', 'cd', 'ef' ) dict(tos) ``` ] ] --- # Dictionaries ### <p style="color:#00449E"> Add or Change an Item by `[ key ]` </p> - Adding an item to a dictionary is not complicated. - Just refer to the item by its *key* and assign a *value*. - If the key was already present in the dictionary, the existing value is replaced by the new one. - If the key is new, it’s added to the dictionary with its value. --- # Dictionaries ### <p style="color:#00449E"> Add or Change an Item by `[ key ]` </p> .panelset[ .panel[.panel-name[st_city] - Let's make a dictionary of some pairs of US cities and states, using their states as *keys* and city names as *values*: ```python st_city = { 'MN': 'Rochester', 'WY': 'Laramie', 'CO': 'Denver' } st_city ``` ] .panel[.panel-name[Rocjester] - Here, we want to add Rochester, NY. - Here’s an attempt to add it, but it spelled wrong: ```python st_city['NY'] = 'Rocjester' st_city ``` ] .panel[.panel-name[Rochester] - Here's some repair code: ```python st_city['NY'] = 'Rochester' st_city ``` ] .panel[.panel-name[city_st] - Remember that dictionary keys must be unique. - That’s why we used state names for keys instead of city names. - If we use a key more than once, the last value wins: ```python city_st = { 'Rochester': 'MN', 'Laramie': 'WY', 'Denver': 'CO', 'Rochester': 'NY' } city_st ``` ] ] --- # Dictionaries ### <p style="color:#00449E"> Get an Item by `[key]` or with `get()` </p> .panelset[ .panel[.panel-name[`[key]`] - We specify the dictionary and key to get the corresponding value: ```python city_st = { 'Rochester': 'MN', 'Laramie': 'WY', 'Denver': 'CO', 'Rochester': 'NY' } city_st['Denver'] ``` ] .panel[.panel-name[`[key]`] - If the key is not present in the dictionary, we'll get an exception: ```python city_st['Buffalo'] ``` - There are two good ways to avoid this: `in` and `get()` ] .panel[.panel-name[`in`] - Test for the key at the outset by using `in`: ```python 'Buffalo' in city_st ``` ] .panel[.panel-name[`get()`] - Use the special dictionary `get()` function. - If the key exists, we get its value: ```python city_st.get('Denver') ``` ] .panel[.panel-name[`get()`] - Use the special dictionary `get()` function. - If the key does not exist and the optional value is provided, we get the optional value: ```python city_st('Buffalo', 'Not in city_st') ``` ] .panel[.panel-name[`get()`] - Use the special dictionary `get()` function. - If the key does not exist and the optional value is not provided, we get `None`, which displays nothing in the console. ```python city_st('Buffalo') ``` ] ] --- # Dictionaries .panelset[ .panel[.panel-name[`keys()`] ### <p style="color:#00449E"> Get All Keys with `keys()` </p> - We can use `keys()` to get all of the keys in a dictionary. ```python signals = {'green': 'go', 'yellow': 'go faster', 'red': 'smile for the camera'} signals.keys() list( signals.keys() ) # to turn the result into a list object. ``` ] .panel[.panel-name[`values()`] ### <p style="color:#00449E"> Get All Values with values() </p> - To obtain all the values in a dictionary, use `values()`: ```python signals = {'green': 'go', 'yellow': 'go faster', 'red': 'smile for the camera'} signals.values() list( signals.values() ) # to turn the result into a list object. ``` ] .panel[.panel-name[`items()`] ### <p style="color:#00449E"> Get All Key-Value Pairs with `items()` </p> - When we want to get all the key-value pairs from a dictionary, use the `items()` function: ```python signals = {'green': 'go', 'yellow': 'go faster', 'red': 'smile for the camera'} signals.items() list( signals.items() ) # to turn the result into a list object. ``` ] .panel[.panel-name[`items()`] ### <p style="color:#00449E"> Get Length with `len()` </p> - Count your key-value pairs: ```python signals = {'green': 'go', 'yellow': 'go faster', 'red': 'smile for the camera'} len(signals) ``` ] .panel[.panel-name[{**a, **b}] ### <p style="color:#00449E"> Combine Dictionaries with `{**a, **b}` </p> - Starting with Python 3.5, there’s a new way to merge dictionaries, using the `**`: ```python first = {'a': 'agony', 'b': 'bliss'} second = {'b': 'bagels', 'c': 'candy'} {**first, **second} third = {'d': 'donuts'} {**first, **third, **second} ``` ] .panel[.panel-name[`update()`] ### <p style="color:#00449E"> Combine Dictionaries with `update()` </p> - We can use the `update()` function to copy the keys and values of one dictionary into another. - Let's update `st_city` with `other_city` ```python st_city = { 'MN': 'Rochester', 'WY': 'Laramie', 'CO': 'Denver', 'NY': 'Rochester' } other_city = {'MA': 'Boston'} st_city.update(other_city) ``` ] .panel[.panel-name[`update()`] ### <p style="color:#00449E"> Combine Dictionaries with `update()` </p> - What happens if the second dictionary has the same key as the dictionary into which it’s being merged? ```python first = {'a': 1, 'b': 2} second = {'b': 'platypus'} first.update(second) first ``` ] ] --- # Dictionaries .panelset[ .panel[.panel-name[`del`] ### <p style="color:#00449E"> Delete an Item by Key with `del` </p> - We can delete an item in a dictionary with `del`: ```python del st_city['NY'] st_city ``` ] .panel[.panel-name[`pop()`] ### <p style="color:#00449E"> Get an Item by Key and Delete It with `pop()` </p> - `pop()` combines `get()` and `del`. - If we give `pop()` a key and it exists in the dictionary, it returns the matching value and deletes the key-value pair. - If the key doesn’t exist but we give `pop()` a second default argument, all is well and the dictionary is not changed: ```python len(st_city) st_city.pop('MN') # Check len(st_city) st_city.pop('MN') # If it doesn’t exist, it raises an exception: st_city.pop('MN', 'Not exist') ``` ] .panel[.panel-name[`clear()`] ### <p style="color:#00449E"> Delete All Items with `clear()` </p> - To delete all keys and values from a dictionary, use `clear()` or just reassign an empty dictionary (`{}`) to the name: ```python st_city.clear() st_city st_city = { 'MN': 'Rochester', 'WY': 'Laramie', 'CO': 'Denver', 'NY': 'Rochester' } st_city = {} st_city ``` ] .panel[.panel-name[`in`] ### <p style="color:#00449E"> Test for a Key with `in` </p> - If we want to know whether a key exists in a dictionary, use `in`: ```python st_city = { 'MN': 'Rochester', 'WY': 'Laramie', 'CO': 'Denver', 'NY': 'Rochester' } 'NY' in st_city 'WY' in st_city 'CA' in st_city ``` ] .panel[.panel-name[`=`] ### <p style="color:#00449E"> Assign with `=` </p> - If we make a change to a dictionary, it will be reflected in all the names that refer to it: ```python signals = { 'green': 'go', 'yellow': 'go faster', 'red': 'smile for the camera' } save_signals = signals signals['blue'] = 'confuse everyone' signals ``` - What are in `save_signals` now? ] .panel[.panel-name[`copy()`] ### <p style="color:#00449E"> Assign with `=` </p> - To actually copy keys and values from a dictionary to another dictionary and avoid this, we can use `copy()`: ```python signals = { 'green': 'go', 'yellow': 'go faster', 'red': 'smile for the camera' } original_signals = signals.copy() signals['blue'] = 'confuse everyone' signals ``` - What are in `original_signals` now? ] .panel[.panel-name[`(1)`] ### <p style="color:#00449E"> Copy Everything with `deepcopy()` </p> - Suppose that the value for *red* in `signals` was a list instead of a single string: ```python signals = { 'green': 'go', 'yellow': 'go faster', 'red': ['stop', 'smile'] } signals_copy = signals.copy() signals ``` - What are in `signals_copy`? ] .panel[.panel-name[`(2)`] ### <p style="color:#00449E"> Copy Everything with `deepcopy()` </p> - Let’s change one of the values in the *red* list: ```python signals = { 'green': 'go', 'yellow': 'go faster', 'red': ['stop', 'smile'] } signals_copy = signals.copy() signals['red'][1] = 'sweat' signals ``` - What are in `signals_copy`? ] .panel[.panel-name[`deepcopy()`] ### <p style="color:#00449E"> Copy Everything with `deepcopy()` </p> - To avoid this, we can use `deepcopy()`: ```python import copy signals = { 'green': 'go', 'yellow': 'go faster', 'red': ['stop', 'smile'] } signals_deepcopy = copy.deepcopy(signals) signals signals_deepcopy signals['red'][1] = 'sweat' signals ``` - What are in `signals_deepcopy`? ] ] --- # Dictionaries .panelset[ .panel[.panel-name[comparison] ### <p style="color:#00449E"> Compare Dictionaries </p> - Much like lists and tuples, dictionaries can be compared with the simple comparison operators `==` and `!=`: ```python a = {1:1, 2:2, 3:3} b = {3:3, 1:1, 2:2} a == b a != b a <= b # does it work? ``` ] .panel[.panel-name[`for`] ### <p style="color:#00449E"> Iterate with `for` and `in` </p> - Iterating over a dictionary (or its `keys()` function) returns the keys. ```python accusation = {'room': 'ballroom', 'weapon': 'lead pipe', 'person': 'Col. Mustard'} for card in accusation: # or, for card in accusation.keys(): print(card) ``` ] .panel[.panel-name[`values()`] ### <p style="color:#00449E"> Iterate with `for` and `in` </p> - To iterate over the values rather than the keys, we use the dictionary’s `values()` function: ```python accusation = {'room': 'ballroom', 'weapon': 'lead pipe', 'person': 'Col. Mustard'} for value in accusation.values(): print(value) ``` ] .panel[.panel-name[`items()`] ### <p style="color:#00449E"> Iterate with `for` and `in` </p> - To return both the key and value as a tuple, we can use the `items()` function: ```python accusation = {'room': 'ballroom', 'weapon': 'lead pipe', 'person': 'Col. Mustard'} for item in accusation.items(): print(item) ``` ] .panel[.panel-name[`items()`] ### <p style="color:#00449E"> Iterate with `for` and `in` </p> - For each tuple returned by `items()`, let's assign the first value (the key) to *card*, and the second (the value) to *contents*: ```python accusation = {'room': 'ballroom', 'weapon': 'lead pipe', 'person': 'Col. Mustard'} for card, contents in accusation.items(): print('Card', card, 'has the contents', contents) ``` ] .panel[.panel-name[comprehensions] ### <p style="color:#00449E"> Dictionary Comprehensions </p> - Dictionaries also have comprehensions. - The simplest form of dictionary comprehension looks like this: <img src="../lec_figs/int-py-dict-comprehension.png" width="80%" style="display: block; margin: auto;" /> ] .panel[.panel-name[comprehensions] ### <p style="color:#00449E"> Dictionary Comprehensions </p> - Let's run a loop over each of the seven letters in the string 'letters' and counting how many times that letter appears. ```python word = 'letters' letter_counts = {letter: word.count(letter) for letter in word} letter_counts ``` - How many times do we count 't' here? ] .panel[.panel-name[comprehensions w/ `set()`] ### <p style="color:#00449E"> Dictionary Comprehensions </p> - Let's run a loop over each of the seven letters in the string 'letters' and counting how many times that letter appears. ```python word = 'letters' letter_counts = {letter: word.count(letter) for letter in set(word)} letter_counts ``` - How many times do we count 't' here? ] .panel[.panel-name[comprehensions w/ if] ### <p style="color:#00449E"> Dictionary Comprehensions </p> - Dictionary comprehensions can also have *if* tests and multiple *for* clauses: <img src="../lec_figs/int-py-dict-comprehension-if.png" width="95%" style="display: block; margin: auto;" /> ```python vowels = 'aeiou' word = 'onomatopoeia' vowel_counts = {letter: word.count(letter) for letter in set(word) if letter in vowels} vowel_counts ``` ] ] --- # Sets - A set is like a dictionary with its values thrown away, leaving only the keys. - As with a dictionary, each key must be unique. - We use a set when we only want to know that something exists, and nothing else about it. - Use a dictionary if we want to attach some information to the key as a value. --- # Sets ### <p style="color:#00449E"> Common things to do with sets </p> <img src="../lec_figs/int-py-fig8-1.png" width="55%" style="display: block; margin: auto;" /> - The null or empty set is a set with zero elements. --- # Sets .panelset[ .panel[.panel-name[`set()`] ### <p style="color:#00449E"> Create with `set()` </p> - To create a set, we use the `set()` function or enclose one or more comma-separated values in curly brackets: ```python empty_set = set() empty_set even_numbers = {0, 2, 4, 6, 8} even_numbers odd_numbers = {1, 3, 5, 7, 9} odd_numbers ``` ] .panel[.panel-name[`set()`] ### <p style="color:#00449E"> Convert with `set()` </p> - We can create a set from a list, string, tuple, or dictionary, discarding any duplicate values. ```python set( 'letters' ) set( ['Dasher', 'Dancer', 'Prancer', 'Mason-Dixon'] ) set( ('Ummagumma', 'Echoes', 'Atom Heart Mother') ) set( {'apple': 'red', 'orange': 'orange', 'cherry': 'red'} ) ``` - Sets are unordered. ] .panel[.panel-name[`len()`] ### <p style="color:#00449E"> Get Length with `len()` </p> - Let’s count our *reindeer*: ```python reindeer = set( ['Dasher', 'Dancer', 'Prancer', 'Mason-Dixon'] ) len(reindeer) ``` ] .panel[.panel-name[`add()`] ### <p style="color:#00449E"> Add an Item with `add()` </p> - Throw another item into a set with the set `add()` method ```python s = set((1,2,3)) s s.add(4) s ``` ] .panel[.panel-name[`remove()`] ### <p style="color:#00449E"> Delete an Item with `remove()` </p> - We can delete a value from a set by value: ```python s = set((1,2,3)) s.remove(3) s ``` ] .panel[.panel-name[`for`] ### <p style="color:#00449E"> Iterate with `for` and `in` </p> - We can iterate over all items in a set: ```python furniture = set(('sofa', 'ottoman', 'table')) for piece in furniture: print(piece) ``` ] ] --- # Sets ### <p style="color:#00449E"> Test for a Value with `in` </p> .panelset[ .panel[.panel-name[`drinks`] - Here is a dictionary called `drinks`. - Each key is the name of a mixed drink, and the corresponding value is a set of that drink’s ingredients: - A set is just a bunch of *values*, and a dictionary contains *key : value* pairs. ```python drinks = { 'martini': {'vodka', 'vermouth'}, 'black russian': {'vodka', 'kahlua'}, 'white russian': {'cream', 'kahlua', 'vodka'}, 'manhattan': {'rye', 'vermouth', 'bitters'}, 'screwdriver': {'orange juice', 'vodka'} } ``` ] .panel[.panel-name[`vodka`] - Print drinks that contain vodka: ```python for name, contents in drinks.items(): if 'vodka' in contents: print(name) ``` ] .panel[.panel-name[`vodka`] - Print drinks that neither contain either `cream` nor `vermouth` but do `vodka`: ```python for name, contents in drinks.items(): if 'vodka' in contents \ and not ('vermouth' in contents or 'cream' in contents): print(name) ``` ] ] --- # Sets ### <p style="color:#00449E"> Combinations and Operators </p> .panelset[ .panel[.panel-name[`drinks`] - Here are some examples to consider for set combinations and operations. ```python drinks = { 'martini': {'vodka', 'vermouth'}, 'black russian': {'vodka', 'kahlua'}, 'white russian': {'cream', 'kahlua', 'vodka'} } bruss = drinks['black russian'] wruss = drinks['white russian'] a = {1, 2} b = {2, 3} ``` ] .panel[.panel-name[intersection] - We get the intersection (members common to both sets) with the special punctuation symbol `&`. - The set `intersection()` function does the same. ```python a & b a.intersection(b) bruss & wruss ``` ] .panel[.panel-name[union] - Let's get the union (members of either set) by using `|` or the set `union()` function: ```python a | b a.union(b) bruss | wruss ``` ] .panel[.panel-name[difference] - The difference (members of the first set but not the second) is obtained by using the character `-` or the `difference()` function: ```python a - b a.difference(b) bruss - wruss wruss - bruss ``` ] .panel[.panel-name[exclusive] - The *exclusive* or (items in one set or the other, but not both) uses `^` or `symmetric_difference()`: ```python a ^ b a.symmetric_difference(b) bruss ^ wruss ``` ] .panel[.panel-name[subset] - We can check whether one set is a subset of another (all members of the first set are also in the second set) by using `<=` or `issubset()`: ```python a <= b a.issubset(b) bruss <= wruss a <= a # any set is a subset of itself? a.issubset(a) ``` ] .panel[.panel-name[subset] - To be a *proper subset*, the second set needs to have all the members of the first and more. - Calculate it by using `<`: ```python a < b b < a bruss < wruss ``` ] .panel[.panel-name[superset] - A *superset* is the opposite of a subset (all members of the second set are also members of the first). This uses `>=` or `issuperset()`: ```python a >= b a.issuperset(b) wruss >= bruss a >= a # any set is a superset of itself? a.issuperset(a) ``` ] .panel[.panel-name[superset] - To be a *proper superset*, the first set needs to have all the members of the second and more. - Calculate it by using `>`: ```python a > b wruss > bruss a > a ``` ] .panel[.panel-name[combinations] - What if we want to check for combinations of set values? - Suppose that we want to find any drink that has orange juice or vermouth. - Let’s use the *set intersection operator*, which is an ampersand (`&`): ```python for name, contents in drinks.items(): if contents & {'vermouth', 'orange juice'}: print(name) ``` ] .panel[.panel-name[combinations] - Let’s rewrite the example from the previous section, in which we wanted vodka but neither cream nor vermouth: ```python for name, contents in drinks.items(): if 'vodka' in contents and not contents & {'vermouth', 'cream'}: print(name) ``` ] ] --- # Sets .panelset[ .panel[.panel-name[comprehensions] ### <p style="color:#00449E"> Set Comprehensions </p> - Sets also have comprehensions: <img src="../lec_figs/int-py-set-comprehension.png" width="66%" style="display: block; margin: auto;" /> <img src="../lec_figs/int-py-set-comprehension-if.png" width="83%" style="display: block; margin: auto;" /> ```python a_set = {number for number in range(1,6) if number % 3 == 1} ``` ] .panel[.panel-name[`frozenset()`] ### <p style="color:#00449E"> Create an Immutable Set with `frozenset()` </p> - If we want to create a set that can’t be changed, call the `frozenset()` function with any iterable argument: ```python frozenset([3, 2, 1]) frozenset(set([2, 1, 3])) frozenset({3, 1, 2}) frozenset( (2, 3, 1) ) fs = frozenset([3, 2, 1]) fs fs.add(4) ``` ] ] --- # Data Structures So Far .panelset[ .panel[.panel-name[data structures] - To review, we make: - A list by using square brackets (`[]`) - A tuple by using commas and optional parentheses (`()`) - A dictionary or set by using curly brackets (`{}`) ```python marx_list = ['Groucho', 'Chico', 'Harpo'] marx_tuple = ('Groucho', 'Chico', 'Harpo') marx_dict = {'Groucho': 'banjo', 'Chico': 'piano', 'Harpo': 'harp'} marx_set = {'Groucho', 'Chico', 'Harpo'} ``` ] .panel[.panel-name[single item] - For all but sets, we access a single element with square brackets. - For the list and the tuple, the value between the square brackets is an integer offset. - For the dictionary, we access a single element with a key. ```python marx_list[2] marx_tuple[2] marx_dict['Harpo'] 'Harpo' in marx_set ``` - For the list, the tuple, and the dictionary, the result is a value. - For the set, the result is either there or it’s not; there’s no index or key. ] ] --- # Make Bigger Data Structures .panelset[ .panel[.panel-name[bigger data structures] - We worked up from simple booleans, numbers, and strings to lists, tuples, sets, and dictionaries. - We can combine these built-in data structures into bigger, more complex structures of our own. - Let’s start with three different lists: ```python newyork = ['Geneseo', 'Rochester', 'Buffalo'] california = ['San Francisco', 'Los Angeles', 'San Diego'] texas = ['Dallas', 'Houston', 'Austin'] ``` ] .panel[.panel-name[tuple_of_lists] - We can make a tuple that contains each list as an element: ```python tuple_of_lists = newyork, california, texas tuple_of_lists ``` ] .panel[.panel-name[list_of_lists] - We can make a list that contains the three lists: ```python list_of_lists = [newyork, california, texas] list_of_lists ``` ] .panel[.panel-name[dict_of_lists] - Finally, let’s create a dictionary of lists. - In this example, let’s use the abbreviated name of the state as the key and the list of cities as the value: ```python dict_of_lists = {'NY': newyork, 'CA': california, 'TX': texas} dict_of_lists ``` ] ] --- # Dictionaries and Sets ### <p style="color:#00449E"> Class Exercises </p> *1*. Make an English-to-French dictionary called `e2f` and print it. Here are your starter words: `dog` is `chien`, `cat` is `chat`, and `walrus` is `morse`. *2*. Using your three-word dictionary `e2f`, print the French word for `walrus`. *3*. Make a French-to-English dictionary called `f2e` from `e2f` Use the `items` method. *4*. Print the English equivalent of the French word `chien`. *5*. Print the set of English words from `e2f`. --- # Dictionaries and Sets ### <p style="color:#00449E"> Class Exercises </p> *6*. Make a multilevel dictionary called `life`. Use these strings for the topmost keys: '`animals`', '`plants`', and '`other`'. Make the 'animals' key refer to another dictionary with the keys '`cats`', '`octopi`', and '`emus`'. Make the '`cats`' key refer to a list of strings with the values '`Henri`', '`Grumpy`', and '`Lucy`'. Make all the other keys refer to empty dictionaries. *7*. Print the top-level keys of `life`. *8*. Print the keys for `life['animals']`. *9*. Print the values for `life['animals']['cats']`. --- # Dictionaries and Sets ### <p style="color:#00449E"> Class Exercises </p> *10*. Use a dictionary comprehension to create the dictionary `squares`. Use `range(10)` to return the keys, and use the square of each key as its value. *11*. Use a set comprehension to create the set `odd` from the odd numbers in `range(10)`.