SECTION 1
Historical Context & Motivation
Counting things is one of the oldest computational tasks humans have ever performed. Long before computers existed, merchants scratched tally marks into clay tablets to track inventory, and census workers recorded population counts on paper ledgers. The core idea — pairing a category with a count — is exactly what a Python dictionary does when you use it for frequency counting.
In the history of programming, the concept of mapping keys to values evolved from simple lookup tables into one of the most versatile data structures in modern languages. Python's dictionary (the dict type) traces its roots through decades of computer science research on hash tables and associative arrays. Understanding this timeline helps you appreciate why dictionaries are so central to Python programming today.
1953 • Hash Tables Invented
1969 • Associative Arrays in AWK
1991 • Python 0.9 Includes Dictionaries
2010s • Big Data & Frequency Analysis
So here is the question this lesson answers: given a collection of items — letters in a word, scores in a gradebook, colors of cars in a parking lot — how do you efficiently count how many times each unique item appears, and then display those counts visually? The answer lies in Python's dictionary data structure.
SECTION 2
Core Principles & Definitions
Before you start writing code, you need a solid mental model of what a dictionary is and how frequency counting works. A dictionary in Python is a collection of key-value pairs, where each key is unique and maps to exactly one value. When you use a dictionary for frequency counting, the keys represent the items you are counting, and the values represent how many times each item has appeared.
Key-Value Pair
{"a": 3}.Frequency Counting
The Counting Pattern
Histogram
KEY TAKEAWAY
SECTION 3
Visual Explanation — How Frequency Counting Builds a Dictionary
The diagram below traces how a Python dictionary evolves as the program iterates through the string "banana" one character at a time. Each row represents one step of the loop. Notice how the dictionary starts empty and grows each time a new letter is encountered, while existing keys simply get their counts incremented.
"banana". New keys are created at steps 0, 1, and 2. Steps 3–5 increment existing keys. The bottom shows the resulting text-based histogram.Notice how the dictionary never contains duplicate keys. Each letter appears exactly once as a key, and its value represents the total number of occurrences. This is the fundamental property that makes dictionaries ideal for frequency counting: the uniqueness constraint on keys naturally groups identical items together, while the values accumulate the counts.
SECTION 4
How It Works — The Counting Pattern in Code
Python gives you several ways to implement the frequency counting pattern. Each approach has the same result, but they differ in how they handle the "what if the key doesn't exist yet?" problem. Let's look at the three main techniques, starting from the most explicit and moving to the most concise.
Technique 1: The if-else Check
The most straightforward approach uses an if statement to check whether the key already exists in the dictionary. If it does, you increment its value by 1. If it does not, you create a new entry with a value of 1. Here is the pattern:
IF-ELSE PATTERN
freq = the dictionary accumulating counts. data = any iterable (string, list, etc.). item = each element from data. The in keyword checks for key existence in O(1) average time.
Technique 2: The .get() Method
Python dictionaries have a built-in method called .get() that returns a default value if the key is missing, rather than raising an error. This eliminates the need for an if-else block and makes the code more compact.
GET METHOD PATTERN
freq.get(item, 0) returns the current count if item exists, or 0 if it doesn't. Adding 1 to the result handles both cases in a single line.
Technique 3: Building the Histogram
Once you have a frequency dictionary, you can visualize it as a text-based histogram by looping through the dictionary and printing a repeated character for each count. The string multiplication operator "*" * n repeats the asterisk character n times, producing a horizontal bar proportional to the frequency.
HISTOGRAM OUTPUT PATTERN
For each key in the dictionary, this prints the key followed by a row of asterisks. If freq["a"] is 3, it prints a ***. The visual length of each bar directly corresponds to the count.
KEY TAKEAWAY
.get() method is the Pythonic go-to for frequency counting. It is like asking a librarian, "How many copies of this book do you have?" If the book exists, you get the count; if not, the librarian politely says "zero" instead of giving you an error.SECTION 5
Common Frequency Counting Patterns
Frequency counting appears in many different contexts. The diagram below shows four common patterns you will encounter in Python programming. Each pattern takes a different type of input but follows the same core logic: iterate, check, and count.
.get() method handles all data types uniformly.| Pattern | Input Type | Key Type | Split Needed? |
|---|---|---|---|
| Count characters | str | str (single char) | No — iterating a string yields characters |
| Count list items | list | Same as element type | No — iterating a list yields elements |
| Count words | str | str (word) | Yes — use .split() |
| Count numbers | list | int or float | No — iterate directly |
Summary of frequency counting patterns by data type
SECTION 6
Worked Example — Counting and Displaying Letter Frequencies
Let's walk through a complete program that counts the frequency of each letter in a user-provided sentence and displays the results as a text-based histogram. We will use the .get() method and handle uppercase/lowercase by converting everything to lowercase first.
Letter Frequency Histogram
- 1
Step 1 — Define the Input and Initialize
We start with a sentence and create an empty dictionary to hold our frequency counts. We also convert the sentence to lowercase so that "A" and "a" are treated as the same letter.sentence = "Mississippi is a state"sentence = sentence.lower()freq = {}sentence is now"mississippi is a state"and freq is{} - 2
Step 2 — Loop Through Each Character
We iterate through every character in the sentence. We only want to count letters, so we skip spaces using.isalpha(), which returnsTrueonly for alphabetic characters.for ch in sentence:if ch.isalpha():freq[ch] = freq.get(ch, 0) + 1After the loop:{"m": 1, "i": 4, "s": 5, "p": 2, "a": 2, "t": 2, "e": 1} - 3
Step 3 — Sort the Dictionary by Count (Descending)
To make the histogram more readable, we sort the keys by their frequency in descending order. Thesorted()function withkey=freq.getsorts by value, andreverse=Trueputs the highest count first.sorted_keys = sorted(freq, key=freq.get, reverse=True)sorted_keys =["s", "i", "p", "a", "t", "m", "e"] - 4
Step 4 — Print the Histogram
Finally, we loop through the sorted keys and print each letter followed by a bar of asterisks. We use Python's string multiplication to create bars of the right length.for key in sorted_keys:print(key, "*" * freq[key])Output:s ***** i **** p ** a ** t ** m * e *
Pro Tip
print(f"{key:>3} | {'*' * freq[key]}"). The :>3 right-aligns the key in a 3-character-wide space, keeping your bars neatly lined up.SECTION 7
Dictionaries vs. Other Counting Approaches
You might wonder: why use a dictionary at all? Couldn't you just use a list, or count items one at a time with the .count() method? The table below compares three different approaches to frequency counting so you can see why dictionaries are usually the best choice.
| Approach | Strengths | Limitations |
|---|---|---|
| Dictionary with .get() | Counts all items in a single pass through the data. Efficient — O(n) time. Clean, Pythonic code. Works with any hashable type. | Requires understanding of dictionaries and the .get() method. Unordered in older Python versions (before 3.7). |
| list.count() in a loop | Simple to understand. No dictionary knowledge needed. | Very slow — O(n²) time because .count() scans the entire list for every unique item. Also counts duplicates multiple times unless you track what you've already counted. |
| Parallel lists (keys + values) | Does not require dictionary syntax. Two separate lists for items and counts. | Tedious to manage. Must manually keep two lists in sync. Searching for a key requires scanning the items list — O(n) per lookup. |
Comparison of frequency counting approaches in Python
KEY TAKEAWAY
SECTION 8
Connection to Advanced Tools
The manual frequency counting pattern you just learned is the foundation for more powerful tools built into Python's standard library and popular third-party packages. As you advance, you will encounter these tools that automate and extend what you can do by hand.
| What You Know Now | Advanced Version | What It Adds |
|---|---|---|
freq.get(item, 0) + 1 | collections.Counter(data) | Counts all items in one line. Includes .most_common(n) to get the top n items, arithmetic between Counters, and more. |
Text histogram with "*" * count | matplotlib.pyplot.bar() | Creates professional graphical bar charts, pie charts, and other visualizations with colors, labels, and axes. |
| Manual if/else or .get() | collections.defaultdict(int) | A dictionary subclass that auto-creates missing keys with a default value (0 for int), eliminating the need for .get() entirely. |
Sorting with sorted() | pandas.Series.value_counts() | Frequency counting integrated into the pandas data analysis library. Returns a sorted series of counts from a DataFrame column. |
From manual counting to professional-grade tools
Even though these advanced tools exist, learning to write frequency counting logic manually is essential. It builds your understanding of iteration, conditionals, and dictionary operations — three pillars of Python programming. When you eventually use Counter or pandas, you will understand exactly what those tools are doing under the hood, which makes debugging far easier.
SECTION 9
Practice Problems
Question 1: CONCEPTUAL
Show answer
Question 2: BASIC CALCULATION
freq:
data = ["red", "blue", "red", "green", "blue", "red"]
freq = {}
for color in data:
freq[color] = freq.get(color, 0) + 1Show answer
{"red": 1}, "blue" → {"red": 1, "blue": 1}, "red" → {"red": 2, "blue": 1}, "green" → {"red": 2, "blue": 1, "green": 1}, "blue" → {"red": 2, "blue": 2, "green": 1}, "red" → {"red": 3, "blue": 2, "green": 1}. The final dictionary is {"red": 3, "blue": 2, "green": 1}.Question 3: INTERMEDIATE
Show answer
sentence = input("Enter a sentence: ").lower()
words = sentence.split()
freq = {}
for word in words:
freq[word] = freq.get(word, 0) + 1
for key in freq:
print(key, '*' * freq[key])
The key steps are: (1) convert to lowercase with .lower(), (2) split into words with .split(), (3) count using the dictionary .get() pattern, and (4) display with string multiplication. For the input "the cat sat on the mat," the output would show "the" with two asterisks and all other words with one.Question 4: APPLIED
scores = [85, 92, 78, 85, 90, 92, 85, 78, 90, 92, 85, 100]. Write a Python program that (a) counts the frequency of each score, (b) finds which score appeared most often, and (c) prints a histogram sorted from highest score to lowest. Your output should show the score, the count, and a bar of asterisks.Show answer
scores = [85, 92, 78, 85, 90, 92, 85, 78, 90, 92, 85, 100]
freq = {}
for s in scores:
freq[s] = freq.get(s, 0) + 1
most_common = max(freq, key=freq.get)
print(f"Most frequent score: {most_common} ({freq[most_common]} times)")
for key in sorted(freq, reverse=True):
print(f"{key:>4} ({freq[key]}) {'*' * freq[key]}")
The frequency dictionary is {85: 4, 92: 3, 78: 2, 90: 2, 100: 1}. The max(freq, key=freq.get) function finds the key with the highest value, which is 85 (appearing 4 times). The sorted output by score descending shows 100, 92, 90, 85, 78 with their respective bars.Question 5: CRITICAL THINKING
list.count() instead of a dictionary because "it's simpler." If the novel has 80,000 words, estimate how many comparison operations each approach requires and explain which is more practical. Then describe one real-world application where word frequency analysis provides meaningful insight.Show answer
.get(), you make a single pass through all 80,000 words. Each dictionary lookup is O(1) on average, so the total work is approximately 80,000 operations. With list.count(), you would need to call .count() for each of the 8,000 unique words, and each call scans all 80,000 words. That is 8,000 × 80,000 = 640,000,000 operations — roughly 8,000 times slower. For a full novel, the dictionary approach finishes in milliseconds while the list approach could take noticeable seconds or even minutes. A real-world application is spam detection: email filters analyze word frequencies to identify patterns. If words like "free," "winner," and "click" appear far more frequently than in normal emails, the filter can flag the message as spam. Word frequency analysis is also central to search engines, plagiarism detection, and natural language processing.SUMMARY
Lesson Summary
Summary
A Python dictionary stores key-value pairs where each key is unique. For frequency counting, the keys represent items being counted and the values store how many times each item appears. The .get() method provides the cleanest implementation: freq[item] = freq.get(item, 0) + 1 handles both new and existing keys in a single line. This pattern works with strings, lists, and any iterable collection. Once you have a frequency dictionary, you can build a text-based histogram by printing repeated characters using "*" * count.
The dictionary approach runs in O(n) time — one pass through the data — making it far more efficient than alternatives like list.count(). You can sort results using sorted() with key=freq.get to display items by frequency. This manual counting skill is the foundation for professional tools like collections.Counter and pandas.value_counts() that you will use in more advanced courses.