<jupyter_start><jupyter_text>Spotted a mistake? Report it here  Beginner's Python—Session Seven Homework Solutions _As we are moving deeper into the world of Python, the tasks we can complete with our skills can become more and more complicated. For that reason, there are fewer exercises to complete this week but of a slightly harder standard. Don't be put off; these are meant to challenge you._  Dictionaries Create a dictionary with two keys, `x` and `y` the value of each being a numeric list of length 5. Notice, that this is essentially a dataframe/table with two variables.<jupyter_code>dataframe = {
    'x': [1, 3, 5],
    'y': [2, 5, 4]
}<jupyter_output><empty_output><jupyter_text>Ask the user for their name, age, and whether they own a dog. Assign the responses to variables `name`, `age`, and `has_dog`. Remember to convert the inputted age to an integer. The response for the last question should be either `yes` or `no` which should then be converted to `True` or `False` respectively. You can use the example from near the end of the session three presentation as a basis for this.<jupyter_code>name = input("What is your name? ")
age = int(input("What is your age? "))
has_dog = ''
while not has_dog in ('yes', 'no'):
    has_dog = input("Do you have a dog? ")
has_dog = has_dog == 'yes'<jupyter_output>What is your name? Tim
What is your age? 21
Do you have a dog? no<jupyter_text>Create a dictionary containing the users information using the three variables defined above. Print out these values in a meaningful sentence using this dictionary.<jupyter_code>user = {
    'name': name,
    'age': age,
    'has_dog': has_dog
}

if user['has_dog']:
    dog_text = "do"
else:
    dog_text = "do not"
print("Hi, I'm", user['name'] + ".",
      "I am", user['age'], "and I",
      dog_text, "have a dog.")<jupyter_output>Hi, I'm Tim. I am 21 and I do not have a dog.<jupyter_text>Dictionary keys are not limited to strings although there are limits. Try to create dictionaries with keys of type integer, float, Boolean, list, tuple, and dictionary, respectively. Which are allowed and what error do you recieve when a key's type is invalid?<jupyter_code>int_dict = {1: 1}
float_dict = {1.0: 1}
bool_dict = {True: 1}
tuple_dict = {(1, 1): 1}
list_dict = {[1, 1]: 1}
dict_dict = {{1: 1}: 1}<jupyter_output><empty_output><jupyter_text>Integers, floats, Booleans, and tuples are allowed as keys but lists and dictionaries are not. This is because they are not _hashable_ objects and so we get an error reporting this. The fact that lists/dictionaries are not hashable comes down to the fact that their contents can change. Dictionaries are also advantagous over lists when storing _sparse_ data (data with lots of missing or default values).Suppose there is a race with one thousand participants. We know that the first place was awarded to 'Ann' and last place to 'Bob'. Create an 1000 element list (by starting from an empty list and appending) to store this information using the string 'Unknown' for any race position for which we don't know the winner.<jupyter_code>place_names = []
for i in range(1000):
    if i == 0:
        place_names.append('Ann')
    elif i == 999:
        place_names.append('Bob')
    else:
        place_names.append('Unknown')<jupyter_output><empty_output><jupyter_text>Store this information in a two item dictionary and create a function `get_name(i)` to access the name of the person in the ith place. Remember, you can use `in` to check for the existance of a key in a dictionary.<jupyter_code>place_dict = {1: 'Ann', 1000: 'Bob'}

def get_name(i):
    if i in place_dict:
        return place_dict[i]
    else:
        return 'Unknown'
    
print(get_name(1))
print(get_name(2))<jupyter_output>Ann
Unknown<jupyter_text>Consider the amount of storage required for each solution—there should be a clear winner!  Manipulating Dictionaries The following code creates a list of the letters of the English alphabet (called `letters`)<jupyter_code>from string import ascii_uppercase
letters = list(ascii_uppercase)<jupyter_output><empty_output><jupyter_text>Create an empty dictionary called `encode` and loop through the list using enumeration to add key-value pairs to the dictionary mapping each letter to its position in the alphabet (`A -> 1, B -> 2, ..., Z -> 26`)<jupyter_code>encode = {}
for i, l in enumerate(letters):
    encode[l] = i + 1<jupyter_output><empty_output><jupyter_text>Loop through the list of letters (without enumeration this time) to decrease the value of each key by one so that the new mapping is (`A -> 0, ..., Z -> 25`)<jupyter_code>for l in letters:
    encode[l] = encode[l] - 1<jupyter_output><empty_output><jupyter_text>Given an uppercase string, loop through its letters and use this dictionary to encode the text into a numeric list. For example the word 'BAG' would be encoded as `[1, 0, 6]`.<jupyter_code>text = 'SPAM'
encoded = []
for l in text:
    encoded.append(encode[l])
print(encoded)<jupyter_output>[18, 15, 0, 12]<jupyter_text>Create a function that when given a dictionary and a key will delete the item associated with that key only if the key exists<jupyter_code>test_dict = {'A': 1, 'B': 2}

def del_if_exists(dictionary, key):
    if key in dictionary:
        del dictionary[key]
        
del_if_exists(test_dict, 'A')
del_if_exists(test_dict, 'C')
print(test_dict)<jupyter_output>{'B': 2}<jupyter_text>Looping Through Dictionaries As promised, we will look at removing a key by value. We can do this by looping through the items of a dictionary and deleting a corresponding key if the value matches. There is a nuance however: we aren't allowed to use `.keys()`, `.values()` or `.items()` when the size of the dictionary is changing within the loop. The work around this is to convert the dictionary items to a list using `for key, value in list(dictionary.items())`. Use this to remove all instances with the value `Homer` in the following dictionary<jupyter_code>father = {
    'Bart': 'Homer',
    'Homer': 'Abe',
    'Lisa': 'Homer'
}
for key, value in list(father.items()):
    if value == 'Homer':
        del father[key]
print(father)<jupyter_output>{'Homer': 'Abe'}<jupyter_text>How could modify the above code to only delete the first matching instance, as is the case with `.delete()` for lists? We could add a `break` after the `del` statement. Create an empty dictionary called `decode`. Take the letter mapping dictionary from above and loop through its items and using them to fill `decode` with keys and values reversed<jupyter_code>decode = {}
for letter, number in encode.items():
    decode[number] = letter<jupyter_output><empty_output><jupyter_text>Take the encoded string from above and decode it by looping through the list and using this dictionary. You will want to use `''.join(list_of_characters)` to combine the decoded letters back into one string. This is the key step in a large amount of machine learning with text data and cryptography<jupyter_code>decoded = []
for number in encoded:
    decoded.append(decode[number])
decoded = ''.join(decoded)
print(decoded)<jupyter_output>SPAM
{'fraction_non_alphanumeric': 0.09097300337457818, 'fraction_numerical': 0.009420697412823397, 'mean_word_length': 4.654213036565978, 'pattern_counts': {'":': 0, '<': 50, '<?xml version=': 0, '>': 55, 'https://': 0, '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': '14', 'dir': 'jupyter-structured-clean-dedup', 'id': '97445', 'license': 'MIT', 'n_tokens_mistral': 2190, 'n_tokens_neox': 2079, 'n_words': 953, 'path': 'session_seven/session_seven_solutions.ipynb', 'repo_name': 'AstraZeneca-Code-Club/beginners-python', 'size': '7112'}