Entropy — how many questions is the answer worth?
Sixteen animals take four questions, and the first lesson never said where the four came from. It came from entropy, and this lesson proves it by building the best strategy there is and counting.
Twenty questions used a number it never explained. Sixteen animals, and four questions is the best anyone could do. So a tree averaging 4.1 was close to perfect.
Where did the four come from?
It came from entropy. Entropy is the second way to measure how mixed a pile is, and it answers a very concrete question.
Entropy is how many yes/no questions an answer is worth
Suppose I know an outcome and you do not. How many yes-or-no questions would you need to get it out of me?
| Possible answers, all equally likely | Questions needed | Entropy |
|---|---|---|
| 2 | 1 | 1 bit |
| 4 | 2 | 2 bits |
| 16 | 4 | 4 bits |
2
- Questions needed
- 1
- Entropy
- 1 bit
4
- Questions needed
- 2
- Entropy
- 2 bits
16
- Questions needed
- 4
- Entropy
- 4 bits
Sixteen equally likely answers cost four questions. That is the number Twenty questions used and never explained.
log₂ of a share is how many halvings it takes to reach that share
How mixed is this pile? scored ten deals with gini and got 0.48, which is a probability. Entropy asks a different question about the same pile and answers in questions.
An animal that comes up one time in sixteen is four halvings away, so naming it costs four questions. The formula averages that cost over every outcome, weighted by how often each one comes up.
The ten deals are worth 0.971 questions
Under one question, because the pile already leans one way. Knowing a deal was won is the less surprising answer, so it costs less to tell you.
- won: 0.6 × log₂(1 / 0.6) = 0.442
- lost: 0.4 × log₂(1 / 0.4) = 0.529
- together: 0.971 bits
A pile that is split down the middle costs a whole question. A pile that agrees costs none.
| The pile | Entropy |
|---|---|
| 5 won, 5 lost | 1 — a whole question, because you know nothing |
| 6 won, 4 lost | 0.971 |
| 10 won, 0 lost | 0 — no question needed, because you already know |
5 won, 5 lost
- Entropy
- 1 — a whole question, because you know nothing
6 won, 4 lost
- Entropy
- 0.971
10 won, 0 lost
- Entropy
- 0 — no question needed, because you already know
Entropy falls as a pile agrees with itself, exactly as gini does. The two measures move together. Only the unit is different.
Sixteen animals are worth exactly 4 questions
Entropy claims to be a floor. It says no questioning strategy can beat it on average. That is a strong claim, so the notebook builds the best possible strategy and counts.
| Sixteen equally likely animals | Questions |
|---|---|
| what entropy says is possible | 4 |
| the best strategy there is, built and measured | 4 |
| lesson 1's tree, measured | 4.1 |
what entropy says is possible
- Questions
- 4
the best strategy there is, built and measured
- Questions
- 4
lesson 1's tree, measured
- Questions
- 4.1
Entropy predicted the floor before any tree existed, and the best strategy hit it exactly. Lesson 1's tree paid 4.1, a tenth of a question above the minimum.
If one animal is picked half the time, entropy falls to 2.95
All of that assumed the sixteen animals were equally likely. Suppose half the people who play think of the dolphin.
Then “is it the dolphin?” becomes the best first question after all. Lesson 1 dismissed that opening, and it was only a bad opening while every animal was equally likely.
Entropy is the number that knows this. It falls from 4 to 2.953, and the best strategy falls with it. Nothing about the animals changed. Only how often each one comes up.
When 99% of rows share one outcome, there is very little for a model to earn
A pile that almost agrees with itself has a very low entropy. There is very little to tell anybody. So a model that is right 99% of the time by always saying the same thing has learned nothing.
Keep that in mind. Perfect on what it has seen meets it in a real file, where 86.5% of the people share one outcome.
Gini and entropy pick the same best split in all 33 splits of the ten deals
Gini is a probability. Entropy is a count of questions. Across all 33 possible splits of those ten deals, they pick the same best split and the same top three.
This path uses gini, because it has no logarithms
Gini is the measure from here on, and not because it is better. It uses squares instead of logarithms, so the arithmetic in every later lesson stays checkable on paper. On real splits the two measures agree anyway.
You now have both numbers and know what each one means. The next lesson spends them on twenty-four films and five questions.
content/notebooks/decision-trees/how-many-questions-is-it-worth.ipynb
Give one animal 0.9 of the probability and watch entropy fall below a single question. Worth trying: attempt a strategy for the equally likely sixteen that averages under four questions. You cannot, and that is the only thing entropy claims.
Show the code5 cells
from math import log2
DEALS = ["won"] * 6 + ["lost"] * 4
def entropy(pile):
"""The average number of yes/no questions the answer is worth, in bits."""
if not pile:
return 0.0
shares = [pile.count(k) / len(pile) for k in set(pile)]
return -sum(s * log2(s) for s in shares if s > 0)
by_hand = -0.6 * log2(0.6) - 0.4 * log2(0.4)
print(f" 0.6 x log2(1/0.6) = {-0.6 * log2(0.6):.3f}")
print(f" 0.4 x log2(1/0.4) = {-0.4 * log2(0.4):.3f}")
print(f" -----")
print(f" {by_hand:.3f} bits")
record("deals_entropy", round(by_hand, 3))
record("even_entropy", round(entropy(["won", "lost"]), 3))
# abs(): -0.0 is a true statement and an ugly one to print in a lesson.
record("pure_entropy", abs(round(entropy(["won"] * 10), 3)))
print(f"\nfive and five {entropy(['won'] * 5 + ['lost'] * 5):.3f} (a whole question — you know nothing)")
print(f"all won {entropy(['won'] * 10):.3f} (no question needed — you already know)")uniform = [f"animal {i}" for i in range(16)]
print(f"entropy of 16 equally likely animals = {entropy(uniform):.3f} bits")
print(f"log2(16) = {log2(16):.3f}")
record("animals_entropy", round(entropy(uniform), 3))
record("animals_measured", 4.1) # what the greedy tree in lesson 1 actually averagedimport heapq
def best_tree_cost(weights):
"""Average questions under the shortest possible strategy (Huffman)."""
if len(weights) == 1:
return 0.0
heap = [(w, i, 0) for i, w in enumerate(weights)] # (weight, tiebreak, depth-so-far)
heapq.heapify(heap)
total = 0.0
nxt = len(weights)
while len(heap) > 1:
a = heapq.heappop(heap)
bq = heapq.heappop(heap)
merged = a[0] + bq[0]
total += merged # every merge adds one question to everything below it
heapq.heappush(heap, (merged, nxt, 0))
nxt += 1
return total
equal = [1 / 16] * 16
print(f"entropy of the sixteen {entropy(uniform):.3f} questions")
print(f"best possible strategy {best_tree_cost(equal):.3f} questions")
record("animals_best_strategy", round(best_tree_cost(equal), 3))def entropy_of(weights):
return -sum(w * log2(w) for w in weights if w > 0)
skewed = [0.5] + [0.5 / 15] * 15 # one animal picked half the time
print(f"{'':22} {'entropy':>9} {'best strategy':>15}")
print(f"{'all equally likely':22} {entropy_of(equal):>9.3f} {best_tree_cost(equal):>15.3f}")
print(f"{'one picked half the time':22} {entropy_of(skewed):>9.3f} {best_tree_cost(skewed):>15.3f}")
record("skewed_entropy", round(entropy_of(skewed), 3))
record("skewed_best", round(best_tree_cost(skewed), 3))
record("questions_saved", round(best_tree_cost(equal) - best_tree_cost(skewed), 1))
def plot(ax):
labels = ["all sixteen\nequally likely", "one animal picked\nhalf the time"]
ent = [entropy_of(equal), entropy_of(skewed)]
best = [best_tree_cost(equal), best_tree_cost(skewed)]
xs = range(len(labels))
ax.bar([x - 0.19 for x in xs], ent, width=0.36, color="#e2574c", label="entropy — the floor")
ax.bar([x + 0.19 for x in xs], best, width=0.36, color="#9aa0aa", label="best strategy — measured")
for x, (e, bb) in enumerate(zip(ent, best)):
ax.text(x - 0.19, e + 0.07, f"{e:.2f}", ha="center", fontsize=9.5)
ax.text(x + 0.19, bb + 0.07, f"{bb:.2f}", ha="center", fontsize=9.5)
ax.set_xticks(list(xs))
ax.set_xticklabels(labels)
ax.set_ylabel("Questions to name the animal")
ax.set_ylim(0, 5.0)
ax.legend(frameon=False, fontsize=9)
ax.grid(axis="x", visible=False)
save_fig("entropy-is-the-floor", plot, figsize=(7, 3.9))def gini(pile):
if not pile:
return 0.0
shares = [pile.count(k) / len(pile) for k in set(pile)]
return 1 - sum(s * s for s in shares)
# Every way of splitting ten deals into two sides, scored both ways: does the
# ranking ever change? This is the honest version of "they mostly agree".
from itertools import combinations
rows = []
for size in range(1, 10):
for left_won in range(0, min(size, 6) + 1):
left_lost = size - left_won
if left_lost > 4:
continue
left = ["won"] * left_won + ["lost"] * left_lost
right = ["won"] * (6 - left_won) + ["lost"] * (4 - left_lost)
w = len(left) / 10
rows.append((
gini(DEALS) - (w * gini(left) + (1 - w) * gini(right)),
entropy(DEALS) - (w * entropy(left) + (1 - w) * entropy(right)),
))
by_gini = sorted(range(len(rows)), key=lambda i: -rows[i][0])
by_entropy = sorted(range(len(rows)), key=lambda i: -rows[i][1])
record("n_splits_compared", len(rows))
record("same_best_split", by_gini[0] == by_entropy[0])
record("same_top_three", by_gini[:3] == by_entropy[:3])
print(f"{len(rows)} possible splits of the ten deals")
print(f"same best split {by_gini[0] == by_entropy[0]}")
print(f"same top three {by_gini[:3] == by_entropy[:3]}")
print(f"identical ranking {by_gini == by_entropy}")
def plot2(ax):
ps = [i / 200 for i in range(201)]
ax.plot(ps, [1 - p * p - (1 - p) ** 2 for p in ps], color="#e2574c", label="gini — chance two draws disagree")
ax.plot(ps, [0 if p in (0, 1) else -p * log2(p) - (1 - p) * log2(1 - p) for p in ps],
color="#3b6fd4", ls="--", label="entropy — questions the answer is worth")
ax.set_xlabel("Share of the pile that were won")
ax.set_ylabel("Impurity")
ax.legend(frameon=False, fontsize=9)
save_fig("gini-and-entropy", plot2, figsize=(7, 3.6))Related lessons
Base rates — what a piece of evidence is actually worth
A face-recognition system that is 99.9% accurate and almost entirely wrong, and a number that sent an innocent woman to prison. Both are the same arithmetic, and it is the arithmetic that decides what any piece of evidence is worth.
ReadConfirmation and survivorship — what you never looked for
Two questions about evidence you did not go looking for. One is a rule you have to discover, and one is a pattern in five famous people — and in both, the thing that would have told you the truth is the thing nobody checks.
ReadLoss aversion, sunk cost and regression — what it costs you
Four questions you answer about yourself rather than about a scenario, and your own answers are the finding. Then the pattern that makes praise look useless and criticism look like it works, whatever you actually do.
Read
