5 The most common words, and a report
Build it yourself. Read the tutorial is the other way to read this part.
If you start at this part, make your clone as Get the project says, and then run this command. It puts in your clone the files that this part starts from. A file that the project deleted before this part stays, so delete it if git check lists it:
git rm -rq --cached --ignore-unmatch -- :/ && git restore --source f79588d3b046 --staged --worktree -- :/just setuptally prints a line for every word, in alphabetical order. A reader of a long text wants the most common words first, and only a few of them. A function finds them, and an option of the command prints them. A recipe writes a report of the ten most common words of the corpus.
The code before this part. The code after it.
5.1 A function for the most common words
count gives the counts in no useful order. To find the most common words, tally must sort the counts, most common
first, and keep the first few. A function, top, does it. It sorts by count, highest first, and then by the
word, so that two words with the same count keep one order. Then it keeps the first n.
Apply the patch to src/tally/__init__.py, from the top of the project:
curl -fsSL https://github.com/rahuldave/tally/commit/72a0129dcee765132ad267788636ff4af9066369.diff | git apply --include=src/tally/__init__.pyThe patch puts top after count and before main, with two blank lines around it.
A test says what top must give. It gives top three words with their counts, and asks for the two most common.
Apply the patch to tests/test_top.py, from the top of the project:
curl -fsSL https://github.com/rahuldave/tally/commit/72a0129dcee765132ad267788636ff4af9066369.diff | git apply --include=tests/test_top.pyThe patch makes the file tests/test_top.py, with its one test.
uv run python -m unittest discover -s tests -qThe four tests pass.
Check your files against this move. The command prints nothing when they match, and otherwise shows what differs:
git check 72a0129dcee7If you are stuck, this command puts the files of this move in place of yours. A file that you made yourself stays:
git restore --source f79588d3b046 --staged -- :/ && git restore --source 72a0129dcee7 --staged --worktree -- :/What changed
src/tally/__init__.py: sort by count, most common first, then by the word. 5 lines added. The file.- look at
return sorted(counts.items(), key=lambda pair: (-pair[1], pair[0]))[:n]
- look at
tests/test_top.py: its test, with three words. New, 8 lines. The file.
The patch to src/tally/__init__.py
@@ -11,6 +11,11 @@ def count(text: str) -> dict[str, int]:
return counts
+def top(counts: dict[str, int], n: int) -> list[tuple[str, int]]:
+ "The n most common words, most common first."
+ return sorted(counts.items(), key=lambda pair: (-pair[1], pair[0]))[:n]
+
+
def main() -> None:
text = open(sys.argv[1]).read() if len(sys.argv) > 1 else sys.stdin.read()
for word, n in sorted(count(text).items()):The patch to tests/test_top.py
@@ -0,0 +1,8 @@
+import unittest
+
+from tally import top
+
+
+class Top(unittest.TestCase):
+ def test_most_common_first(self):
+ self.assertEqual(top({"a": 1, "b": 3, "c": 2}, 2), [("b", 3), ("c", 2)])5.2 tally --top N
tally prints every word of a text, and the corpus fills the screen. An option, --top N, makes main print only the
N most common words, with top. Without the option, main prints every count, as before. main reads the
option before the name of the file, so the command is tally --top 3 data/corpus.txt.
Apply the patch to src/tally/__init__.py, from the top of the project:
curl -fsSL https://github.com/rahuldave/tally/commit/c4b92c9bd158cd85d783cf17adc6bedc76c9ba0d.diff | git apply --include=src/tally/__init__.pyThe patch writes main again, in place of the old one.
uv run tally --top 3 data/corpus.txtThe command prints three lines, the most common word first. They are 127 line, 126 class and 125 word.
Check your files against this move. The command prints nothing when they match, and otherwise shows what differs:
git check c4b92c9bd158If you are stuck, this command puts the files of this move in place of yours. A file that you made yourself stays:
git restore --source 72a0129dcee7 --staged -- :/ && git restore --source c4b92c9bd158 --staged --worktree -- :/What changed
src/tally/__init__.py:mainreads the option before the name of the file. 8 lines added, 3 removed. The file.- look at
if args[:1] == ["--top"]:
- look at
The patch to src/tally/__init__.py
@@ -17,6 +17,11 @@ def top(counts: dict[str, int], n: int) -> list[tuple[str, int]]:
def main() -> None:
- text = open(sys.argv[1]).read() if len(sys.argv) > 1 else sys.stdin.read()
- for word, n in sorted(count(text).items()):
- print(n, word)
+ "Count the words of a file, or of the standard input. With --top N, only the N most common."
+ args, n = sys.argv[1:], None
+ if args[:1] == ["--top"]:
+ n, args = int(args[1]), args[2:]
+ text = open(args[0]).read() if args else sys.stdin.read()
+ counts = count(text)
+ for word, times in top(counts, n) if n else sorted(counts.items()):
+ print(times, word)5.3 A report, which just setup makes
The most common words of the corpus make a report, which others want to read without running tally. A recipe of
the justfile, report, writes the ten most common words to build/top.txt. The recipe has two lines, and just
prints each line before it runs it. The second line has two parts.
mkdir -p buildmakes the folderbuild, and does nothing when the folder is there.uv run tally --top 10 data/corpus.txtcounts the corpus, and keeps the ten most common words.> build/top.txtsends what tally prints to the file, in place of the terminal.
Apply the patch to justfile, from the top of the project:
curl -fsSL https://github.com/rahuldave/tally/commit/9e37b6eed29516f62bc176d1eaa66bf4e86683a6.diff | git apply --include=justfileThe patch adds the recipe report at the end of the justfile.
The report is an artifact, as the corpus is. git ignores the folder build/, so git holds the recipe, and not the
report. just setup makes the report when the file is missing. test -f build/top.txt is true when the file is
there, and || runs just report only when it is not.
The same patch adds a line to the recipe setup, after the line that makes the corpus.
A report from an earlier run stays, because git ignores it. So remove the report first, and then just setup makes
it again:
rm -f build/top.txt
just setup
cat build/top.txtThe file has ten lines, the most common word first.
Check your files against this move. The command prints nothing when they match, and otherwise shows what differs:
git check 9e37b6eed295If you are stuck, this command puts the files of this move in place of yours. A file that you made yourself stays:
git restore --source c4b92c9bd158 --staged -- :/ && git restore --source 9e37b6eed295 --staged --worktree -- :/What changed
justfile: the report, and the line insetupthat makes it when it is missing. 6 lines added. The file.- look at
uv run tally --top 10 data/corpus.txt > build/top.txt - look at
test -f build/top.txt || just report
- look at
The patch to justfile
@@ -6,6 +6,7 @@ default:
setup:
uv sync
uv run python scripts/corpus.py data/corpus.txt
+ test -f build/top.txt || just report
# Count the words of a file
run file:
@@ -14,3 +15,8 @@ run file:
# Run the tests
test:
uv run python -m unittest discover -s tests -q
+
+# Write the ten most common words of the corpus to build/top.txt
+report:
+ mkdir -p build
+ uv run tally --top 10 data/corpus.txt > build/top.txt