<jupyter_start><jupyter_text>Problem 1Using the following dictionary:```my_dict = {    'a': 3,     'b': 2,     'c': 10,     'd': 7,     'e': 9,     'f' : 12,     'g' : 13    }```Print out:    - the keys of all values that are even.    - the key with the maximum value.    - the sum of all the values.<jupyter_code>my_dict = {
    'a': 3, 
    'b': 2, 
    'c': 10, 
    'd': 7, 
    'e': 9, 
    'f' : 12, 
    'g' : 13
    }
# print the keys with even values
print('keys with even values:')
for key, value in my_dict.items():
    # modulo 2 == 0 implies the number is even
    if value % 2 == 0:
        print(key)

# print the key corresponding to the largest value
print('\nkey with max value:')
max_val = 0    # assume the values are positive numbers!
for key, value in my_dict.items():
    if value > max_val:
        max_val = value
        max_key = key
print(max_key)

# print the sum of all of the values
print('\nsum of values:')
val_sum = 0
for value in my_dict.values():
    val_sum += value
print(val_sum)

# or, using Python built-ins
print('\nsum of values (again):')
print(sum(my_dict.values()))<jupyter_output>keys with even values:
c
f
b

key with max value:
g

sum of values:
56

sum of values (again):
56<jupyter_text>Problem 2Write a function that finds the number of elements in a list (without using the built-in `len` function). Now, use `%%timeit` to compare the speed to `len` for a list of 100,000 elements.<jupyter_code>def my_len(my_list):
    n = 0
    for element in my_list:
        n += 1
    
    return n
my_list = list(range(100000))
%%timeit
my_len(my_list)
%%timeit
len(my_list)<jupyter_output>10000000 loops, best of 3: 78.8 ns per loop<jupyter_text>It's not a coincidence that the custom function is roughly 100,000x slower than the built-in. Python lists actually have an attribute that stores their length - so using the `len` built-in means just looking up that number (so basically one step). Iterating over the entire list takes 100,000 steps (one for each element in the list). Try changing the length of the list to 1,000 elements - the custom length function now should be approximately 1,000x slower than the built-in.  Problem 3Write a function that calculates the median of a list of numbers (without using statistics). Use the `randint` function the the random module to create a list of integers to test your function.<jupyter_code>def median(x):
    # sort the input list first
    x.sort()
    mid = int(len(x) / 2)
    if len(x) % 2 == 0:
        # for even length, median is average of middle 2 vals
        return (x[mid] + x[mid-1]) / 2
    else:
        # for odd length, median is just the middle value
        return x[mid]
my_list = [1, 2, 3, 4, 5]
median(my_list)
import random
my_random_list = []
# make a list of 1000 random integers between 0 and 99,999
for _ in range(1000):
    my_random_list.append(random.randint(0, 100000))
median(my_random_list)
# double check with median function from `statistics` module
import statistics
statistics.median(my_random_list)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10062071218556028, 'fraction_numerical': 0.03626265926167919, 'mean_word_length': 3.3556187766714083, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 12, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 1, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '296411', 'license': 'MIT', 'n_tokens_mistral': 1093, 'n_tokens_neox': 977, 'n_words': 412, 'path': 'week_1/your_turn_solutions.ipynb', 'repo_name': 'nickdavidhaynes/python-data-science-intro', 'size': '3061'}