<jupyter_start><jupyter_text>Dictionaries and Sets* Dictionaries: unordered collection of key-value pairs* Sets: unordered collection of unique values  - analog to mathematical set Objectives:* Create and refer to elements of dictionaries and sets* Interate through keys and values* Add, remove, and update key-value pairs* Compare elements in sets and dictionaries* Apply set operators and methods* Use `in` and `not` to determine membership* Modify set contents* Use compreshensions to create dictionaries and sets* Enhance understanding of mutability and immutablity  DictionaryAn unordered collection of __key-value__ pairs that map immutable keys to values.  * __Keys__ must be unique (and immutable). Create Dictionary* `{}`* Comma separated list of `key:value` pairs enclosed in `{}`<jupyter_code>empty_dict = {}
areacodes = {'207':'Maine','213':'South Central LA','304':'West Virginia', '302':'LA','312':'LA'}
areacodes
# No KeyError, but second key is retained, while first key is removed.
new = {1:3,1:2}
new<jupyter_output><empty_output><jupyter_text>Is a dictionary empty?  You can use `len` to check as well as use conditional statement(s) such as `dict == {}` to return `True` or `False`.<jupyter_code>empty_dict = {}
len(empty_dict)
empty_dict == {}
areacodes == {}<jupyter_output><empty_output><jupyter_text>Accessing ValueUse the key to access associated values.  A `KeyError` occurs when attempting to access a nonexistent key.<jupyter_code>areacodes['207']  # this will give me the value corresponding to the key '207'
Grades = {'A':90,'B':80}
Grades.keys()
Grades.values()
for k,v in Grades.items():
  print(f'Keys {k} with Values {v}')
for i in Grades.values():
  print(i)<jupyter_output>Keys A with Values 90
Keys B with Values 80
90
80<jupyter_text>Iterate over key-value pairs using `.items()` function, which returns tuples that are unpacked.<jupyter_code># Recall how to unpack a tuple
x,y = (1,2)  
print(f'x = {x}, y = {y}')
for key, value in areacodes.items():   # unpack key-value pair
  print(f'Areacode {key} is {value}')  # prints key-value pair<jupyter_output>Areacode 207 is Maine
Areacode 213 is South Central LA
Areacode 304 is West Virginia
Areacode 302 is LA
Areacode 312 is LA<jupyter_text>Adding a new entry to the dictionary (i.e., key:value pair)<jupyter_code># dictionary[key] = value
areacodes['216'] = 'Cleveland'<jupyter_output><empty_output><jupyter_text>Updating a key's value.<jupyter_code>areacodes['216'] = 'Cleveland, Ohio'<jupyter_output><empty_output><jupyter_text>Removing a key-value pair<jupyter_code>del areacodes['216']<jupyter_output><empty_output><jupyter_text>Attempt to access nonexisting key.<jupyter_code>areacodes['216']<jupyter_output><empty_output><jupyter_text>Get methodChecks to see if key exists first.<jupyter_code>areacodes.get('216')<jupyter_output><empty_output><jupyter_text>Include a message if key is nonexistent.<jupyter_code>areacodes.get('216','Record does not exist.  Try another.')<jupyter_output><empty_output><jupyter_text>Test if key exists using `in`<jupyter_code>print('216' in areacodes)
print('304' in areacodes)<jupyter_output><empty_output><jupyter_text>Methods: `keys` & `values`<jupyter_code>f = {1:1, 2:4, 3:9, 4:16}

# Print keys of f
for k in f.keys():
  print(k, end=' ')  # end = 'specify here what happens at end of each print'
                     # options include: '', ' ', ', ', '\t', '\n\
# Alteratively
g = f.keys()
for k in g:
  print(k)
f = {1:1, 2:4, 3:9, 4:16}

# Print values in f
for v in f.values():
  print(v, end=' ')
for x,y in f.items():
  print(f'(x,y) = ({x},{y})')<jupyter_output>(x,y) = (1,1)
(x,y) = (2,4)
(x,y) = (3,9)
(x,y) = (4,16)<jupyter_text>> Note: a VIEW can be made similar to a SQL.  Do __not__ modify a dictionary when iterating a view.<jupyter_code>view = f.keys()
for k in view:
  print(k)
f[5]=25
view<jupyter_output><empty_output><jupyter_text>Convert to Lists<jupyter_code>list(f.keys())
list(f.values())
list(f.items())  # List of tuples<jupyter_output><empty_output><jupyter_text>Any modification of these lists does not change to the corresponding dictionary.  Processing keys in sorted order<jupyter_code>for x in sorted(areacodes.keys()):
  print(x)<jupyter_output>207
213
302
304
312<jupyter_text>Processing values in sorted order<jupyter_code>for x in sorted(areacodes.values()):
  print(x)<jupyter_output>LA
LA
Maine
South Central LA
West Virginia<jupyter_text>ComparisonsThe operators `==` and `!=` are used to compare if two dictionaries have the same key-value pairs (regardless of order).<jupyter_code>f = {1:1, 2:4, 3:9, 4:16}
g = {1:2, 2:4, 3:9, 4:16}
h = {2:4, 1:1, 4:16, 3:9}

f==g
f==h<jupyter_output><empty_output><jupyter_text>Dictionary values can contain multiple objects (e.g., lists).  Moreover, definitions can span multiple lines to __increase readability__.<jupyter_code>grade_book = {
  'Justin':[78,93,81],
  'Hannah':[87,62,94],
  'Billie':[75,81,79]    
}
gradecount = 0
sumscores = 0
for name, grades in grade_book.items():
  print(f'{name}\'s average is {sum(grades)/len(grades):.2f}')
  gradecount += len(grades)
  sumscores += sum(grades)

print(f'Class average = {sumscores/gradecount:.2f}')<jupyter_output>Justin's average is 84.00
Hannah's average is 81.00
Billie's average is 78.33
Class average = 81.11<jupyter_text>Tokenizing Strings: Word Count<jupyter_code>text = 'this is a string of text to count this is another block of text to count here is yet another some more text to count'

text.split()
text = 'this is a string of text to count this is another block of text'\
        ' to count here is yet another some more text to count'\
        'more text is here and is very very good text it is'

words = {}                            # New dictionary
for word in text.split():             # splits all words
  if word in words:                   # already in dictionary
    words[word] += 1                  # words[word] = words[word] + 1  
  else:                               # not in dictionary
    words[word] = 1                   # add word to dictionary/assign value 1  

print(f'{"Word":<10}Counts')          # table header
print('-------------------')          # table divider
for w,c in words.items():             # unpack key:value pairs into w,c
  print(f'{w:<10}{c}')                # print key:value pairs

temps_by_day = {
    'Monday':[75,62,71,83],
    'Tuesday':[60,59,70,67],
    'Wed':[1,2,3,4]
}
airlines_cities={
    'AA':['DFW','PWM'],
    'UA':['DEN','OHR']
}<jupyter_output><empty_output><jupyter_text>SetsSets are unordered collection of unique values. Sets are enclosed by curly brackets, {}<jupyter_code>some_numbers = {2,3,3,4,5,6,1}
Colors = {'Red', 'Green','Blue'}<jupyter_output><empty_output><jupyter_text>Set Membership<jupyter_code>'red' in Colors
# remember Python is case-sensitive
'Blue' in Colors
'red' not in Colors
for color in Colors:
  print(color.upper())
# Reminder to Chapter 2
# on variable names, case-sensitivity, etc.
xyz = 5
Xy_Z = 3
XYZ = 7
_ = 6
the_colors = {'Red'}
theColors = {'Blue'}
print(_)
numbers = [1, 2, 3, 4, 5, 6, 7, 8]
evens = {x for x in numbers if x & 1 ==0 }      # unordered "list"
even_list = [x for x in numbers if x & 1 ==0 ]  # ordered list
print(evens)
print(even_list)
even_list<jupyter_output><empty_output><jupyter_text>Set comparisons<jupyter_code>{1,2,3} == {3,2,1}
A = {1,2,3, 4, 5, 6, 7}
B = {1, 3, 4, 5, 9}
C = {2, 7}
D = set()     # Empty Sets
# Equivalent Sets
A == B
# Not equivalent
A != B<jupyter_output><empty_output><jupyter_text>ContainmentSubsets and supersets<jupyter_code>A = {1,2,3, 4, 5, 6, 7}
B = {1, 3, 4, 5, 9}
C = {2, 7}
D = set()     # Empty Sets

# Subset:  A <= B if every element of A is an element of B.
A <= B
C <= A
A <= A
A < A
A > C
D < C
# Proper subset: A is subset of B, but there is an element of B not in A.
C < A<jupyter_output><empty_output><jupyter_text>Binary Operations on Sets<jupyter_code>A = {1,2,3, 4, 5, 6, 7}
B = {1, 3, 4, 5, 9}
C = {2, 7}
D = set()     # Empty Sets
# Union
A | B
A.union(B)
# Set Intersection
A & B
A.intersection(B)
# Set Difference
A - B
# Symmetric Difference  = (A | B) - (A & B)
A ^ B
# Also Symmetric Difference
(A | B) - (A & B)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1383939467903344, 'fraction_numerical': 0.03514766902611667, 'mean_word_length': 4.5, 'pattern_counts': {'":': 1, '<': 100, '<?xml version=': 0, '>': 93, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '24', 'dir': 'jupyter-structured-clean-dedup', 'id': '333973', 'license': 'MIT', 'n_tokens_mistral': 3196, 'n_tokens_neox': 2958, 'n_words': 912, 'path': 'lectures/Chap6-DictionarySets.ipynb', 'repo_name': 'jamesquinlan/python-DSC225', 'size': '8194'}