from pathlib import Path
DATA_RAW = Path("../data/raw")
text = (DATA_RAW / "the_star.txt").read_text(encoding="utf-8")
words = text.lower().split()
words[0:6]['twinkle,', 'twinkle,', 'little', 'star,', 'how', 'i']
Last week words.count("twinkle") said zero, even though “twinkle” is in the rhyme four times. Every “twinkle” in our list still had a comma stuck to it. To fix that, we need to do something to every word in the list, which is what a loop is for. In this tutorial you will write loops, choose what code runs with if and else, count words with a dictionary, and package your work into a function you can reuse. In the next tutorial you will use all of them to build the counting model from class in Python.
Skim the sections on loops and if statements, but type the counting loop and the clean function as they are written here, because the next tutorial builds on both. Do the Your turn questions, and then the ones under Going further.
This week you will get your lab from GitHub. Follow Getting and handing in work with GitHub to join the course’s organization, accept this week’s assignment from its link on the schedule, clone the repository into your CSCI295 folder, open it in VS Code, and run uv sync. The repository has the rhyme in data/raw/the_star.txt and a much longer text, alice.txt, that we will use in the next tutorial.
Make a notebook called repeating_and_deciding.ipynb in the notebooks folder, and choose the .venv kernel. Start the notebook with a Markdown cell with a title, and then read in the rhyme the way you did last week.
from pathlib import Path
DATA_RAW = Path("../data/raw")
text = (DATA_RAW / "the_star.txt").read_text(encoding="utf-8")
words = text.lower().split()
words[0:6]['twinkle,', 'twinkle,', 'little', 'star,', 'how', 'i']
Save the notebook, and make your first commit and push it, as described in the GitHub guide’s Committing and pushing. Check on GitHub that it arrived, and then carry on here.
A for loop runs the same code once for each item in a list. Here is a loop that prints the first six words, one per line.
Read it as “for each word in the first six words, print the word.” Python is strict about three details here:
:.IndentationError.for (here word) is a variable. Each time through the loop, Python sets it to the next item in the list, so the body runs once with word equal to 'twinkle,', then again with 'twinkle,', then with 'little', and so on.The loop ends where the indenting ends. Compare these two cells, and predict how many lines each one prints before you run them.
In the second cell, the last line is not indented, so it is not part of the loop. It runs once, after the loop is done.
The string method strip removes characters from the beginning and end of a string. We tell it which characters to remove, here the punctuation marks that appear in the rhyme.
To clean every word, we start with an empty list and use a loop to add each cleaned word to it with the list method append.
['twinkle', 'twinkle', 'little', 'star', 'how', 'i']
Now let’s count again.
The count is now four. Notice that we did not change the original list. words still has the punctuation, because we made a new list, clean_words. We kept the original for the same reason we never edit the files in data/raw.
Often we want the code to do different things in different situations. An if statement runs its indented code only when a condition is True. Conditions are usually comparisons like the ones you met last week, such as 2 + 2 == 4, and they are either True or False.
twinkle <-- found one
twinkle <-- found one
little
star
how
i
wonder
what
The else part runs when the condition is False, and you can leave it out. Like for, both if and else end with a colon and have their own indented code. Here the if statement is inside the loop, so its code is indented twice.
Here are the comparisons you will use most often:
| Code | Means |
|---|---|
a == b |
a equals b |
a != b |
a does not equal b |
a < b, a > b |
less than, greater than |
a <= b, a >= b |
less than or equal to, greater than or equal to |
a in b |
a is an item in the list b, a piece of the string b, or a key of the dictionary b |
For example, which words in the rhyme are longer than five letters?
twinkle
twinkle
little
wonder
diamond
blazing
nothing
shines
little
twinkle
twinkle
And we can ask whether a word appears in the rhyme at all:
The count method answers the question for one word at a time. To count every word, we need somewhere to keep a running total for each one, so we will use a dictionary. It stores keys and a value for each key, like a word and its count. Dictionaries are written with curly braces, with a colon between each key and its value:
Square brackets look up the value for a key, and you can use them to add a key or change its value:
Now we can count every word in the rhyme, starting with an empty dictionary. For each word, if it is already a key, we add one to its count. If not, it is the first time we have seen the word, so its count starts at one.
counts = {}
for word in clean_words:
if word in counts:
counts[word] = counts[word] + 1
else:
counts[word] = 1
counts{'twinkle': 4,
'little': 2,
'star': 1,
'how': 1,
'i': 1,
'wonder': 1,
'what': 1,
'you': 2,
'are': 1,
'up': 1,
'above': 1,
'the': 4,
'world': 1,
'so': 1,
'high': 1,
'like': 1,
'a': 1,
'diamond': 1,
'in': 1,
'sky': 1,
'when': 2,
'blazing': 1,
'sun': 1,
'is': 1,
'gone': 1,
'he': 1,
'nothing': 1,
'shines': 1,
'upon': 1,
'then': 1,
'show': 1,
'your': 1,
'light': 1,
'all': 1,
'night': 1}
Take a minute to trace through the loop for the first few words by hand. What does counts look like after the first word? After the second? After the third? (A dictionary keeps its keys in the order they were first added, which is why the words come out in the order they first appear in the rhyme.)
The dictionary has one key for each different word:
So the rhyme has 44 words but only 35 different ones. And now we can look up any word:
What happens if you look up a word that is not a key, like counts["moon"]? Try it, and read the error message. (Then turn that line into a comment, so it does not stop Run All.)
We have cleaned the words of one text, and in the next tutorial we will want to do the same to a much longer one. Rather than copy the code, we can turn it into our own function. You have been using functions all along (len, print, and type), and now you will write one.
Nothing happens when you run this cell, because it only defines the function and does not run it. Here is what each part does:
def clean(text): says we are defining a function named clean that takes one input, which will be called text inside the function.help(clean) shows.return words sends the finished list back to whoever called the function.Now we can call the function on the rhyme, or on any text at all:
['curiouser', 'and', 'curiouser', 'cried', 'alice']
The variables inside a function belong to the function. The words inside clean is a separate variable from the words list we made at the top of the notebook, which has not changed:
Work below the tutorial code, with a Markdown cell before each question.
help(str.startswith).) Then make your code count them, repeats included, instead of counting by hand.best_word = "" and best_count = 0. Inside the loop, when a word’s count is larger than best_count, update both. (Looping over a dictionary gives you its keys, as in for word in counts:.) Is there a tie? What does your loop do when there is one?clean_words = [] were inside the loop (indented) instead of before it?These are optional.
count_words that takes a list of words and returns a dictionary of counts. Check that count_words(clean_words) == counts is True.counts[word] = counts.get(word, 0) + 1. Look up the dictionary method get and explain why this line does the same job as our if and else.in a list or in a dictionary? Why might it be?When you have finished, click Restart and then Run All. Check that everything runs and that the results still match the tutorial and your notes, and save. Then commit and push your work as described in the GitHub guide, so that you have a backup. You will keep working on this same lab in the next tutorial, and both of this week’s notebooks are handed in together by the week 2 deadline on the schedule.