3b1b-manim/from_3b1b/active/bayes/part1.py
2020-02-13 10:54:09 -08:00

4830 lines
142 KiB
Python

from manimlib.imports import *
import scipy.integrate
OUTPUT_DIRECTORY = "bayes/part1"
HYPOTHESIS_COLOR = YELLOW
NOT_HYPOTHESIS_COLOR = GREY
EVIDENCE_COLOR1 = BLUE_C
EVIDENCE_COLOR2 = BLUE_E
NOT_EVIDENCE_COLOR1 = GREY
NOT_EVIDENCE_COLOR2 = DARK_GREY
#
def get_bayes_formula(expand_denominator=False):
t2c = {
"{H}": HYPOTHESIS_COLOR,
"{\\neg H}": NOT_HYPOTHESIS_COLOR,
"{E}": EVIDENCE_COLOR1,
}
substrings_to_isolate = ["P", "\\over", "=", "\\cdot", "+"]
tex = "P({H} | {E}) = {P({H}) P({E} | {H}) \\over "
if expand_denominator:
tex += "P({H}) P({E} | {H}) + P({\\neg H}) \\cdot P({E} | {\\neg H})}"
else:
tex += "P({E})}"
formula = TexMobject(
tex,
tex_to_color_map=t2c,
substrings_to_isolate=substrings_to_isolate,
)
formula.posterior = formula[:6]
formula.prior = formula[8:12]
formula.likelihood = formula[13:19]
if expand_denominator:
pass
formula.denom_prior = formula[20:24]
formula.denom_likelihood = formula[25:31]
formula.denom_anti_prior = formula[32:36]
formula.denom_anti_likelihood = formula[37:42]
else:
formula.p_evidence = formula[20:]
return formula
class BayesDiagram(VGroup):
CONFIG = {
"height": 2,
"square_style": {
"fill_color": DARK_GREY,
"fill_opacity": 1,
"stroke_color": WHITE,
"stroke_width": 2,
},
"rect_style": {
"stroke_color": WHITE,
"stroke_width": 1,
"fill_opacity": 1,
},
"hypothesis_color": HYPOTHESIS_COLOR,
"not_hypothesis_color": NOT_HYPOTHESIS_COLOR,
"evidence_color1": EVIDENCE_COLOR1,
"evidence_color2": EVIDENCE_COLOR2,
"not_evidence_color1": NOT_EVIDENCE_COLOR1,
"not_evidence_color2": NOT_EVIDENCE_COLOR2,
"prior_rect_direction": DOWN,
}
def __init__(self, prior, likelihood, antilikelihood, **kwargs):
super().__init__(**kwargs)
square = Square(side_length=self.height)
square.set_style(**self.square_style)
# Create all rectangles
h_rect, nh_rect, he_rect, nhe_rect, hne_rect, nhne_rect = [
square.copy().set_style(**self.rect_style)
for x in range(6)
]
# Add as attributes
self.square = square
self.h_rect = h_rect # Hypothesis
self.nh_rect = nh_rect # Not hypothesis
self.he_rect = he_rect # Hypothesis and evidence
self.hne_rect = hne_rect # Hypothesis and not evidence
self.nhe_rect = nhe_rect # Not hypothesis and evidence
self.nhne_rect = nhne_rect # Not hypothesis and not evidence
# Stretch the rectangles
for rect in h_rect, he_rect, hne_rect:
rect.stretch(prior, 0, about_edge=LEFT)
for rect in nh_rect, nhe_rect, nhne_rect:
rect.stretch(1 - prior, 0, about_edge=RIGHT)
he_rect.stretch(likelihood, 1, about_edge=DOWN)
hne_rect.stretch(1 - likelihood, 1, about_edge=UP)
nhe_rect.stretch(antilikelihood, 1, about_edge=DOWN)
nhne_rect.stretch(1 - antilikelihood, 1, about_edge=UP)
# Color the rectangles
h_rect.set_fill(self.hypothesis_color)
nh_rect.set_fill(self.not_hypothesis_color)
he_rect.set_fill(self.evidence_color1)
hne_rect.set_fill(self.not_evidence_color1)
nhe_rect.set_fill(self.evidence_color2)
nhne_rect.set_fill(self.not_evidence_color2)
# Add them
self.hypothesis_split = VGroup(h_rect, nh_rect)
self.evidence_split = VGroup(he_rect, hne_rect, nhe_rect, nhne_rect)
# Don't add hypothesis split by default
self.add(self.square, self.hypothesis_split, self.evidence_split)
self.square.set_opacity(0)
self.hypothesis_split.set_opacity(0)
def add_brace_attrs(self, buff=SMALL_BUFF):
braces = self.braces = self.create_braces(buff)
self.braces_buff = buff
attrs = [
"h_brace",
"nh_brace",
"he_brace",
"hne_brace",
"nhe_brace",
"nhne_brace",
]
for brace, attr in zip(braces, attrs):
setattr(self, attr, brace)
return self
def create_braces(self, buff=SMALL_BUFF):
kw = {
"buff": buff,
"min_num_quads": 1,
}
return VGroup(
Brace(self.h_rect, self.prior_rect_direction, **kw),
Brace(self.nh_rect, self.prior_rect_direction, **kw),
Brace(self.he_rect, LEFT, **kw),
Brace(self.hne_rect, LEFT, **kw),
Brace(self.nhe_rect, RIGHT, **kw),
Brace(self.nhne_rect, RIGHT, **kw),
)
def refresh_braces(self):
if hasattr(self, "braces"):
self.braces.become(
self.create_braces(self.braces_buff)
)
return self
def set_prior(self, new_prior):
p = new_prior
q = 1 - p
full_width = self.square.get_width()
left_rects = [self.h_rect, self.he_rect, self.hne_rect]
right_rects = [self.nh_rect, self.nhe_rect, self.nhne_rect]
for group, vect, value in [(left_rects, LEFT, p), (right_rects, RIGHT, q)]:
for rect in group:
rect.set_width(
value * full_width,
stretch=True,
about_edge=vect,
)
self.refresh_braces()
return self
def general_set_likelihood(self, new_likelihood, low_rect, high_rect):
height = self.square.get_height()
low_rect.set_height(
new_likelihood * height,
stretch=True,
about_edge=DOWN,
)
high_rect.set_height(
(1 - new_likelihood) * height,
stretch=True,
about_edge=UP,
)
self.refresh_braces()
return self
def set_likelihood(self, new_likelihood):
self.general_set_likelihood(
new_likelihood,
self.he_rect,
self.hne_rect,
)
return self
def set_antilikelihood(self, new_antilikelihood):
self.general_set_likelihood(
new_antilikelihood,
self.nhe_rect,
self.nhne_rect,
)
return self
def copy(self):
return self.deepcopy()
class ProbabilityBar(VGroup):
CONFIG = {
"color1": BLUE_D,
"color2": GREY_BROWN,
"height": 0.5,
"width": 6,
"rect_style": {
"stroke_width": 1,
"stroke_color": WHITE,
"fill_opacity": 1,
},
"include_braces": False,
"brace_direction": UP,
"include_percentages": True,
"percentage_background_stroke_width": 2,
}
def __init__(self, p=0.5, **kwargs):
super().__init__(**kwargs)
self.add_backbone()
self.add_p_tracker(p)
self.add_bars()
if self.include_braces:
self.braces = always_redraw(lambda: self.get_braces())
self.add(self.braces)
if self.include_percentages:
self.percentages = always_redraw(lambda: self.get_percentages())
self.add(self.percentages)
def add_backbone(self):
backbone = Line()
backbone.set_opacity(0)
backbone.set_width(self.width)
self.backbone = backbone
self.add(backbone)
def add_p_tracker(self, p):
self.p_tracker = ValueTracker(p)
def add_bars(self):
bars = VGroup(Rectangle(), Rectangle())
bars.set_height(self.height)
colors = [self.color1, self.color2]
for bar, color in zip(bars, colors):
bar.set_style(**self.rect_style)
bar.set_fill(color=color)
bars.add_updater(self.update_bars)
self.bars = bars
self.add(bars)
def update_bars(self, bars):
vects = [LEFT, RIGHT]
p = self.p_tracker.get_value()
values = [p, 1 - p]
total_width = self.backbone.get_width()
for bar, vect, value in zip(bars, vects, values):
bar.set_width(value * total_width, stretch=True)
bar.move_to(self.backbone, vect)
return bars
def get_braces(self):
return VGroup(*[
Brace(
bar,
self.brace_direction,
min_num_quads=1,
buff=SMALL_BUFF,
)
for bar in self.bars
])
def get_percentages(self):
p = self.p_tracker.get_value()
labels = VGroup(*[
Integer(value, unit="\\%")
for value in [
np.floor(p * 100),
100 - np.floor(p * 100),
]
])
for label, bar in zip(labels, self.bars):
label.set_height(0.75 * bar.get_height())
min_width = 0.75 * bar.get_width()
if label.get_width() > min_width:
label.set_width(min_width)
label.move_to(bar)
label.set_stroke(
BLACK,
self.percentage_background_stroke_width,
background=True
)
return labels
def add_icons(self, *icons, buff=SMALL_BUFF):
if hasattr(self, "braces"):
refs = self.braces
else:
refs = self.bars
for icon, ref in zip(icons, refs):
icon.ref = ref
icon.add_updater(lambda i: i.next_to(
i.ref,
self.brace_direction,
buff=buff
))
self.icons = VGroup(*icons)
self.add(self.icons)
class Steve(SVGMobject):
CONFIG = {
"file_name": "steve",
"fill_color": GREY,
"sheen_factor": 0.5,
"sheen_direction": UL,
"stroke_width": 0,
"height": 3,
"include_name": True,
"name": "Steve"
}
def __init__(self, **kwargs):
super().__init__(**kwargs)
if self.include_name:
self.add_name()
def add_name(self):
self.name = TextMobject(self.name)
self.name.match_width(self)
self.name.next_to(self, DOWN, SMALL_BUFF)
self.add(self.name)
class Linda(Steve):
CONFIG = {
"file_name": "linda",
"name": "Linda"
}
class LibrarianIcon(SVGMobject):
CONFIG = {
"file_name": "book",
"stroke_width": 0,
"fill_color": LIGHT_GREY,
"sheen_factor": 0.5,
"sheen_direction": UL,
"height": 0.75,
}
class FarmerIcon(SVGMobject):
CONFIG = {
"file_name": "farming",
"stroke_width": 0,
"fill_color": GREEN_E,
"sheen_factor": 0.5,
"sheen_direction": UL,
"height": 1.5,
}
class PitchforkIcon(SVGMobject):
CONFIG = {
"file_name": "pitch_fork_and_roll",
"stroke_width": 0,
"fill_color": LIGHT_GREY,
"sheen_factor": 0.5,
"sheen_direction": UL,
"height": 1.5,
}
class Person(SVGMobject):
CONFIG = {
"file_name": "person",
"height": 1.5,
"stroke_width": 0,
"fill_opacity": 1,
"fill_color": LIGHT_GREY,
}
class Librarian(Person):
CONFIG = {
"IconClass": LibrarianIcon,
"icon_style": {
"background_stroke_width": 5,
"background_stroke_color": BLACK,
},
}
def __init__(self, **kwargs):
super().__init__(**kwargs)
icon = self.IconClass()
icon.set_style(**self.icon_style)
icon.match_width(self)
icon.move_to(self.get_corner(DR), DOWN)
self.add(icon)
class Farmer(Librarian):
CONFIG = {
"IconClass": FarmerIcon,
"icon_style": {
"background_stroke_width": 2,
},
"fill_color": GREEN,
}
# Scenes
class Test(Scene):
def construct(self):
icon = FarmerIcon()
icon.scale(2)
self.add(icon)
# self.add(get_submobject_index_labels(icon))
# class FullFormulaIndices(Scene):
# def construct(self):
# formula = get_bayes_formula(expand_denominator=True)
# formula.set_width(FRAME_WIDTH - 1)
# self.add(formula)
# self.add(get_submobject_index_labels(formula))
class IntroduceFormula(Scene):
def construct(self):
formula = get_bayes_formula()
formula.save_state()
formula.set_width(FRAME_WIDTH - 1)
def get_formula_slice(*indices):
return VGroup(*[formula[i] for i in indices])
H_label = formula.get_part_by_tex("{H}")
E_label = formula.get_part_by_tex("{E}")
hyp_label = TextMobject("Hypothesis")
hyp_label.set_color(HYPOTHESIS_COLOR)
hyp_label.next_to(H_label, UP, LARGE_BUFF)
evid_label = TextMobject("Evidence")
evid_label.set_color(EVIDENCE_COLOR1)
evid_label.next_to(E_label, DOWN, LARGE_BUFF)
hyp_arrow = Arrow(hyp_label.get_bottom(), H_label.get_top(), buff=SMALL_BUFF)
evid_arrow = Arrow(evid_label.get_top(), E_label.get_bottom(), buff=SMALL_BUFF)
self.add(formula[:6])
# self.add(get_submobject_index_labels(formula))
# return
self.play(
FadeInFrom(hyp_label, DOWN),
GrowArrow(hyp_arrow),
FadeInFrom(evid_label, UP),
GrowArrow(evid_arrow),
)
self.wait()
# Prior
self.play(
ShowCreation(formula.get_part_by_tex("=")),
TransformFromCopy(
get_formula_slice(0, 1, 2, 5),
get_formula_slice(8, 9, 10, 11),
),
)
# Likelihood
lhs_copy = formula[:6].copy()
likelihood = formula[12:18]
run_time = 1
self.play(
lhs_copy.next_to, likelihood, UP,
run_time=run_time,
)
self.play(
Swap(lhs_copy[2], lhs_copy[4]),
run_time=run_time,
)
self.play(
lhs_copy.move_to, likelihood,
run_time=run_time,
)
# Evidence
self.play(
ShowCreation(formula.get_part_by_tex("\\over")),
TransformFromCopy(
get_formula_slice(0, 1, 4, 5),
get_formula_slice(19, 20, 21, 22),
),
)
self.wait()
self.clear()
self.play(
formula.restore,
formula.scale, 1.5,
formula.to_edge, UP,
FadeOut(VGroup(
hyp_arrow, hyp_label,
evid_arrow, evid_label,
))
)
class StateGoal(PiCreatureScene, Scene):
CONFIG = {
"default_pi_creature_kwargs": {
"color": BLUE_B,
"height": 2,
},
}
def construct(self):
# Zoom to later
you = self.pi_creature
line = NumberLine(
x_min=-2,
x_max=12,
include_tip=True
)
line.to_edge(DOWN, buff=1.5)
line.to_edge(LEFT, buff=-0.5)
you.next_to(line.n2p(0), UP)
you_label = TextMobject("you")
you_label.next_to(you, RIGHT, MED_LARGE_BUFF)
you_arrow = Arrow(you_label.get_left(), you.get_right() + 0.5 * LEFT, buff=0.1)
now_label = TextMobject("Now")
later_label = TextMobject("Later")
now_label.next_to(line.n2p(0), DOWN)
later_label.next_to(line.n2p(10), DOWN)
self.add(line, now_label)
self.add(you)
self.play(
FadeInFrom(you_label, LEFT),
GrowArrow(you_arrow),
you.change, "pondering",
)
self.wait()
you_label.add(you_arrow)
self.play(
you.change, "horrified",
you.look, DOWN,
you.next_to, line.n2p(10), UP,
MaintainPositionRelativeTo(you_label, you),
FadeInFromPoint(later_label, now_label.get_center()),
)
self.wait()
# Add bubble
bubble = you.get_bubble(
height=4,
width=6,
)
bubble.set_fill(opacity=0)
formula = get_bayes_formula()
bubble.position_mobject_inside(formula)
self.play(
you.change, "confused", bubble,
ShowCreation(bubble),
)
self.play(FadeIn(formula))
self.play(you.change, "hooray", formula)
self.wait(2)
# Show examples
icons = VGroup(
SVGMobject(file_name="science"),
SVGMobject(file_name="robot"),
)
for icon in icons:
icon.set_stroke(width=0)
icon.set_fill(GREY)
icon.set_sheen(1, UL)
icon.set_height(1.5)
icons[0].set_stroke(GREY, 3, background=True)
gold = self.get_gold()
icons.add(gold)
icons.arrange(DOWN, buff=MED_LARGE_BUFF)
icons.to_corner(UL)
for icon in icons[:2]:
self.play(
Write(icon, run_time=2),
you.change, "thinking", icon,
)
self.play(
Blink(you),
FadeOut(VGroup(
line, now_label, later_label,
you_label, you_arrow
)),
)
self.play(
FadeInFrom(gold, LEFT),
you.change, "erm", gold,
)
self.play(Blink(you))
# Brief Thompson description
words = VGroup(
TextMobject("1988").scale(1.5),
TextMobject("Tommy Thompson\\\\and friends"),
)
words.arrange(DOWN, buff=0.75)
ship = ImageMobject("ss_central_america")
ship.set_width(4)
ship.move_to(gold, DL)
ship_title = TextMobject("SS Central America")
ship_title.next_to(ship, UP)
words.next_to(ship, RIGHT)
self.play(
FadeInFrom(words[0], LEFT),
you.change, "tease", words,
FadeOut(icons[:2]),
)
self.play(FadeInFrom(words[1], UP))
self.wait()
self.add(ship, gold)
self.play(
FadeIn(ship),
gold.scale, 0.2,
gold.move_to, ship,
)
self.play(FadeInFromDown(ship_title))
self.play(you.change, "thinking", ship)
amount = TexMobject("> \\$700{,}000{,}000")
amount.scale(1.5)
amount.next_to(ship, DOWN, MED_LARGE_BUFF)
amount.to_edge(LEFT, buff=2)
amount.set_color(YELLOW)
gold_copy = gold.copy()
self.play(
gold_copy.scale, 3,
gold_copy.next_to, amount, LEFT,
FadeIn(amount),
)
self.play(Blink(you))
self.wait()
self.play(LaggedStartMap(
FadeOutAndShift,
Group(*words, ship_title, ship, gold, gold_copy, amount),
))
# Levels of understanding
# Turn bubble into level points
level_points = VGroup(*[bubble.copy() for x in range(3)])
for n, point in enumerate(level_points):
point.set_width(0.5)
point.set_height(0.5, stretch=True)
point.add(*[
point[-1].copy().scale(1.2**k)
for k in range(1, n + 1)
])
point[:3].scale(1.2**n, about_point=point[3].get_center())
point.set_stroke(width=2)
point.set_fill(opacity=0)
level_points.arrange(DOWN, buff=LARGE_BUFF)
title = TextMobject("Levels of understanding")
title.scale(1.5)
title.to_corner(UL)
underline = Line()
underline.match_width(title)
underline.move_to(title, DOWN)
title.add(underline)
level_points.next_to(title, DOWN, buff=1.5)
level_points.to_edge(LEFT)
level_points.set_submobject_colors_by_gradient(GREEN, YELLOW, RED)
self.remove(bubble)
self.play(
formula.to_corner, UR,
FadeOut(you),
*[
ReplacementTransform(bubble.copy(), point)
for point in level_points
],
)
self.play(Write(title, run_time=1))
self.wait()
# Write level 1
level_labels = VGroup(
TextMobject("What is it saying?"),
TextMobject("Why is it true?"),
TextMobject("When is it useful?"),
)
for lp, ll in zip(level_points, level_labels):
ll.scale(1.25)
ll.match_color(lp)
ll.next_to(lp, RIGHT)
formula_parts = VGroup(
formula.prior,
formula.likelihood,
formula.p_evidence,
formula.posterior,
).copy()
formula_parts.generate_target()
formula_parts.target.scale(1.5)
formula_parts.target.arrange(DOWN, buff=LARGE_BUFF, aligned_edge=LEFT)
formula_parts.target.next_to(formula, DOWN, buff=LARGE_BUFF)
formula_parts.target.shift(3 * LEFT)
equal_signs = VGroup(*[
TextMobject("=").next_to(fp, RIGHT)
for fp in formula_parts.target
])
kw = {
"tex_to_color_map": {
"hypothesis": HYPOTHESIS_COLOR,
"evidence": EVIDENCE_COLOR1,
},
"alignment": "",
}
meanings = VGroup(
TextMobject("Probability a hypothesis is true\\\\(before any evidence)", **kw),
TextMobject("Probability of seeing the evidence \\quad \\\\if the hypothesis is true", **kw),
TextMobject("Probability of seeing the evidence", **kw),
TextMobject("Probability a hypothesis is true\\\\given some evidence", **kw),
)
for meaning, equals in zip(meanings, equal_signs):
meaning.scale(0.5)
meaning.next_to(equals, RIGHT)
self.play(
FadeIn(level_labels[0], lag_ratio=0.1),
MoveToTarget(formula_parts),
LaggedStartMap(FadeInFrom, equal_signs, lambda m: (m, RIGHT)),
LaggedStartMap(FadeIn, meanings),
)
self.wait()
# Write level 2
diagram = BayesDiagram(0.35, 0.5, 0.2, height=2.5)
diagram.next_to(formula, DOWN, aligned_edge=LEFT)
braces = VGroup(*[
Brace(diagram.he_rect, vect, buff=SMALL_BUFF)
for vect in [DOWN, LEFT]
])
formula_parts.generate_target()
formula_parts.target[:2].scale(0.5)
formula_parts.target[0].next_to(braces[0], DOWN, SMALL_BUFF)
formula_parts.target[1].next_to(braces[1], LEFT, SMALL_BUFF)
pe_picture = VGroup(
diagram.he_rect.copy(),
TexMobject("+"),
diagram.nhe_rect.copy()
)
pe_picture.arrange(RIGHT, buff=SMALL_BUFF)
pe_picture.next_to(equal_signs[2], RIGHT)
phe_picture = VGroup(
diagram.he_rect.copy(),
Line().match_width(pe_picture),
pe_picture.copy(),
)
phe_picture.arrange(DOWN, buff=MED_SMALL_BUFF)
phe_picture.next_to(equal_signs[3], RIGHT)
pe_picture.scale(0.5, about_edge=LEFT)
phe_picture.scale(0.3, about_edge=LEFT)
self.play(
FadeOut(meanings),
FadeOut(equal_signs[:2]),
MoveToTarget(formula_parts),
FadeIn(diagram),
LaggedStartMap(GrowFromCenter, braces),
FadeIn(level_labels[1], lag_ratio=0.1),
level_labels[0].set_opacity, 0.5,
)
self.play(
TransformFromCopy(diagram.he_rect, pe_picture[0]),
TransformFromCopy(diagram.nhe_rect, pe_picture[2]),
FadeIn(pe_picture[1]),
)
self.play(
TransformFromCopy(pe_picture, phe_picture[2]),
TransformFromCopy(pe_picture[0], phe_picture[0]),
ShowCreation(phe_picture[1])
)
self.wait()
# Write level 3
steve = Steve(height=3)
steve.to_edge(RIGHT, buff=2)
arrow = Arrow(level_points.get_bottom(), level_points.get_top(), buff=0)
arrow.shift(0.25 * LEFT)
self.play(
LaggedStartMap(
FadeOutAndShift,
VGroup(
VGroup(diagram, braces, formula_parts[:2]),
VGroup(formula_parts[2], equal_signs[2], pe_picture),
VGroup(formula_parts[3], equal_signs[3], phe_picture),
),
lambda m: (m, 3 * RIGHT),
),
FadeIn(level_labels[2], lag_ratio=0.1),
level_labels[1].set_opacity, 0.5,
)
self.wait()
self.play(
GrowArrow(arrow),
level_points.shift, 0.5 * RIGHT,
level_labels.shift, 0.5 * RIGHT,
level_labels.set_opacity, 1,
)
self.wait()
self.play(Write(steve, run_time=3))
self.wait()
# Transition to next scene
self.play(
steve.to_corner, UR,
Uncreate(arrow),
LaggedStartMap(
FadeOutAndShift,
VGroup(
title,
formula,
*level_points,
*level_labels,
),
lambda m: (m, DOWN),
),
)
self.wait()
def get_gold(self):
gold = SVGMobject(file_name="gold_bars")[0]
gold.set_stroke(width=0)
gold.set_fill(GOLD)
gold.set_sheen(0.5, UP)
gold.flip(UP)
gold_copy = gold.copy()
gold_copy.shift(2 * OUT)
rects = VGroup()
for curve in CurvesAsSubmobjects(gold):
p1 = curve.points[0]
p2 = curve.points[-1]
rect = Polygon(p1, p2, p2 + 2 * OUT, p1 + 2 * OUT)
rect.match_style(gold)
# rect.set_fill(GOLD)
# rect.set_sheen(1, UL)
rects.add(rect)
rects.sort(lambda p: p[1])
gold.add(*rects)
gold.add(gold_copy)
# gold = rects
gold.rotate(2 * DEGREES, UP)
gold.rotate(2 * DEGREES, RIGHT)
gold.set_shade_in_3d(True)
gold.set_height(1.5)
gold.set_stroke(BLACK, 0.5)
return gold
class DescriptionOfSteve(Scene):
def construct(self):
self.write_description()
self.compare_probabilities()
def write_description(self):
steve = Steve(height=3)
steve.to_corner(UR)
description = self.get_description()
description.to_edge(LEFT)
description.align_to(steve, UP)
mt_parts = VGroup(
description.get_part_by_tex("meek"),
description.get_part_by_tex("soul"),
)
mt_parts.set_color(WHITE)
self.add(steve)
self.play(
FadeIn(description),
run_time=3,
lag_ratio=0.01,
rate_func=linear,
)
self.wait(3)
lines = VGroup(*[
Line(mob.get_corner(DL), mob.get_corner(DR), color=YELLOW)
for mob in mt_parts
])
self.play(
ShowCreation(lines),
mt_parts.set_color, YELLOW,
)
self.play(FadeOut(lines))
self.wait()
def compare_probabilities(self):
bar = ProbabilityBar(
0.5, width=10,
include_braces=True,
percentage_background_stroke_width=2,
)
icons = VGroup(
LibrarianIcon(),
FarmerIcon(),
)
for icon, text, half in zip(icons, ["Librarian", "Farmer"], bar.bars):
icon.set_height(0.7)
label = TextMobject(text)
label.next_to(icon, DOWN, buff=SMALL_BUFF)
label.set_color(
interpolate_color(half.get_color(), WHITE, 0.5)
)
icon.add(label)
bar.add_icons(*icons)
bar.move_to(1.75 * DOWN)
bar.icons.set_opacity(0)
q_marks = TexMobject(*"???")
q_marks.scale(1.5)
q_marks.space_out_submobjects(1.5)
q_marks.next_to(bar, DOWN)
self.play(FadeIn(bar))
self.wait()
self.play(
bar.p_tracker.set_value, 0.9,
bar.icons[0].set_opacity, 1,
)
self.wait()
self.play(
bar.p_tracker.set_value, 0.1,
bar.icons[1].set_opacity, 1,
)
self.play(
LaggedStartMap(
FadeInFrom, q_marks,
lambda m: (m, UP),
run_time=2,
),
ApplyMethod(
bar.p_tracker.set_value, 0.7,
run_time=8,
)
)
for value in 0.3, 0.7:
self.play(
bar.p_tracker.set_value, 0.3,
run_time=7,
)
def get_description(self):
return TextMobject(
"""
Steve is very shy and withdrawn,\\\\
invariably helpful but with very\\\\
little interest in people or in the\\\\
world of reality. A meek and tidy\\\\
soul, he has a need for order and\\\\
structure, and a passion for detail.\\\\
""",
tex_to_color_map={
"shy and withdrawn": BLUE,
"meek and tidy": YELLOW,
"soul": YELLOW,
},
alignment="",
)
class IntroduceKahnemanAndTversky(DescriptionOfSteve, MovingCameraScene):
def construct(self):
# Introduce K and T
images = Group(
ImageMobject("kahneman"),
ImageMobject("tversky"),
)
danny, amos = images
images.set_height(3.5)
images.arrange(DOWN, buff=0.5)
images.to_edge(LEFT, buff=MED_LARGE_BUFF)
names = VGroup(
TextMobject("Daniel\\\\Kahneman", alignment=""),
TextMobject("Amos\\\\Tversky", alignment=""),
)
for name, image in zip(names, images):
name.scale(1.25)
name.next_to(image, RIGHT)
image.name = name
prize = ImageMobject("nobel_prize", height=1)
prize.move_to(danny, UR)
prize.shift(MED_SMALL_BUFF * UR)
books = Group(
ImageMobject("thinking_fast_and_slow"),
ImageMobject("undoing_project"),
)
books.set_height(5)
books.arrange(RIGHT, buff=0.5)
books.to_edge(RIGHT, buff=MED_LARGE_BUFF)
self.play(
FadeInFrom(danny, DOWN),
FadeInFrom(danny.name, LEFT),
)
self.play(
FadeInFrom(amos, UP),
FadeInFrom(amos.name, LEFT),
)
self.wait()
self.play(FadeInFromLarge(prize))
self.wait()
for book in books:
self.play(FadeInFrom(book, LEFT))
self.wait()
# Show them thinking
for image in images:
image.generate_target()
amos.target.to_corner(DL)
danny.target.to_corner(DR)
targets = Group(amos.target, danny.target)
bubble = ThoughtBubble(
width=7, height=4,
)
bubble.next_to(targets, UP)
new_stem = bubble[:-1].copy()
new_stem.rotate(PI, UP, about_point=targets.get_top())
new_stem.shift(SMALL_BUFF * DR)
bubble.add_to_back(*new_stem)
bubble[-1].scale(1.2)
bubble[-1].to_edge(UP, buff=0)
bubble[:-1].shift(DOWN)
bubble.set_fill(DARK_GREY, 1)
randy = Randolph(color=BLUE_B)
randy.set_height(1)
randy.next_to(bubble[-1].get_center(), DL)
randy.shift(LEFT)
lil_bubble = ThoughtBubble(height=1.5, width=2)
lil_bubble.next_to(randy, UR, buff=0)
lil_bubble[:-1].rotate(
PI, axis=UR, about_point=lil_bubble[:-1].get_corner(UL),
)
lil_bubble.move_to(randy.get_top(), DL)
for i, part in enumerate(lil_bubble[-2::-1]):
part.rotate(90 * DEGREES)
part.shift(0.05 * i * UR)
lil_bubble[-1].scale(0.8)
librarian = TextMobject("Librarian")
librarian.set_color(BLUE)
librarian.scale(0.5)
librarian.move_to(lil_bubble[-1])
bar = ProbabilityBar(percentage_background_stroke_width=1)
bar.add_icons(
LibrarianIcon(height=1),
FarmerIcon(height=1),
)
bar.scale(0.5)
bar.next_to(randy, RIGHT, buff=0.75)
bar.update()
self.play(
LaggedStartMap(MoveToTarget, images),
LaggedStartMap(FadeOutAndShiftDown, books),
LaggedStartMap(FadeOut, Group(prize, *names)),
)
self.play(
DrawBorderThenFill(bubble),
FadeInFrom(
randy, UR,
rate_func=squish_rate_func(smooth, 0.5, 1),
run_time=2,
)
)
self.play(
DrawBorderThenFill(lil_bubble),
randy.change, "pondering",
)
self.play(Blink(randy))
self.play(Write(librarian))
self.add(bar, lil_bubble, librarian)
self.play(FadeIn(bar))
self.play(
bar.p_tracker.set_value, 1 / 6,
randy.change, "thinking", bar,
)
self.play(Blink(randy))
self.wait()
# Zoom in
description = self.get_description()
description.scale(0.4)
description.next_to(randy, UL)
description.shift(1.25 * RIGHT + 0.75 * UP)
description.set_color(WHITE)
frame = self.camera_frame
steve = Steve()
steve.match_height(description)
steve.align_to(bar, RIGHT)
steve.align_to(description, UP)
# cross = Cross(librarian)
book_border = bar.icons[0].copy()
farm_border = bar.icons[1].copy()
for border in [book_border, farm_border]:
border.set_fill(opacity=0)
border.set_stroke(YELLOW, 1)
seems_bookish = TextMobject("Seems\\\\bookish")
seems_bookish.match_width(librarian)
seems_bookish.scale(0.8)
seems_bookish.move_to(librarian)
self.play(
frame.scale, 0.5,
frame.move_to, bubble[-1], DOWN,
frame.shift, 0.75 * LEFT,
FadeOut(bubble),
FadeOut(images),
FadeOut(lil_bubble),
FadeOut(librarian),
FadeIn(description, lag_ratio=0.05),
randy.change, "pondering", description,
run_time=6,
)
self.play(randy.change, "happy", description)
self.play(
description.get_part_by_tex("shy").set_color, BLUE,
lag_ratio=0.1,
)
self.play(
description.get_part_by_tex("meek").set_color, YELLOW,
description.get_part_by_tex("soul").set_color, YELLOW,
lag_ratio=0.1,
)
self.play(
bar.p_tracker.set_value, 0.9,
FadeIn(lil_bubble),
Write(librarian),
)
self.play(ShowCreationThenFadeOut(book_border))
self.play(Blink(randy))
self.play(
FadeInFromDown(steve),
randy.look_at, steve,
)
self.play(
randy.change, "tease", steve,
FadeOut(librarian),
FadeIn(seems_bookish),
)
lil_bubble.add(seems_bookish)
self.wait()
self.play(Blink(randy))
self.play(
LaggedStartMap(
FadeOutAndShift, lil_bubble,
lambda m: (m, LEFT),
run_time=1,
),
bar.p_tracker.set_value, 1 / 6,
randy.change, "confused", bar,
)
self.play(
ShowCreationThenFadeOut(farm_border)
)
self.wait()
# Transition to next scene
fh = frame.get_height()
fw = frame.get_width()
center = frame.get_center()
right = (fw / FRAME_WIDTH) * RIGHT
up = (fh / FRAME_HEIGHT) * UP
left = -right
down = -up
book, farm = bar.icons.deepcopy()
bar.clear_updaters()
bar.icons.set_opacity(0)
for mob in book, farm:
mob.clear_updaters()
mob.generate_target(use_deepcopy=True)
mob.target.set_height(get_norm(up))
mob.target.move_to(center + down + 2 * left)
farm.target.shift(4 * right)
steve.generate_target()
steve.target.match_width(book.target)
steve.target.move_to(book.target, DOWN)
steve.target.shift(3 * up)
steve_copy = steve.target.copy()
steve_copy.match_x(farm.target),
self.play(
TransformFromCopy(steve, steve_copy),
LaggedStartMap(MoveToTarget, VGroup(steve, book, farm)),
LaggedStartMap(
FadeOutAndShift,
description,
lambda m: (m, LEFT)
),
FadeOutAndShift(randy, LEFT),
FadeOutAndShift(bar, LEFT),
)
class CorrectViewOfFarmersAndLibrarians(Scene):
def construct(self):
# Match last scene
steves = VGroup(Steve(), Steve())
book = LibrarianIcon()
farm = FarmerIcon()
icons = VGroup(book, farm)
for mob in icons:
mob.set_height(1)
mob.move_to(DOWN + 2 * LEFT)
farm.shift(4 * RIGHT)
steves.match_width(book)
steves.move_to(book, DOWN)
steves.shift(3 * UP)
steve1, steve2 = steves
steve2.match_x(farm)
self.add(steves, book, farm)
# Add arrows
arrows = VGroup(*[
Arrow(s.get_bottom(), m.get_top())
for s, m in zip(steves, icons)
])
words = VGroup(
TextMobject("Stereotype"),
TextMobject("Unexpected"),
)
for arrow, word, vect in zip(arrows, words, [LEFT, RIGHT]):
word.scale(1.5)
word.next_to(arrow, vect)
self.play(
GrowArrow(arrow),
FadeInFrom(word, UP),
)
self.wait()
# Show people proportions
librarian = Librarian()
farmer = Farmer()
librarian.move_to(LEFT).to_edge(UP)
farmer.move_to(RIGHT).to_edge(UP)
farmer_ul = farmer.get_corner(UL)
farmers = VGroup(farmer, *[farmer.copy() for x in range(19)])
farmers.arrange_in_grid(n_rows=4)
farmers.move_to(farmer_ul, UL)
farmer_outlines = farmers.copy()
farmer_outlines.set_fill(opacity=0)
farmer_outlines.set_stroke(YELLOW, 1)
farmer_count = Integer(1)
farmer_count.scale(2)
farmer_count.set_color(GREEN)
farmer_count.next_to(farmers, LEFT, buff=LARGE_BUFF)
farmer_count.add_updater(lambda m: m.set_value(len(farmer_outlines)))
for person, icon in zip([librarian, farmer], icons):
person.save_state()
person[:-1].set_opacity(0)
person.scale(
icon.get_height() / person[-1].get_height()
)
person.move_to(icon, DR)
self.remove(*icons)
self.play(
LaggedStartMap(FadeOut, VGroup(steves, arrows, words)),
Restore(librarian),
Restore(farmer),
run_time=1,
)
self.play(
LaggedStartMap(FadeIn, farmers[1:])
)
self.wait()
self.add(farmer_count)
self.play(
ShowIncreasingSubsets(farmer_outlines),
int_func=np.ceil,
rate_func=linear,
run_time=2,
)
self.play(FadeOut(farmer_outlines))
self.wait()
# Show higher number of farmers
farmers.save_state()
farmers.generate_target()
farmers.target.scale(0.5, about_edge=UL)
new_farmers = VGroup(*it.chain(*[
farmers.target.copy().next_to(
farmers.target, vect, buff=SMALL_BUFF,
)
for vect in [RIGHT, DOWN]
]))
new_farmers[-10:].align_to(new_farmers, RIGHT)
new_farmers[-10:].align_to(new_farmers[-20], UP)
farmer_count.clear_updaters()
self.play(
MoveToTarget(farmers),
ShowIncreasingSubsets(new_farmers),
ChangeDecimalToValue(farmer_count, 60),
)
self.wait()
self.play(
FadeOut(new_farmers),
Restore(farmers),
ChangeDecimalToValue(farmer_count, 20),
)
self.wait()
# Organize into a representative sample
farmers.generate_target()
librarian.generate_target()
group = VGroup(librarian.target, *farmers.target)
self.arrange_bottom_row(group)
l_brace = self.get_count_brace(VGroup(librarian.target))
f_brace = self.get_count_brace(farmers.target)
self.play(
MoveToTarget(librarian),
MoveToTarget(farmers),
ReplacementTransform(farmer_count, f_brace[-1]),
GrowFromCenter(f_brace[:-1]),
)
self.play(GrowFromCenter(l_brace))
self.wait()
def get_count_brace(self, people):
brace = Brace(people, UP, buff=SMALL_BUFF)
count = Integer(len(people), edge_to_fix=DOWN)
count.next_to(brace, UP, SMALL_BUFF)
brace.add(count)
brace.count = count
return brace
def arrange_bottom_row(self, group):
group.arrange(RIGHT, buff=0.5)
group.set_width(FRAME_WIDTH - 3)
group.to_edge(DOWN)
for person in group:
person[-1].set_stroke(BLACK, 1, background=True)
class ComplainAboutNotKnowingTheStats(TeacherStudentsScene):
def construct(self):
self.student_says(
"Are people expected\\\\to know that?",
student_index=2
)
self.change_student_modes(
"sassy", "sassy",
)
self.play(self.teacher.change, "hesitant")
self.look_at(self.screen)
self.wait(3)
self.teacher_says(
"No, but did you\\\\think to estimate it?",
bubble_kwargs={"width": 4.5, "height": 3.5},
)
self.change_all_student_modes("guilty")
self.wait(2)
self.change_all_student_modes("pondering")
self.wait(3)
class SpoilerAlert(Scene):
def construct(self):
sa_words = TextMobject("Spoiler Alert")
sa_words.scale(2)
sa_words.to_edge(UP)
sa_words.set_color(RED)
alert = Triangle(start_angle=90 * DEGREES)
alert.set_stroke(RED, 8)
alert.set_height(sa_words.get_height())
alert.round_corners(0.1)
bang = TextMobject("!")
bang.set_color(RED)
bang.scale(1.5)
bang.move_to(alert)
alert.add(bang)
alert.next_to(sa_words, LEFT)
sa_words.add(alert.copy())
alert.next_to(sa_words, RIGHT)
sa_words.add(alert)
formula = get_bayes_formula()
formula_words = TextMobject("This is secretly ")
formula_words.scale(1.5)
formula_group = VGroup(formula_words, formula)
formula_group.arrange(DOWN, buff=MED_LARGE_BUFF)
formula_group.next_to(sa_words, DOWN, LARGE_BUFF)
self.add(sa_words)
self.wait()
self.play(FadeInFrom(formula_group, UP))
self.wait()
class ReasonByRepresentativeSample(CorrectViewOfFarmersAndLibrarians):
CONFIG = {
"ignore_icons": False,
# "ignore_icons": True,
}
def construct(self):
# Match previous scene
librarians = VGroup(Librarian())
farmers = VGroup(*[Farmer() for x in range(20)])
everyone = VGroup(*librarians, *farmers)
self.arrange_bottom_row(everyone)
self.add(everyone)
if self.ignore_icons:
for person in everyone:
person.submobjects.pop()
l_brace = self.get_count_brace(librarians)
f_brace = self.get_count_brace(farmers)
braces = VGroup(l_brace, f_brace)
for brace in braces:
brace.count.set_stroke(BLACK, 3, background=True)
brace.count.brace = brace
brace.remove(brace.count)
brace.count_tracker = ValueTracker(brace.count.get_value())
brace.count.add_updater(
lambda c: c.set_value(
c.brace.count_tracker.get_value(),
).next_to(c.brace, UP, SMALL_BUFF)
)
self.add(brace, brace.count)
# Multiply by 10
new_people = VGroup()
for group in [librarians, farmers]:
new_rows = VGroup(*[group.copy() for x in range(9)])
new_rows.arrange(UP, buff=SMALL_BUFF)
new_rows.next_to(group, UP, SMALL_BUFF)
new_people.add(new_rows)
new_librarians, new_farmers = new_people
self.play(
*[
FadeIn(new_rows, lag_ratio=0.1)
for new_rows in new_people
],
*[
ApplyMethod(brace.next_to, new_rows, UP, {"buff": SMALL_BUFF})
for brace, new_rows in zip(braces, new_people)
],
*[
ApplyMethod(
brace.count_tracker.set_value,
10 * brace.count_tracker.get_value(),
)
for brace in braces
],
)
farmers = VGroup(farmers, *new_farmers)
librarians = VGroup(librarians, *new_librarians)
everyone = VGroup(farmers, librarians)
# Add background rectangles
big_rect = SurroundingRectangle(
everyone,
buff=0.05,
stroke_width=1,
stroke_color=WHITE,
fill_opacity=1,
fill_color=DARKER_GREY,
)
left_rect = big_rect.copy()
prior = 1 / 21
left_rect.stretch(prior, 0, about_edge=LEFT)
right_rect = big_rect.copy()
right_rect.stretch(1 - prior, 0, about_edge=RIGHT)
dl_rect = left_rect.copy()
ul_rect = left_rect.copy()
dl_rect.stretch(0.4, 1, about_edge=DOWN)
ul_rect.stretch(0.6, 1, about_edge=UP)
dr_rect = right_rect.copy()
ur_rect = right_rect.copy()
dr_rect.stretch(0.1, 1, about_edge=DOWN)
ur_rect.stretch(0.9, 1, about_edge=UP)
colors = [
interpolate_color(color, BLACK, 0.5)
for color in [EVIDENCE_COLOR1, EVIDENCE_COLOR2]
]
for rect, color in zip([dl_rect, dr_rect], colors):
rect.set_fill(color)
all_rects = VGroup(
left_rect, right_rect,
dl_rect, ul_rect, dr_rect, ur_rect,
)
all_rects.set_opacity(0)
self.add(all_rects, everyone)
self.play(
left_rect.set_opacity, 1,
right_rect.set_opacity, 1,
)
self.wait()
# 40% of librarians and 10% of farmers
for rect, vect in zip([dl_rect, dr_rect], [LEFT, RIGHT]):
rect.set_opacity(1)
rect.save_state()
rect.brace = Brace(rect, vect, buff=SMALL_BUFF)
rect.brace.save_state()
rect.number = Integer(0, unit="\\%")
rect.number.scale(0.75)
rect.number.next_to(rect.brace, vect, SMALL_BUFF)
rect.number.brace = rect.brace
rect.number.vect = vect
rect.number.add_updater(
lambda d: d.set_value(100 * d.brace.get_height() / big_rect.get_height())
)
rect.number.add_updater(lambda d: d.next_to(d.brace, d.vect, SMALL_BUFF))
for mob in [rect, rect.brace]:
mob.stretch(0, 1, about_edge=DOWN)
for rect, to_fade in [(dl_rect, librarians[4:]), (dr_rect, farmers[1:])]:
self.add(rect.brace, rect.number)
self.play(
Restore(rect),
Restore(rect.brace),
to_fade.set_opacity, 0.1,
)
self.wait()
# Emphasize restricted set
highlighted_librarians = librarians[:4].copy()
highlighted_farmers = farmers[0].copy()
for highlights in [highlighted_librarians, highlighted_farmers]:
highlights.set_color(YELLOW)
self.add(braces, *[b.count for b in braces])
self.play(
l_brace.next_to, librarians[:4], UP, SMALL_BUFF,
l_brace.count_tracker.set_value, 4,
ShowIncreasingSubsets(highlighted_librarians)
)
self.play(FadeOut(highlighted_librarians))
self.play(
f_brace.next_to, farmers[:1], UP, SMALL_BUFF,
f_brace.count_tracker.set_value, 20,
ShowIncreasingSubsets(highlighted_farmers),
run_time=2,
)
self.play(FadeOut(highlighted_farmers))
self.wait()
# Write answer
equation = TexMobject(
"P\\left(",
"\\text{Librarian }",
"\\text{given }",
"\\text{description}",
"\\right)",
"=",
"{4", "\\over", " 4", "+", "20}",
"\\approx", "16.7\\%",
)
equation.set_color_by_tex_to_color_map({
"Librarian": HYPOTHESIS_COLOR,
"description": EVIDENCE_COLOR1,
})
equation.set_width(FRAME_WIDTH - 2)
equation.to_edge(UP)
equation_rect = BackgroundRectangle(equation, buff=MED_SMALL_BUFF)
equation_rect.set_fill(opacity=1)
equation_rect.set_stroke(WHITE, width=1, opacity=1)
self.play(
FadeIn(equation_rect),
FadeInFromDown(equation[:6])
)
self.wait()
self.play(
TransformFromCopy(
l_brace.count,
equation.get_parts_by_tex("4")[0],
),
Write(equation.get_part_by_tex("\\over")),
)
self.play(
Write(equation.get_part_by_tex("+")),
TransformFromCopy(
f_brace.count,
equation.get_part_by_tex("20"),
),
TransformFromCopy(
l_brace.count,
equation.get_parts_by_tex("4")[1],
),
)
self.wait()
self.play(FadeIn(equation[-2:]))
self.wait()
# Compare raw likelihoods
axes = Axes(
x_min=0,
x_max=10,
y_min=0,
y_max=100,
y_axis_config={
"unit_size": 0.07,
"tick_frequency": 10,
},
axis_config={
"include_tip": False,
},
)
axes.x_axis.tick_marks.set_opacity(0)
axes.y_axis.add_numbers(
*range(20, 120, 20),
number_config={"unit": "\\%"}
)
axes.center().to_edge(DOWN)
title = TextMobject("Likelihood of fitting the description")
title.scale(1.25)
title.to_edge(UP)
title.shift(RIGHT)
axes.add(title)
lines = VGroup(
Line(axes.c2p(3, 0), axes.c2p(3, 40)),
Line(axes.c2p(7, 0), axes.c2p(7, 10)),
)
rects = VGroup(*[Rectangle() for x in range(2)])
rects.set_fill(EVIDENCE_COLOR1, 1)
rects[1].set_fill(GREEN)
rects.set_stroke(WHITE, 1)
icons = VGroup(LibrarianIcon(), FarmerIcon())
for rect, line, icon in zip(rects, lines, icons):
rect.replace(line, dim_to_match=1)
rect.set_width(1, stretch=True)
icon.set_width(1)
icon.next_to(rect, UP)
y = axes.y_axis.p2n(rect.get_top())
y_point = axes.y_axis.n2p(y)
rect.line = DashedLine(y_point, rect.get_corner(UL))
pre_rects = VGroup()
for r in dl_rect, dr_rect:
r_copy = r.deepcopy()
pre_rects.add(r_copy)
people_copy = VGroup(librarians[:4], farmers[:1]).copy()
everything = self.get_mobjects()
self.play(
*[FadeOut(mob) for mob in everything],
FadeIn(axes),
FadeIn(icons),
*[
TransformFromCopy(pr, r)
for pr, r in zip(pre_rects, rects)
],
FadeOut(people_copy),
)
self.play(*[
ShowCreation(rect.line)
for rect in rects
])
self.wait()
self.play(
FadeOut(axes),
FadeOut(rects[0].line),
FadeOut(rects[1].line),
FadeOut(icons),
*[
ReplacementTransform(r, pr)
for pr, r in zip(pre_rects, rects)
],
# FadeOut(fsfr),
*[FadeIn(mob) for mob in everything],
)
self.remove(*pre_rects)
self.wait()
# Emphasize prior belief
prior_equation = TexMobject(
"P\\left(",
"\\text{Librarian}",
"\\right)",
"=",
"{1", "\\over", "21}",
"\\approx", "4.8\\%",
)
prior_equation.set_color_by_tex_to_color_map({
"Librarian": HYPOTHESIS_COLOR,
})
prior_equation.match_height(equation)
prior_rect = BackgroundRectangle(prior_equation, buff=MED_SMALL_BUFF)
prior_rect.match_style(equation_rect)
group = VGroup(prior_equation, prior_rect)
group.align_to(equation_rect, UP)
group.shift(
(
equation.get_part_by_tex("\\over").get_x() -
prior_equation.get_part_by_tex("\\over").get_x()
) * RIGHT
)
prior_label = TextMobject("Prior belief")
prior_label.scale(1.5)
prior_label.next_to(prior_rect, LEFT, buff=1.5)
prior_label.to_edge(UP, buff=0.25)
prior_label.set_stroke(BLACK, 5, background=True)
prior_arrow = Arrow(
prior_label.get_right(),
prior_equation.get_left(),
buff=SMALL_BUFF,
)
self.play(
VGroup(equation_rect, equation).shift, prior_rect.get_height() * DOWN,
FadeIn(prior_rect),
FadeIn(prior_equation),
FadeInFrom(prior_label, RIGHT),
GrowArrow(prior_arrow),
)
self.wait()
class NewEvidenceUpdatesPriorBeliefs(DescriptionOfSteve):
def construct(self):
# Determining belief in a vacuum
description = self.get_description()
rect = SurroundingRectangle(description)
rect.set_stroke(WHITE, 2)
rect.set_fill(BLACK, 0.9)
evid = VGroup(rect, description)
evid.set_height(2)
librarian = Librarian()
librarian.set_height(2)
arrow = Arrow(LEFT, RIGHT)
arrow.set_stroke(WHITE, 5)
group = VGroup(evid, arrow, librarian)
group.arrange(RIGHT, buff=LARGE_BUFF)
cross = Cross(VGroup(group))
cross.set_stroke(RED, 12)
cross.scale(1.2)
self.add(evid)
self.play(
GrowArrow(arrow),
FadeInFrom(librarian, LEFT)
)
self.play(ShowCreation(cross))
self.wait()
#
icons = VGroup(LibrarianIcon(), FarmerIcon())
for icon in icons:
icon.set_height(0.5)
kw = {
"include_braces": True,
"width": 11,
"height": 0.75,
}
top_bar = ProbabilityBar(p=1 / 21, **kw)
low_bar = ProbabilityBar(p=1 / 21, brace_direction=DOWN, **kw)
bars = VGroup(top_bar, low_bar)
for bar in bars:
bar.percentages[1].add_updater(lambda m: m.set_opacity(0))
bar.add_icons(*icons.copy())
bar.suspend_updating()
new_arrow = Arrow(1.5 * UP, 1.5 * DOWN)
new_arrow.set_stroke(WHITE, 5)
top_bar.next_to(new_arrow, UP)
low_bar.next_to(new_arrow, DOWN)
self.add(arrow, evid, cross)
self.play(
FadeOut(cross),
Transform(arrow, new_arrow),
evid.scale, 0.6,
evid.move_to, new_arrow,
ReplacementTransform(librarian, low_bar.icons[0]),
FadeIn(bars)
)
self.play(low_bar.p_tracker.set_value, 1 / 6)
self.wait()
class HeartOfBayesTheorem(Scene):
def construct(self):
title = TextMobject("Heart of Bayes' theorem")
title.scale(1.5)
title.add(Underline(title))
title.to_edge(UP)
# Bayes diagrams
# prior_tracker = ValueTracker(0.1)
# likelihood_tracker = ValueTracker(0.4)
# antilikelihood_tracker = ValueTracker(0.1)
diagram = self.get_diagram(
prior=0.1, likelihood=0.4, antilikelihood=0.1,
# prior_tracker.get_value(),
# likelihood_tracker.get_value(),
# antilikelihood_tracker.get_value(),
)
diagram.nh_rect.set_fill(GREEN_E)
diagram.hypothesis_split.set_opacity(1)
diagram.evidence_split.set_opacity(0)
restricted_diagram = self.get_restricted_diagram(diagram)
diagram_copy = diagram.copy()
diagram_copy.move_to(restricted_diagram)
diagrams = VGroup(diagram, restricted_diagram)
label1 = TextMobject("All possibilities")
label2 = TextMobject(
"All possibilities\\\\", "fitting the evidence",
tex_to_color_map={"evidence": EVIDENCE_COLOR1},
)
labels = VGroup(label1, label2)
labels.scale(diagram.get_width() / label1.get_width())
for l, d in zip(labels, diagrams):
l.next_to(d, UP)
label2.save_state()
label2[0].move_to(label2, DOWN)
label2[1:].shift(0.25 * UP)
label2[1:].set_opacity(0)
# Final fraction written geometrically
fraction = always_redraw(
lambda: self.get_geometric_fraction(restricted_diagram)
)
frac_box = always_redraw(lambda: DashedVMobject(
SurroundingRectangle(
fraction,
buff=MED_SMALL_BUFF,
stroke_width=2,
stroke_color=WHITE,
),
num_dashes=100,
))
prob = TexMobject(
"P\\left(",
"{\\text{Librarian }",
"\\text{given}", "\\over", "\\text{the evidence}}",
"\\right)"
)
prob.set_color_by_tex("Librarian", HYPOTHESIS_COLOR)
prob.set_color_by_tex("evidence", EVIDENCE_COLOR1)
prob.get_part_by_tex("\\over").set_opacity(0)
prob.match_width(frac_box)
prob.next_to(frac_box, UP)
updaters = VGroup(
diagram, restricted_diagram, fraction, frac_box
)
updaters.suspend_updating()
self.play(
FadeIn(diagram),
FadeInFromDown(label1),
)
self.play(
TransformFromCopy(diagram, diagram_copy),
TransformFromCopy(label1[0], label2[0]),
)
self.play(
Restore(label2),
ReplacementTransform(diagram_copy, restricted_diagram)
)
self.wait()
self.play(
TransformFromCopy(
restricted_diagram.he_rect,
fraction[0],
),
TransformFromCopy(
restricted_diagram.he_rect,
fraction[2][0],
),
TransformFromCopy(
restricted_diagram.nhe_rect,
fraction[2][2],
),
ShowCreation(fraction[1]),
Write(fraction[2][1]),
)
self.add(fraction)
self.play(
ShowCreation(frac_box),
FadeIn(prob)
)
self.wait()
self.play(Write(title, run_time=1))
self.wait()
# Mess with some numbers
updaters.resume_updating()
self.play(*[
ApplyMethod(d.set_prior, 0.4, run_time=2)
for d in diagrams
])
self.play(*[
ApplyMethod(d.set_likelihood, 0.3, run_time=2)
for d in diagrams
])
self.play(*[
ApplyMethod(d.set_antilikelihood, 0.6, run_time=2)
for d in diagrams
])
# self.play(prior_tracker.set_value, 0.4, run_time=2)
# self.play(antilikelihood_tracker.set_value, 0.3, run_time=2)
# self.play(likelihood_tracker.set_value, 0.6, run_time=2)
self.wait()
updaters.suspend_updating()
# Ask about a formula
words = TextMobject("Write this more\\\\mathematically")
words.scale(1.25)
words.set_color(RED)
words.to_corner(UR)
arrow = Arrow(words.get_bottom(), frac_box.get_top(), buff=SMALL_BUFF)
arrow.match_color(words)
arrow.set_stroke(width=5)
self.play(
FadeInFrom(words, DOWN),
GrowArrow(arrow),
FadeOut(prob),
title.to_edge, LEFT
)
self.wait()
def get_diagram(self, prior, likelihood, antilikelihood):
diagram = BayesDiagram(
prior, likelihood, antilikelihood,
not_evidence_color1=GREY,
not_evidence_color2=GREEN_E,
)
diagram.set_height(3)
diagram.move_to(5 * LEFT + DOWN)
diagram.add_brace_attrs()
braces = VGroup(diagram.h_brace, diagram.nh_brace)
diagram.add(*braces)
icons = VGroup(LibrarianIcon(), FarmerIcon())
icons[0].set_color(YELLOW_D)
for icon, brace in zip(icons, braces):
icon.set_height(0.5)
icon.brace = brace
icon.add_updater(lambda m: m.next_to(m.brace, DOWN, SMALL_BUFF))
diagram.add(icon)
return diagram
def get_restricted_diagram(self, diagram):
restricted_diagram = diagram.deepcopy()
restricted_diagram.set_x(0)
restricted_diagram.hypothesis_split.set_opacity(0)
restricted_diagram.evidence_split.set_opacity(1)
restricted_diagram.hne_rect.set_opacity(0.1)
restricted_diagram.nhne_rect.set_opacity(0.1)
return restricted_diagram
def get_geometric_fraction(self, diagram):
fraction = VGroup(
diagram.he_rect.copy(),
Line(LEFT, RIGHT),
VGroup(
diagram.he_rect.copy(),
TexMobject("+"),
diagram.nhe_rect.copy(),
).arrange(RIGHT, buff=SMALL_BUFF)
)
fraction.arrange(DOWN)
fraction[1].match_width(fraction)
fraction.to_edge(RIGHT)
fraction.align_to(diagram.square, UP)
return fraction
class WhenDoesBayesApply(DescriptionOfSteve):
def construct(self):
title = TextMobject("When to use Bayes' rule")
title.add(Underline(title, buff=-SMALL_BUFF))
title.scale(1.5)
title.to_edge(UP)
self.add(title)
# Words
all_words = VGroup(
TextMobject("You have a\\\\", "hypothesis"),
TextMobject("You've observed\\\\some ", "evidence"),
TexMobject(
"\\text{You want}\\\\",
"P", "(", "H", "|", "E", ")\\\\",
"P", "\\left(",
"\\substack{""\\text{Hypothesis} \\\\",
"\\textbf{given} \\\\",
"\\, \\text{the evidence} \\,}",
"\\right)",
),
)
for words in all_words:
words.set_color_by_tex_to_color_map({
"hypothesis": HYPOTHESIS_COLOR,
"H": HYPOTHESIS_COLOR,
"evidence": EVIDENCE_COLOR1,
"E": EVIDENCE_COLOR1,
})
goal = all_words[2]
prob = goal[1:7]
big_prob = goal[7:]
for mob in [prob, big_prob]:
mob.match_x(goal[0])
prob.shift(0.2 * DOWN)
big_prob.shift(0.4 * DOWN)
VGroup(big_prob[1], big_prob[-1]).stretch(1.2, 1)
all_words.arrange(RIGHT, buff=2, aligned_edge=UP)
all_words.next_to(title, DOWN, buff=MED_LARGE_BUFF)
big_prob.save_state()
big_prob.move_to(prob, UP)
# Icons
hypothesis_icon = self.get_hypothesis_icon()
evidence_icon = self.get_evidence_icon()
hypothesis_icon.next_to(all_words[0], DOWN, LARGE_BUFF)
evidence_icon.next_to(all_words[1], DOWN, LARGE_BUFF)
# Show icons
self.play(FadeInFromDown(all_words[0]))
self.play(
LaggedStart(
FadeInFrom(hypothesis_icon[0], DOWN),
Write(hypothesis_icon[1]),
FadeInFrom(hypothesis_icon[2], UP),
run_time=1,
)
)
self.wait()
self.play(FadeInFromDown(all_words[1]))
self.play(
FadeIn(evidence_icon),
lag_ratio=0.1,
run_time=2,
)
self.wait()
# More compact probability
self.play(FadeInFromDown(VGroup(goal[0], big_prob)))
self.wait()
self.play(
Restore(big_prob),
*[
TransformFromCopy(big_prob[i], prob[i])
for i in [0, 1, -1]
]
)
self.play(LaggedStart(*[
TransformFromCopy(big_prob[i][j], prob[i])
for i, j in [(2, 0), (3, 1), (4, 3)]
]))
self.wait()
# Highlight "given"
rects = VGroup(*[
SurroundingRectangle(
goal.get_part_by_tex(tex),
buff=0.05,
stroke_width=2,
stroke_color=RED,
)
for tex in ("|", "given")
])
self.play(ShowCreation(rects))
self.play(Transform(rects[0].copy(), rects[1], remover=True))
self.play(FadeOut(rects))
self.wait()
self.remove(prob)
everything = Group(*self.get_mobjects())
self.play(
LaggedStartMap(FadeOut, everything, run_time=2),
prob.copy().to_corner, UR,
)
def get_hypothesis_icon(self):
group = VGroup(
Steve().set_height(1.5),
TexMobject("\\updownarrow"),
LibrarianIcon().set_color(YELLOW_D)
)
group.arrange(DOWN)
return group
def get_evidence_icon(self):
result = self.get_description()
result.scale(0.5)
result.set_color_by_tex("meek", EVIDENCE_COLOR1)
result.set_color_by_tex("soul", EVIDENCE_COLOR1)
rect = SurroundingRectangle(result)
rect.set_stroke(WHITE, 2)
result.add(rect)
return result
class CreateFormulaFromDiagram(Scene):
CONFIG = {
"tex_to_color_map": {
"P": WHITE,
"H": HYPOTHESIS_COLOR,
"E": EVIDENCE_COLOR1,
"\\neg": RED,
},
"bayes_diagram_config": {
"prior": 1 / 21,
"likelihood": 0.4,
"antilikelihood": 0.1,
"not_evidence_color1": GREY,
"not_evidence_color2": DARK_GREY,
"prior_rect_direction": UP,
},
}
def construct(self):
t2c = self.tex_to_color_map
# Add posterior
posterior = TexMobject("P(H|E)", tex_to_color_map=t2c)
posterior.to_corner(UR)
posterior_words = TextMobject("Goal: ")
posterior_words.next_to(posterior, LEFT, aligned_edge=UP)
self.add(posterior)
self.add(posterior_words)
# Show prior
diagram = self.get_diagram()
prior_label = TexMobject("P(H)", tex_to_color_map=t2c)
prior_label.add_updater(
lambda m: m.next_to(diagram.h_brace, UP, SMALL_BUFF)
)
prior_example = TexMobject("= 1 / 21")
prior_example.add_updater(
lambda m: m.next_to(prior_label, RIGHT).shift(0.03 * UP)
)
# example_words = TextMobject("In our example")
# example_words.next_to(prior_example[0][1:], UP, buff=SMALL_BUFF, aligned_edge=LEFT)
# prior_example.add(example_words)
prior_arrow = Vector(0.7 * RIGHT)
prior_arrow.next_to(prior_label, LEFT, SMALL_BUFF)
prior_word = TextMobject("``Prior''")
prior_word.next_to(prior_arrow, LEFT, SMALL_BUFF)
prior_word.align_to(prior_label[0], DOWN)
prior_word.set_color(HYPOTHESIS_COLOR)
self.add(diagram)
self.play(ShowIncreasingSubsets(diagram.people, run_time=2))
self.wait()
self.play(
diagram.hypothesis_split.set_opacity, 1,
FadeIn(diagram.h_brace),
FadeInFromDown(prior_label),
)
self.wait()
self.play(FadeIn(prior_example))
self.play(
LaggedStartMap(
Indicate, diagram.people[::10],
color=BLUE,
)
)
self.wait()
self.play(
FadeInFrom(prior_word, RIGHT),
GrowArrow(prior_arrow)
)
self.wait()
prior_group = VGroup(
prior_word, prior_arrow,
prior_label, prior_example,
diagram.h_brace,
)
# First likelihood split
like_example = TexMobject("= 0.4")
like_example.add_updater(
lambda m: m.next_to(diagram.he_brace, LEFT)
)
like_example.update()
like_label = TexMobject("P(E|H)", tex_to_color_map=t2c)
like_label.add_updater(
lambda m: m.next_to(like_example, LEFT)
)
like_label.update()
like_word = TextMobject("``Likelihood''")
like_word.next_to(like_label, UP, buff=LARGE_BUFF, aligned_edge=LEFT)
like_arrow = Arrow(
like_word.get_bottom(),
like_label.get_top(),
buff=0.2,
)
limit_arrow = Vector(0.5 * UP)
limit_arrow.next_to(like_label.get_part_by_tex("|"), UP, SMALL_BUFF)
limit_arrow.set_stroke(WHITE, 4)
limit_word = TextMobject("Limit\\\\your\\\\view")
limit_word.next_to(limit_arrow, UP)
hne_people = diagram.people[:6]
he_people = diagram.people[6:10]
nhe_people = diagram.people[19::10]
nhne_people = diagram.people[10:]
nhne_people.remove(*nhe_people)
for group in [hne_people, nhne_people]:
group.generate_target()
group.target.set_opacity(0.25)
group.target.set_stroke(BLACK, 0, background=True)
self.play(
diagram.he_rect.set_opacity, 1,
diagram.hne_rect.set_opacity, 1,
MoveToTarget(hne_people),
GrowFromCenter(diagram.he_brace),
FadeInFrom(like_label, RIGHT),
FadeInFrom(like_example, RIGHT),
)
self.wait()
self.play(
ShowCreationThenFadeAround(
like_label.get_part_by_tex("E"),
surrounding_rectangle_config={
"color": EVIDENCE_COLOR1
}
),
)
self.play(WiggleOutThenIn(like_label.get_part_by_tex("|")))
self.play(
ShowCreationThenFadeAround(
like_label.get_part_by_tex("H")
),
)
self.wait()
self.play(
diagram.people[10:].set_opacity, 0.2,
diagram.nh_rect.set_opacity, 0.2,
FadeInFrom(limit_word, DOWN),
GrowArrow(limit_arrow),
rate_func=there_and_back_with_pause,
run_time=6,
)
self.wait()
self.play(
Write(like_word, run_time=1),
GrowArrow(like_arrow),
)
self.wait()
# Show anti-likelihood
anti_label = TexMobject("P(E| \\neg H)", tex_to_color_map=t2c)
anti_label.add_updater(
lambda m: m.next_to(diagram.nhe_brace, RIGHT)
)
anti_example = TexMobject("= 0.1")
anti_example.add_updater(
lambda m: m.next_to(anti_label, RIGHT).align_to(anti_label[0], DOWN)
)
neg_sym = anti_label.get_part_by_tex("\\neg").copy()
neg_sym.generate_target()
neg_sym.target.scale(2.5)
not_word = TextMobject("means ", "``not''")
not_word[1].set_color(RED)
neg_group = VGroup(neg_sym.target, not_word)
neg_group.arrange(RIGHT)
neg_group.next_to(anti_label, UP, LARGE_BUFF)
neg_group.to_edge(RIGHT, buff=MED_SMALL_BUFF)
diagram.nhe_rect.set_opacity(1)
diagram.nhe_rect.save_state()
diagram.nhe_rect.become(diagram.nh_rect)
self.play(
Restore(diagram.nhe_rect),
GrowFromCenter(diagram.nhe_brace),
MoveToTarget(nhne_people),
FadeInFrom(anti_label, LEFT),
FadeInFrom(anti_example, LEFT),
)
diagram.nhne_rect.set_opacity(1)
self.wait()
self.play(
ShowCreationThenFadeAround(
anti_label[2],
surrounding_rectangle_config={"color": EVIDENCE_COLOR1},
)
)
self.play(
ShowCreationThenFadeAround(
anti_label[4:6],
surrounding_rectangle_config={"color": RED},
)
)
self.wait()
self.play(
MoveToTarget(neg_sym),
FadeIn(not_word)
)
self.wait()
self.play(
FadeOut(not_word),
Transform(neg_sym, anti_label.get_part_by_tex("\\neg"))
)
self.remove(neg_sym)
# Recall final answer, geometrically
he_group = VGroup(diagram.he_rect, he_people)
nhe_group = VGroup(diagram.nhe_rect, nhe_people)
denom = VGroup(
he_group.copy(),
TexMobject("+"),
nhe_group.copy(),
)
denom.arrange(RIGHT)
answer = VGroup(
he_group.copy(),
Underline(denom, stroke_width=2),
denom,
)
answer.arrange(DOWN)
answer.scale(0.5)
answer.to_edge(UP, MED_SMALL_BUFF)
answer.shift(LEFT)
equals = TexMobject("=")
posterior.generate_target()
post_group = VGroup(posterior.target, equals, answer)
post_group.arrange(RIGHT)
post_group.to_corner(UL)
post_word = TextMobject("``Posterior''")
post_word.set_color(YELLOW)
post_word.to_corner(UL, buff=MED_SMALL_BUFF)
post_arrow = Arrow(
post_word.get_bottom(),
posterior.target[1].get_top(),
buff=0.2,
)
post_arrow.set_stroke(WHITE, 5)
dividing_line = DashedLine(ORIGIN, FRAME_WIDTH * RIGHT)
dividing_line.set_stroke(WHITE, 1)
dividing_line.next_to(answer, DOWN)
dividing_line.set_x(0)
diagram.add(
diagram.h_brace,
diagram.he_brace,
diagram.nhe_brace,
)
diagram.generate_target(use_deepcopy=True)
diagram.target.scale(0.5, about_edge=DL)
diagram.target.refresh_braces()
like_group = VGroup(like_word, like_arrow)
like_vect = like_group.get_center() - like_label.get_center()
self.play(
ShowCreation(dividing_line),
MoveToTarget(diagram),
MaintainPositionRelativeTo(like_group, like_label),
MaintainPositionRelativeTo(
VGroup(prior_word, prior_arrow),
prior_label,
),
MoveToTarget(posterior),
ReplacementTransform(posterior_words, equals),
)
like_group.move_to(like_label.get_center() + like_vect)
self.wait()
self.play(TransformFromCopy(he_group, answer[0]))
self.play(ShowCreation(answer[1]))
self.play(LaggedStart(
TransformFromCopy(he_group, answer[2][0]),
GrowFromCenter(answer[2][1]),
TransformFromCopy(nhe_group, answer[2][2]),
))
self.wait()
# Write final answer, as a formula
formula = TexMobject(
"=", "{P(H) P(E | H)", "\\over",
"P(H) P(E | H) + P(\\neg H) P(E | \\neg H)}",
tex_to_color_map=t2c
)
formula.scale(0.9)
formula.next_to(answer, RIGHT)
ph = formula[2:6]
peh = formula[6:12]
over = formula.get_part_by_tex("\\over")
ph2 = formula[13:17]
peh2 = formula[17:23]
pnh = formula[23:28]
penh = formula[28:]
parts = VGroup(ph, peh, ph2, peh2, pnh, penh)
parts.save_state()
np1 = TexMobject("(\\# I )")[0]
person = Person()
person.replace(np1[2], dim_to_match=1)
person.scale(1.5)
np1.submobjects[2] = person
np1.match_height(ph)
np1.next_to(ph, LEFT, SMALL_BUFF)
VGroup(np1, ph, peh).match_x(over)
np2 = np1.copy()
np2.next_to(ph2, LEFT, SMALL_BUFF)
VGroup(np2, ph2, peh2).match_width(
VGroup(ph2, peh2), about_edge=RIGHT
)
np3 = np1.copy()
np3.next_to(pnh, LEFT, SMALL_BUFF)
VGroup(np3, pnh, penh).match_width(
VGroup(pnh, penh), about_edge=RIGHT
)
nps = VGroup(np1, np2, np3)
crosses = VGroup(*[Cross(np)[0] for np in nps])
top_brace = Brace(np1, UP, buff=SMALL_BUFF)
top_count = Integer(210)
top_count.add_updater(lambda m: m.next_to(top_brace, UP, SMALL_BUFF))
low_brace = Brace(np3, DOWN, buff=SMALL_BUFF)
low_count = Integer(210)
low_count.add_updater(lambda m: m.next_to(low_brace, DOWN, SMALL_BUFF))
h_rect = Rectangle( # Highlighting rectangle
stroke_color=YELLOW,
stroke_width=3,
fill_color=YELLOW,
fill_opacity=0.25,
)
h_rect.replace(diagram.square, stretch=True)
s_rect = SurroundingRectangle(answer[0])
diagram.refresh_braces()
nh_group = VGroup(
diagram.nh_brace,
TexMobject("P(\\neg H)", tex_to_color_map=t2c),
TexMobject("= 20 / 21"),
)
nh_group[1].next_to(nh_group[0], UP, SMALL_BUFF)
nh_group[2].next_to(nh_group[1], RIGHT, SMALL_BUFF)
self.play(
Write(formula.get_part_by_tex("=")),
Write(formula.get_part_by_tex("\\over")),
run_time=1
)
self.play(ShowCreation(s_rect))
self.wait()
self.play(
FadeIn(np1),
FadeIn(top_brace),
FadeIn(top_count),
)
self.wait()
self.play(h_rect.replace, diagram.h_rect, {"stretch": True})
self.play(
TransformFromCopy(prior_label, ph),
top_brace.become, Brace(VGroup(np1, ph), UP, buff=SMALL_BUFF),
ChangeDecimalToValue(top_count, 10),
)
self.wait()
self.play(h_rect.replace, diagram.he_rect, {"stretch": True})
self.play(
TransformFromCopy(like_label, peh),
top_brace.become, Brace(VGroup(np1, peh), UP, buff=SMALL_BUFF),
ChangeDecimalToValue(top_count, 4)
)
self.wait()
self.play(
s_rect.move_to, answer[2][0],
TransformFromCopy(np1, np2),
TransformFromCopy(ph, ph2),
TransformFromCopy(peh, peh2),
)
self.wait()
self.play(
FadeOut(h_rect),
s_rect.become, SurroundingRectangle(answer[2][2]),
FadeOut(prior_group),
FadeIn(nh_group),
)
self.wait()
h_rect.replace(diagram.square, stretch=True)
self.play(
FadeIn(np3),
FadeIn(low_brace),
FadeIn(low_count),
)
self.play(h_rect.replace, diagram.nh_rect, {"stretch": True})
self.play(
TransformFromCopy(nh_group[1], pnh),
low_brace.become, Brace(VGroup(np3, pnh), DOWN, buff=SMALL_BUFF),
ChangeDecimalToValue(low_count, 200),
)
self.play(h_rect.replace, diagram.nhe_rect, {"stretch": True})
self.play(
TransformFromCopy(anti_label, penh),
low_brace.become, Brace(VGroup(np3, penh), DOWN, buff=SMALL_BUFF),
ChangeDecimalToValue(low_count, 20),
)
self.wait()
# Clean up
self.play(
FadeOut(nh_group),
FadeOut(s_rect),
FadeOut(h_rect),
FadeIn(prior_group),
)
self.wait()
self.play(
ShowCreation(crosses),
FadeOut(low_brace),
FadeOut(top_brace),
FadeOut(low_count),
FadeOut(top_count),
)
self.wait()
self.play(
Restore(parts),
FadeOut(crosses),
FadeOut(nps),
answer.set_opacity, 0.2
)
self.wait()
# Write Bayes' theorem
formula_rect = SurroundingRectangle(formula[1:])
formula_rect.set_stroke(TEAL, 2)
bayes_words = TextMobject("Bayes' theorem")
bayes_words.scale(1.5)
bayes_words.next_to(formula_rect, UP, SMALL_BUFF)
bayes_words.match_color(formula_rect)
self.play(ShowCreation(formula_rect))
self.play(FadeInFromDown(bayes_words))
self.wait()
# Simplify denominator
simpler_form = get_bayes_formula()[7:]
simpler_form.move_to(answer)
pe = simpler_form[-4:].copy()
pe.save_state()
big_denom_rect = SurroundingRectangle(VGroup(ph2, penh))
lil_denom_rect = SurroundingRectangle(pe)
for rect in big_denom_rect, lil_denom_rect:
rect.set_stroke(BLUE, 0)
rect.set_fill(BLUE, 0.25)
pe.move_to(big_denom_rect)
pe.set_opacity(0)
self.play(
FadeOut(answer),
FadeIn(simpler_form[:-4])
)
self.play(TransformFromCopy(formula_rect, big_denom_rect))
self.wait()
self.play(
Restore(pe),
ReplacementTransform(big_denom_rect, lil_denom_rect),
Transform(
formula_rect,
SurroundingRectangle(simpler_form, color=TEAL),
),
bayes_words.match_x, simpler_form,
)
self.remove(pe)
self.add(simpler_form)
self.wait()
# Show all evidence cases
he_group_copy = he_group.copy()
nhe_group_copy = nhe_group.copy()
copies = VGroup(he_group_copy, nhe_group_copy)
self.play(
copies.arrange, RIGHT, {"buff": LARGE_BUFF},
copies.move_to, DOWN,
copies.to_edge, RIGHT, LARGE_BUFF,
)
self.wait()
self.play(
he_group_copy.next_to, VGroup(ph2, peh2), DOWN,
)
self.play(
nhe_group_copy.next_to, VGroup(pnh, penh), DOWN,
)
self.wait()
self.play(
FadeOut(copies),
FadeOut(lil_denom_rect),
)
self.wait()
# Name posterior
self.play(
GrowArrow(post_arrow),
FadeInFrom(post_word, RIGHT),
FadeOut(formula_rect),
FadeOut(bayes_words),
)
self.wait()
# Show confusion
randy = Randolph()
randy.flip()
randy.next_to(dividing_line, DOWN)
randy.to_edge(RIGHT)
prior_rect = SurroundingRectangle(prior_word)
post_rect = SurroundingRectangle(post_word)
VGroup(prior_rect, post_rect).set_stroke(WHITE, 2)
self.play(FadeIn(randy))
self.play(randy.change, "confused", formula)
self.play(Blink(randy))
self.play(randy.change, "horrified", formula)
self.play(randy.look_at, diagram)
self.play(Blink(randy))
self.play(randy.look_at, formula)
self.play(randy.change, "tired")
self.wait(2)
self.play(
randy.change, "pondering", prior_label,
ShowCreation(prior_rect)
)
self.play(Blink(randy))
self.play(
ReplacementTransform(prior_rect, post_rect),
randy.look_at, post_rect,
)
self.play(randy.change, "thinking")
self.play(FadeOut(post_rect))
self.play(Blink(randy))
self.wait()
self.play(randy.look_at, formula)
self.play(Blink(randy))
self.wait()
# Transition to next scene
return # Skip
to_move = VGroup(posterior, formula)
self.remove(to_move, *to_move, *to_move[1])
to_move.generate_target()
to_move.target[1].scale(1 / 0.9)
to_move.target.arrange(RIGHT)
to_move.target.to_corner(UL)
everything = Group(*self.get_mobjects())
self.play(
LaggedStartMap(FadeOutAndShiftDown, everything),
MoveToTarget(to_move, rate_func=squish_rate_func(smooth, 0.5, 1)),
run_time=2,
)
def get_diagram(self, include_people=True):
diagram = BayesDiagram(**self.bayes_diagram_config)
diagram.set_height(5.5)
diagram.set_width(5.5, stretch=True)
diagram.move_to(0.5 * DOWN)
diagram.add_brace_attrs()
diagram.evidence_split.set_opacity(0)
diagram.hypothesis_split.set_opacity(0)
diagram.nh_rect.set_fill(DARK_GREY)
if include_people:
people = VGroup(*[Person() for x in range(210)])
people.set_color(interpolate_color(LIGHT_GREY, WHITE, 0.5))
people.arrange_in_grid(n_cols=21)
people.set_width(diagram.get_width() - SMALL_BUFF)
people.set_height(diagram.get_height() - SMALL_BUFF, stretch=True)
people.move_to(diagram)
people[10:].set_color(GREEN_D)
people.set_stroke(BLACK, 2, background=True)
diagram.add(people)
diagram.people = people
return diagram
class DiscussFormulaAndAreaModel(CreateFormulaFromDiagram):
CONFIG = {
"bayes_diagram_config": {
"prior_rect_direction": DOWN,
},
}
def construct(self):
# Show smaller denominator
t2c = self.tex_to_color_map
formula = TexMobject(
"P(H | E)", "=",
"{P(H) P(E | H)", "\\over",
"P(H) P(E | H) + P(\\neg H) P(E | \\neg H)}",
tex_to_color_map=t2c
)
equals = formula.get_part_by_tex("=")
equals_index = formula.index_of_part(equals)
lhs = formula[:equals_index]
rhs = formula[equals_index + 1:]
lhs.next_to(equals, LEFT)
formula.to_corner(UL)
alt_rhs = TexMobject(
"{P(H)P(E|H)", "\\over", "P(E)}",
"=",
tex_to_color_map=t2c,
)
alt_rhs.next_to(equals, RIGHT, SMALL_BUFF)
s_rect = SurroundingRectangle(rhs[12:], color=BLUE)
self.add(formula)
self.wait()
self.play(ShowCreation(s_rect))
self.add(alt_rhs, s_rect)
self.play(
VGroup(rhs, s_rect).next_to, alt_rhs, RIGHT, SMALL_BUFF,
GrowFromCenter(alt_rhs),
)
self.play(
s_rect.become,
SurroundingRectangle(alt_rhs[12:-1], color=BLUE)
)
self.wait()
# Bring in diagram
diagram = self.get_diagram(include_people=False)
diagram.evidence_split.set_opacity(1)
diagram.set_height(3)
diagram.set_prior(0.1)
diagram.refresh_braces()
diagram.add(
diagram.h_brace,
diagram.he_brace,
diagram.nhe_brace,
)
h_label, he_label, nhe_label = labels = [
TexMobject(tex, tex_to_color_map=t2c)
for tex in ["P(H)", "P(E|H)", "P(E|\\neg H)"]
]
h_label.add_updater(lambda m: m.next_to(diagram.h_brace, DOWN))
he_label.add_updater(lambda m: m.next_to(diagram.he_brace, LEFT))
nhe_label.add_updater(lambda m: m.next_to(diagram.nhe_brace, RIGHT))
diagram.add(*labels)
diagram.to_corner(DL)
he_rect_copy = diagram.he_rect.copy()
nhe_rect_copy = diagram.nhe_rect.copy()
self.play(
FadeIn(diagram),
# FadeOut(s_rect),
ReplacementTransform(
VGroup(s_rect, s_rect.copy()),
VGroup(he_rect_copy, nhe_rect_copy),
),
)
self.wait()
self.play(LaggedStart(
ApplyMethod(he_rect_copy.next_to, rhs[12:22], DOWN),
ApplyMethod(nhe_rect_copy.next_to, rhs[23:], DOWN),
lag_ratio=0.7,
run_time=1.5
))
self.wait()
self.play(FadeOut(VGroup(he_rect_copy, nhe_rect_copy)))
# Tell what to memorize
big_rect = SurroundingRectangle(formula)
big_rect.set_stroke(WHITE, 2)
big_rect.set_fill(BLACK, 0)
words1 = TextMobject("Don't memorize\\\\this")
words2 = TextMobject("Remember\\\\this")
for words in words1, words2:
words.scale(1.5)
words.to_edge(RIGHT)
arrow1 = Arrow(words1.get_corner(UL), big_rect.get_bottom())
arrow2 = Arrow(words2.get_left(), diagram.square.get_right())
self.play(
FadeIn(words1),
ShowCreation(arrow1),
ShowCreation(big_rect)
)
self.wait()
self.play(
ReplacementTransform(arrow1, arrow2),
FadeOut(words1),
FadeIn(words2),
big_rect.set_stroke, WHITE, 0,
big_rect.set_fill, BLACK, 0.7,
)
self.wait()
# Talk about diagram slices
to_fade = VGroup(
diagram.evidence_split,
diagram.he_brace,
diagram.nhe_brace,
he_label, nhe_label,
)
to_fade.save_state()
h_part = VGroup(
diagram.hypothesis_split,
diagram.h_brace,
h_label,
)
people = self.get_diagram().people
people.set_width(diagram.square.get_width() - 0.05)
people.move_to(diagram.square)
sides = VGroup(
DashedLine(
diagram.square.get_corner(UL),
diagram.square.get_corner(UR),
),
DashedLine(
diagram.square.get_corner(UL),
diagram.square.get_corner(DL),
),
)
sides.set_stroke(YELLOW, 4)
ones = VGroup(
TexMobject("1").next_to(diagram.square, UP),
TexMobject("1").next_to(diagram.square, LEFT),
)
self.play(
to_fade.set_opacity, 0,
h_part.set_opacity, 0,
diagram.square.set_opacity, 1,
ShowIncreasingSubsets(people),
)
self.play(FadeOut(people))
self.play(
LaggedStartMap(ShowCreation, sides, lag_ratio=0.8),
LaggedStartMap(FadeIn, ones, lag_ratio=0.8),
)
self.wait()
self.play(
h_part.set_opacity, 1,
)
diagram.square.set_opacity(0)
self.wait()
self.play(
FadeOut(sides),
FadeOut(ones),
)
self.wait()
VGroup(
to_fade[1:],
to_fade[0][::2],
).stretch(0, 1, about_edge=DOWN)
self.play(
Restore(to_fade),
diagram.hypothesis_split.set_opacity, 0,
diagram.hne_rect.set_opacity, 0.2,
diagram.nhne_rect.set_opacity, 0.2,
)
self.wait()
# Add posterior bar
post_bar = always_redraw(
lambda: self.get_posterior_bar(
diagram.he_rect,
diagram.nhe_rect,
)
)
post_label = TexMobject("P(H|E)", tex_to_color_map=t2c)
post_label.add_updater(lambda m: m.next_to(post_bar.brace, DOWN))
self.play(
FadeOut(words2),
FadeOut(arrow2),
TransformFromCopy(
diagram.he_rect, post_bar.rects[0],
),
TransformFromCopy(
diagram.nhe_rect, post_bar.rects[1],
),
FadeIn(post_bar.brace),
FadeIn(post_label),
)
self.add(post_bar)
self.wait()
self.play(
diagram.set_likelihood, 0.8,
rate_func=there_and_back,
run_time=4,
)
self.wait()
self.play(diagram.set_antilikelihood, 0.4)
self.wait()
self.play(
diagram.set_likelihood, 0.8,
diagram.set_antilikelihood, 0.05,
)
self.wait()
self.play(
diagram.set_likelihood, 0.4,
diagram.set_antilikelihood, 0.1,
)
self.wait()
self.play(
big_rect.set_width, rhs.get_width() + 0.7,
{"about_edge": RIGHT, "stretch": True},
)
self.wait()
# Terms from formula to sides in diagram
hs_rect = SurroundingRectangle(alt_rhs[:5], buff=0.05)
hes_rect = SurroundingRectangle(alt_rhs[5:11], buff=0.05)
hs_rect.set_stroke(YELLOW, 3)
hes_rect.set_stroke(BLUE, 3)
self.play(ShowCreation(hs_rect))
self.play(ShowCreation(hes_rect))
self.wait()
self.play(hs_rect.move_to, h_label)
self.play(hes_rect.move_to, he_label)
self.wait()
self.play(FadeOut(VGroup(hs_rect, hes_rect)))
def get_posterior_bar(self, he_rect, nhe_rect):
he_height = he_rect.get_height()
he_width = he_rect.get_width()
nhe_height = nhe_rect.get_height()
nhe_width = nhe_rect.get_width()
total_width = he_width + nhe_width
he_area = he_width * he_height
nhe_area = nhe_width * nhe_height
posterior = he_area / (he_area + nhe_area)
new_he_width = posterior * total_width
new_he_height = he_area / new_he_width
new_nhe_width = (1 - posterior) * total_width
new_nhe_height = nhe_area / new_nhe_width
new_he_rect = Rectangle(
width=new_he_width,
height=new_he_height,
).match_style(he_rect)
new_nhe_rect = Rectangle(
width=new_nhe_width,
height=new_nhe_height,
).match_style(nhe_rect)
rects = VGroup(new_he_rect, new_nhe_rect)
rects.arrange(RIGHT, buff=0)
brace = Brace(
new_he_rect, DOWN,
buff=SMALL_BUFF,
min_num_quads=2,
)
result = VGroup(rects, brace)
result.rects = rects
result.brace = brace
# Put positioning elsewhere?
result.to_edge(RIGHT)
return result
class RandomShapes(Scene):
def construct(self):
diagram = BayesDiagram(0.1, 0.4, 0.1)
diagram.set_height(3)
e_part = VGroup(diagram.he_rect, diagram.nhe_rect).copy()
e_part.set_fill(BLUE)
circle = Circle()
circle.set_fill(RED, 0.5)
circle.set_stroke(RED, 2)
circle.move_to(diagram)
tri = Polygon(UP, ORIGIN, RIGHT)
tri.match_height(diagram)
tri.set_fill(PURPLE, 0.5)
tri.set_stroke(PURPLE, 2)
tri.move_to(diagram)
h_rect = diagram.h_rect
h_rect.set_fill(YELLOW, 1)
pi = TexMobject("\\pi")
pi.set_height(2)
pi.set_stroke(GREEN, 2)
pi.set_fill(GREEN, 0.5)
events = VGroup(
e_part,
h_rect,
circle,
pi,
tri,
)
last = VMobject()
for event in events:
self.play(
FadeIn(event),
FadeOut(last)
)
self.wait()
last = event
class BigArrow(Scene):
def construct(self):
arrow = Arrow(
3 * DOWN + 4 * LEFT,
3 * RIGHT + DOWN,
path_arc=50 * DEGREES,
)
self.play(ShowCreation(arrow))
self.wait()
class UsesOfBayesTheorem(Scene):
def construct(self):
formula = get_bayes_formula()
formula.to_corner(UL)
rhs = formula[7:]
bubble = ThoughtBubble(direction=RIGHT)
bubble.set_fill(opacity=0)
arrow = Vector(RIGHT)
arrow.next_to(formula, RIGHT, MED_LARGE_BUFF)
bubble.set_height(2)
bubble.next_to(arrow, RIGHT, MED_LARGE_BUFF)
bubble.align_to(formula, UP)
bar = ProbabilityBar(
0.1,
percentage_background_stroke_width=1,
include_braces=False,
)
bar.set_width(0.8 * bubble[-1].get_width())
bar.move_to(bubble.get_bubble_center())
scientist = SVGMobject(file_name="scientist")
programmer = SVGMobject(file_name="programmer")
randy = Randolph().flip()
people = VGroup(scientist, programmer, randy)
people.set_stroke(width=0)
for person in people:
person.set_height(2.5)
if person is not randy:
person.set_fill(GREY)
person.set_sheen(0.5, UL)
people.arrange(RIGHT, buff=2.5)
# programmer.shift(0.5 * RIGHT)
people.to_edge(DOWN, buff=LARGE_BUFF)
self.add(formula)
self.wait()
self.play(GrowArrow(arrow))
self.play(ShowCreation(bubble), FadeIn(bar))
self.play(bar.p_tracker.set_value, 0.8)
self.wait()
# Add people
for person in [scientist, programmer]:
self.play(FadeInFrom(person, DOWN))
rhs_copy = rhs.copy()
rhs_copy.add_to_back(
SurroundingRectangle(
rhs_copy,
fill_color=BLACK,
fill_opacity=0.8,
stroke_color=WHITE,
stroke_width=2,
)
)
rhs_copy.generate_target()
rhs_copy[0].set_stroke(width=0)
rhs_copy.target.scale(0.5)
rhs_copy.target.move_to(person.get_corner(DR))
self.add(rhs_copy, formula)
self.play(MoveToTarget(rhs_copy))
self.wait()
person.add(rhs_copy)
self.play(FadeInFromDown(randy))
self.play(randy.change, "pondering")
self.play(Blink(randy))
bubble_group = VGroup(bubble, bar)
self.play(
bubble_group.scale, 1.4,
bubble_group.move_to, randy.get_corner(UL), DR,
randy.look_at, bubble,
FadeOut(arrow),
)
self.wait()
self.play(Blink(randy))
self.play(bar.p_tracker.set_value, 0.3, run_time=3)
self.play(randy.change, "thinking")
self.play(Blink(randy))
self.wait()
self.play(LaggedStartMap(
FadeOutAndShift,
VGroup(*people, bubble_group),
lambda m: (m, DOWN),
lag_ratio=0.15,
))
class AskAboutWhenProbabilityIsIntuitive(TeacherStudentsScene):
def construct(self):
words = TextMobject("What makes probability\\\\more intuitive?")
words.move_to(self.hold_up_spot, DOWN)
words.shift_onto_screen()
self.play(
self.teacher.change, "speaking",
self.get_student_changes(
"pondering", "sassy", "happy",
look_at_arg=self.screen,
)
)
self.wait(2)
self.play(
self.teacher.change, "raise_right_hand",
FadeInFrom(words, DOWN),
self.get_student_changes("erm", "pondering", "confused")
)
self.wait(2)
self.play(
words.scale, 2,
words.center,
words.to_edge, UP,
self.teacher.change, "pondering", 3 * UP,
self.get_student_changes(
"pondering", "thinking", "thinking",
look_at_arg=3 * UP,
)
)
self.wait(6)
class IntroduceLinda(DescriptionOfSteve):
def construct(self):
# Kahneman and Tversky
images = self.get_images()
self.play(
LaggedStartMap(
FadeInFrom, images,
lambda m: (m, LEFT),
lag_ratio=0.3,
run_time=3,
)
)
self.wait()
# Add steve
steve = Steve()
steve.set_height(3)
steve.move_to(2 * RIGHT)
steve.to_edge(DOWN, buff=LARGE_BUFF)
steve_words = self.get_description()
steve_words.scale(0.8)
steve_words.next_to(steve, UP, LARGE_BUFF)
self.play(LaggedStart(
FadeInFrom(steve, LEFT),
FadeInFrom(steve_words, LEFT),
))
self.wait()
# Replace with Linda
linda = Linda()
linda_words = self.get_linda_description()
linda_words.scale(0.8)
linda.replace(steve)
linda_words.move_to(steve_words)
self.play(
LaggedStart(
FadeOutAndShift(steve_words, 2 * RIGHT),
FadeOutAndShift(steve, 2 * RIGHT),
FadeInFrom(linda, 2 * LEFT),
lag_ratio=0.15,
)
)
self.wait()
self.play(
FadeIn(linda_words),
lag_ratio=0.1,
run_time=6,
rate_func=linear,
)
self.wait()
# Ask question
options = VGroup(
TextMobject("1) Linda is a bank teller."),
TextMobject(
"2) Linda is a bank teller and is active\\\\",
"\\phantom{2)} in the feminist movement.",
alignment="",
),
)
options.arrange(DOWN, buff=LARGE_BUFF, aligned_edge=LEFT)
options.to_edge(DOWN, buff=LARGE_BUFF)
self.play(
linda.match_height, linda_words,
linda.next_to, linda_words, LEFT, LARGE_BUFF,
LaggedStartMap(
FadeOutAndShift, images,
lambda m: (m, 2 * LEFT),
)
)
for option in options:
self.play(FadeIn(option, lag_ratio=0.1, run_time=2))
self.wait()
# Show result
rect = SurroundingRectangle(options[1], color=RED)
options.generate_target()
rect.generate_target()
VGroup(options.target, rect.target).to_edge(LEFT)
result = VGroup(
Integer(85, unit="\\%"),
TextMobject("chose this!")
)
result.arrange(RIGHT)
result.scale(1.25)
result.set_color(RED)
result.move_to(rect)
result.to_edge(RIGHT)
result.shift(2 * UP)
arrow = Arrow(result.get_bottom(), rect.target.get_corner(UR))
self.play(
MoveToTarget(options),
MoveToTarget(rect),
VFadeIn(rect),
FadeInFrom(result, LEFT),
GrowArrow(arrow)
)
self.wait()
# Show subsets
fb_words = TextMobject("Active feminist\\\\bank tellers")
fb_words.scale(0.5)
fb_words.set_stroke(BLACK, 0, background=True)
fb_set = Circle()
fb_set.flip(RIGHT)
fb_set.rotate(3 * TAU / 8)
fb_set.set_stroke(WHITE, 1)
fb_set.set_fill(YELLOW, 0.5)
fb_set.replace(fb_words, stretch=True)
fb_set.scale(1.2)
fb_set.stretch(1.4, 1)
fb_group = VGroup(fb_set, fb_words)
fb_group.move_to(linda_words, RIGHT)
b_set = fb_set.copy()
b_set.set_fill(BLUE)
b_set.scale(3, about_edge=RIGHT)
b_set.stretch(1.5, 1)
b_set.to_corner(UR)
b_words = TextMobject("Bank\\\\tellers")
b_words.next_to(b_set.get_left(), RIGHT, LARGE_BUFF)
self.play(
FadeOut(linda_words),
TransformFromCopy(rect, fb_set),
ReplacementTransform(
VectorizedPoint(rect.get_center()),
fb_words,
),
)
self.add(fb_set, fb_words)
self.wait()
self.add(b_set, fb_set, fb_words)
self.play(
DrawBorderThenFill(b_set),
TransformFromCopy(
options[0][0][10:], b_words[0]
),
)
sets_group = VGroup(b_set, b_words, fb_set, fb_words)
self.add(sets_group)
self.wait()
# Reduce 85
number = result[0]
self.play(
LaggedStart(
FadeOut(arrow),
FadeOut(result[1]),
FadeOut(rect),
FadeOut(options[0]),
FadeOut(options[1]),
),
number.scale, 1.5,
number.move_to, DOWN,
)
self.play(
ChangeDecimalToValue(number, 0),
UpdateFromAlphaFunc(
number,
lambda m, a: m.set_color(interpolate_color(
RED, GREEN, a,
))
),
run_time=2,
)
self.wait()
self.play(
FadeOut(number),
FadeOut(sets_group),
FadeIn(linda_words),
)
# New options
words = TextMobject("100 people fit this description. How many are:")
words.set_color(BLUE_B)
kw = {"tex_to_color_map": {"\\underline{\\qquad}": WHITE}}
new_options = VGroup(
TextMobject("1) Bank tellers? \\underline{\\qquad} of 100", **kw),
TextMobject(
"2) Bank tellers and active in the",
" feminist movement? \\underline{\\qquad} of 100",
**kw
),
)
new_options.scale(0.9)
new_options.arrange(DOWN, aligned_edge=LEFT, buff=MED_LARGE_BUFF)
new_options.to_edge(DOWN, buff=LARGE_BUFF)
words.next_to(new_options, UP, LARGE_BUFF)
self.play(
FadeIn(words, lag_ratio=0.1, rate_func=linear)
)
self.wait()
for option in new_options:
self.play(FadeIn(option))
self.wait()
example_numbers = VGroup(Integer(8), Integer(5))
for exn, option in zip(example_numbers, new_options):
line = option.get_part_by_tex("underline")
exn.scale(1.1)
exn.next_to(line, UP, buff=0.05)
exn.set_color(YELLOW)
self.play(Write(exn))
self.wait()
def get_images(self):
images = Group(
ImageMobject("kahneman"),
ImageMobject("tversky"),
)
images.set_height(3.5)
images.arrange(DOWN, buff=0.5)
images.to_edge(LEFT, buff=MED_LARGE_BUFF)
names = VGroup(
TextMobject("Kahneman", alignment=""),
TextMobject("Tversky", alignment=""),
)
for name, image in zip(names, images):
name.next_to(image, DOWN)
image.name = name
image.add(name)
images.arrange(DOWN, buff=1, aligned_edge=LEFT)
images.set_height(FRAME_HEIGHT - 1)
images.to_edge(LEFT)
return images
def get_linda_description(self):
result = TextMobject(
"Linda is 31 years old, single, outspoken, and\\\\",
"very bright. She majored in philosophy. As a \\\\",
"student, she was deeply concerned with issues\\\\",
"of discrimination and social justice, and also\\\\",
"participated in anti-nuclear demonstrations.\\\\",
alignment="",
tex_to_color_map={
"deeply concerned with issues": YELLOW,
"of discrimination": YELLOW,
}
)
return result
class LindaDescription(IntroduceLinda):
def construct(self):
words = self.get_linda_description()
words.set_color(WHITE)
highlighted_part = VGroup(
*words.get_part_by_tex("deeply"),
*words.get_part_by_tex("discrimination"),
)
self.add(words)
self.play(
FadeIn(words),
run_time=3,
lag_ratio=0.01,
rate_func=linear,
)
self.wait(1)
self.play(
highlighted_part.set_color, YELLOW,
lag_ratio=0.1,
run_time=2
)
self.wait()
class AlternatePhrasings(PiCreatureScene):
CONFIG = {
"camera_config": {
"background_color": DARKER_GREY,
}
}
def construct(self):
randy = self.pi_creature
phrases = VGroup(
TextMobject("40 out of 100"),
TexMobject("40\\%"),
TexMobject("0.4"),
TextMobject("What's more likely$\\dots$"),
)
for phrase in phrases:
phrase.scale(1.5)
phrase.next_to(randy, RIGHT, buff=LARGE_BUFF)
phrase.align_to(randy, UP)
def push_down(phrase):
phrase.scale(0.8, about_edge=LEFT)
phrase.shift(1 * DOWN)
phrase.set_opacity(0.5)
return phrase
bubble = randy.get_bubble()
content_width = 4.5
people = VGroup(*[Person() for x in range(100)])
people.arrange_in_grid(n_cols=20)
people.set_width(content_width)
people.move_to(bubble.get_bubble_center())
people.shift(SMALL_BUFF * UP)
people[:40].set_color(YELLOW)
bar = ProbabilityBar(0.9999)
bar.set_width(content_width)
bar.move_to(people)
steve = Steve()
steve.set_height(1)
steve_words = TextMobject("seems bookish...")
steve_words.next_to(steve, RIGHT, MED_LARGE_BUFF)
steve.add(steve_words)
linda = Linda()
linda.set_height(1)
linda_words = TextMobject("seems activist...")
linda_words.next_to(linda, RIGHT, MED_LARGE_BUFF)
linda.add(linda_words)
stereotypes = VGroup(steve, linda)
stereotypes.arrange(DOWN, buff=MED_SMALL_BUFF, aligned_edge=LEFT)
stereotypes.move_to(people)
self.play(
FadeInFrom(phrases[0], UP),
randy.change, "pondering",
)
self.play(
DrawBorderThenFill(bubble, lag_ratio=0.1),
FadeIn(people, lag_ratio=0.1),
randy.change, "thinking", people,
)
self.wait()
self.play(
FadeInFrom(phrases[1], UP),
randy.change, "confused", phrases[1],
FadeOut(people),
ApplyFunction(push_down, phrases[0]),
FadeIn(bar),
)
self.play(bar.p_tracker.set_value, 0.4)
bar.clear_updaters()
self.play(
FadeInFrom(phrases[2], UP),
ApplyFunction(push_down, phrases[:2]),
FadeOut(bar.percentages),
randy.change, "guilty",
)
self.wait()
bar.remove(bar.percentages)
self.play(
FadeInFrom(phrases[3], UP),
ApplyFunction(push_down, phrases[:3]),
FadeOut(bar),
FadeIn(stereotypes),
randy.change, "shruggie", stereotypes,
)
self.wait(6)
class WhenDiscreteChunksArentSoClean(Scene):
def construct(self):
squares = VGroup(*[Square() for x in range(100)])
squares.arrange_in_grid(n_cols=10, buff=0)
squares.set_stroke(WHITE, 1)
squares.set_fill(DARKER_GREY, 1)
squares.set_height(6)
squares.to_edge(DOWN)
target_p = 0.3612
rain, sun = icon_templates = VGroup(
SVGMobject("rain_cloud"),
SVGMobject("sunny"),
)
for icon in icon_templates:
icon.set_width(0.6 * squares[0].get_width())
icon.set_stroke(width=0)
icon.set_sheen(0.5, UL)
rain.set_color(BLUE_E)
sun.set_color(YELLOW)
partial_rects = VGroup()
icons = VGroup()
q_marks = VGroup()
for i, square in enumerate(squares):
icon = rain.copy() if i < 40 else sun.copy()
icon.move_to(square)
icons.add(icon)
partial_rect = square.copy()
partial_rect.set_stroke(width=0)
partial_rect.scale(0.95)
partial_rect.stretch(
0.4,
0,
about_edge=RIGHT
)
partial_rects.add(partial_rect)
q_mark = TexMobject("?")
q_mark.replace(partial_rect, dim_to_match=0)
q_mark.scale(0.8)
q_marks.add(q_mark)
p_label = VGroup(
TexMobject("P", "(", "\\text{Rain}", ")", "="),
DecimalNumber(40, unit="\\%", num_decimal_places=2)
)
percentage = p_label[1]
p_label.arrange(RIGHT)
p_label.to_edge(UP)
p_label[0].set_color_by_tex("Rain", BLUE)
percentage.align_to(p_label[0][0], DOWN)
alt_percentage = Integer(0, unit="\\%")
alt_percentage.move_to(percentage, LEFT)
self.add(squares)
self.add(p_label[0])
self.play(
ChangeDecimalToValue(alt_percentage, 40),
ShowIncreasingSubsets(icons[:40])
)
self.play(FadeIn(icons[40:]))
self.wait()
self.remove(alt_percentage)
self.add(percentage)
self.play(
ChangeDecimalToValue(percentage, 100 * target_p),
FadeIn(partial_rects[30:40]),
FadeIn(q_marks[30:40], lag_ratio=0.3)
)
self.wait(2)
l_rect = Rectangle(fill_color=BLUE_D)
r_rect = Rectangle(fill_color=LIGHT_GREY)
rects = VGroup(l_rect, r_rect)
for rect, p in (l_rect, target_p), (r_rect, 1 - target_p):
rect.set_height(squares.get_height())
rect.set_width(p * squares.get_width(), stretch=True)
rect.set_stroke(WHITE, 2)
rect.set_fill(opacity=1)
rects.arrange(RIGHT, buff=0)
rects.move_to(squares)
brace = Brace(l_rect, UP, buff=SMALL_BUFF)
sun = icons[40].copy()
rain = icons[0].copy()
for mob, rect in [(rain, l_rect), (sun, r_rect)]:
mob.generate_target()
mob.target.set_stroke(BLACK, 3, background=True)
mob.target.set_height(1)
mob.target.move_to(rect)
self.play(
FadeIn(rects),
MoveToTarget(rain),
MoveToTarget(sun),
GrowFromCenter(brace),
p_label.shift,
brace.get_top() + MED_SMALL_BUFF * UP -
percentage.get_bottom(),
)
self.wait()
# With updaters
full_width = rects.get_width()
rain.add_updater(lambda m: m.move_to(l_rect))
sun.add_updater(lambda m: m.move_to(r_rect))
r_rect.add_updater(lambda m: m.set_width(
full_width - l_rect.get_width(),
about_edge=RIGHT,
stretch=True,
))
brace.add_updater(lambda m: m.match_width(l_rect, stretch=True))
brace.add_updater(lambda m: m.next_to(l_rect, UP, SMALL_BUFF))
percentage.add_updater(lambda m: m.set_value(
100 * l_rect.get_width() / full_width,
))
percentage.add_updater(lambda m: m.next_to(brace, UP, MED_SMALL_BUFF))
self.play(
MaintainPositionRelativeTo(p_label[0], percentage),
l_rect.stretch, 2, 0, {"about_edge": LEFT},
run_time=8,
rate_func=there_and_back,
)
class RandomnessVsProportions(Scene):
def construct(self):
prob_word = TextMobject("Probability")
unc_word = TextMobject("Uncertainty")
prop_word = TextMobject("Proportions")
words = VGroup(prop_word, prob_word, unc_word)
words.arrange(RIGHT, buff=LARGE_BUFF)
words.set_width(FRAME_WIDTH - 1)
words.to_edge(UP)
arrows = VGroup()
for w1, w2 in zip(words, words[1:]):
arrow = TexMobject("\\rightarrow")
arrow.move_to(midpoint(w1.get_right(), w2.get_left()))
arrows.add(arrow)
random_dice = self.get_random_dice()
random_dice.next_to(unc_word, DOWN, LARGE_BUFF)
diagram = self.get_dice_diagram()
diagram.next_to(prop_word, DOWN, LARGE_BUFF)
diagram.shift_onto_screen()
grid = diagram[0]
border = grid[0][0].copy()
border.set_stroke(BLACK, 3)
border.set_fill(WHITE, opacity=0.2)
border.scale(1.02)
def update_border(border):
r1, r2 = random_dice
i = len(r1[1]) - 1
j = len(r2[1]) - 1
border.move_to(diagram[0][i][j])
border.add_updater(update_border)
example = VGroup(
TextMobject("P(X = 5)", tex_to_color_map={"5": YELLOW}),
Line(LEFT, RIGHT)
)
example.arrange(RIGHT)
example.next_to(grid, RIGHT, LARGE_BUFF)
example.align_to(random_dice, RIGHT)
example.shift(0.5 * DOWN)
grid_copy = grid.copy()
five_part = VGroup(*[
square
for i, row in enumerate(grid_copy)
for j, square in enumerate(row)
if i + j == 3
])
self.play(FadeInFromDown(prob_word))
self.play(
FadeInFrom(unc_word, LEFT),
Write(arrows[1]),
)
self.add(random_dice)
self.wait(9)
self.play(
FadeInFrom(prop_word, RIGHT),
Write(arrows[0])
)
self.play(FadeIn(diagram))
self.add(border)
self.wait(2)
self.play(FadeIn(example))
self.add(grid_copy, diagram[1])
self.play(
grid_copy.set_width, 0.8 * example[1].get_width(),
grid_copy.next_to, example[1], DOWN,
)
self.play(five_part.copy().next_to, example[1], UP)
self.wait(6)
def get_die_faces(self):
dot = Dot()
dot.set_width(0.15)
dot.set_color(BLUE_B)
square = Square()
square.round_corners(0.25)
square.set_stroke(WHITE, 2)
square.set_fill(DARKER_GREY, 1)
square.set_width(0.6)
edge_groups = [
(ORIGIN,),
(UL, DR),
(UL, ORIGIN, DR),
(UL, UR, DL, DR),
(UL, UR, ORIGIN, DL, DR),
(UL, UR, LEFT, RIGHT, DL, DR),
]
arrangements = VGroup(*[
VGroup(*[
dot.copy().move_to(square.get_bounding_box_point(ec))
for ec in edge_group
])
for edge_group in edge_groups
])
square.set_width(1)
faces = VGroup(*[
VGroup(square.copy(), arrangement)
for arrangement in arrangements
])
faces.arrange(RIGHT)
return faces
def get_random_dice(self):
faces = list(self.get_die_faces())
def get_random_pair():
result = VGroup(*random.sample(faces, 2)).copy()
result.arrange(RIGHT)
for mob in result:
mob.shift(random.random() * RIGHT * MED_SMALL_BUFF)
mob.shift(random.random() * UP * MED_SMALL_BUFF)
return result
result = VGroup(*get_random_pair())
result.time = 0
result.iter_count = 0
def update_result(group, dt):
group.time += dt
group.iter_count += 1
if int(group.time) % 3 == 2:
group.set_stroke(YELLOW)
return group
elif result.iter_count % 3 != 0:
return group
else:
pair = get_random_pair()
pair.move_to(group)
group.submobjects = [*pair]
result.add_updater(update_result)
result.update()
return result
def get_dice_diagram(self):
grid = VGroup(*[
VGroup(*[
Square() for x in range(6)
]).arrange(RIGHT, buff=0)
for y in range(6)
]).arrange(DOWN, buff=0)
grid.set_stroke(WHITE, 1)
grid.set_height(5)
colors = color_gradient([RED, YELLOW, GREEN, BLUE], 11)
numbers = VGroup()
for i, row in enumerate(grid):
for j, square in enumerate(row):
num = Integer(i + j + 2)
num.set_height(square.get_height() - MED_LARGE_BUFF)
num.move_to(square)
# num.set_stroke(BLACK, 2, background=True)
num.set_fill(DARK_GREY)
square.set_fill(colors[i + j], 0.9)
numbers.add(num)
faces = VGroup()
face_templates = self.get_die_faces()
face_templates.scale(0.5)
for face, row in zip(face_templates, grid):
face.next_to(row, LEFT, MED_SMALL_BUFF)
faces.add(face)
for face, square in zip(faces.copy(), grid[0]):
face.next_to(square, UP, MED_SMALL_BUFF)
faces.add(face)
result = VGroup(grid, numbers, faces)
return result
class JustRandomDice(RandomnessVsProportions):
def construct(self):
random_dice = self.get_random_dice()
random_dice.center()
self.add(random_dice)
self.wait(60)
class BayesTheoremOnProportions(Scene):
def construct(self):
# Place on top of visuals from "HeartOfBayes"
formula = get_bayes_formula()
formula.scale(1.5)
title = TextMobject("Bayes' theorem")
title.scale(2)
title.next_to(formula, UP, LARGE_BUFF)
group = VGroup(formula, title)
equals = TexMobject("=")
equals.next_to(formula, RIGHT)
h_line = Line(LEFT, RIGHT)
h_line.set_width(4)
h_line.next_to(equals, RIGHT)
h_line.set_stroke(WHITE, 3)
self.add(group)
self.wait()
self.play(
group.to_edge, LEFT,
MaintainPositionRelativeTo(equals, group),
VFadeIn(equals),
MaintainPositionRelativeTo(h_line, group),
VFadeIn(h_line),
)
# People
people = VGroup(*[Person() for x in range(7)])
people.arrange(RIGHT)
people.match_width(h_line)
people.next_to(h_line, DOWN)
people.set_color(BLUE_E)
people[:3].set_color(GREEN)
num_people = people[:3].copy()
self.play(FadeIn(people, lag_ratio=0.1))
self.play(num_people.next_to, h_line, UP)
self.wait(0.5)
# Diagrams
diagram = BayesDiagram(0.25, 0.5, 0.2)
diagram.set_width(0.7 * h_line.get_width())
diagram.next_to(h_line, DOWN)
diagram.hne_rect.set_fill(opacity=0.1)
diagram.nhne_rect.set_fill(opacity=0.1)
num_diagram = diagram.deepcopy()
num_diagram.next_to(h_line, UP)
num_diagram.nhe_rect.set_fill(opacity=0.1)
low_diagram_rects = VGroup(diagram.he_rect, diagram.nhe_rect)
top_diagram_rects = VGroup(num_diagram.he_rect)
self.play(
FadeOut(people),
FadeOut(num_people),
FadeIn(diagram),
FadeIn(num_diagram),
)
self.wait()
# Circle each part
E_part = VGroup(formula[4], *formula[19:]).copy()
H_part = VGroup(formula[2], *formula[8:18]).copy()
E_arrow = Vector(UP, color=BLUE)
E_arrow.next_to(E_part[0], DOWN)
E_words = TextMobject(
"...among cases where\\\\$E$ is True",
tex_to_color_map={"$E$": BLUE},
)
E_words.next_to(E_arrow, DOWN)
H_arrow = Vector(DOWN, color=YELLOW)
H_arrow.next_to(H_part[0], UP)
H_words = TextMobject(
"How often is\\\\$H$ True...",
tex_to_color_map={"$H$": YELLOW},
)
H_words.next_to(H_arrow, UP)
denom_rect = SurroundingRectangle(E_part[1:], color=BLUE)
numer_rect = SurroundingRectangle(H_part[1:], color=YELLOW)
self.play(
formula.set_opacity, 0.5,
ApplyMethod(
E_part.set_stroke, YELLOW, 3, {"background": True},
rate_func=there_and_back,
),
FadeIn(denom_rect),
ShowCreation(E_arrow),
FadeInFrom(E_words, UP),
low_diagram_rects.set_stroke, TEAL, 3,
)
self.wait()
self.play(
FadeOut(E_part),
FadeIn(H_part),
FadeOut(denom_rect),
FadeIn(numer_rect),
ShowCreation(H_arrow),
FadeInFrom(H_words, DOWN),
FadeOutAndShift(title, UP),
low_diagram_rects.set_stroke, WHITE, 1,
top_diagram_rects.set_stroke, YELLOW, 3,
)
self.wait()
class GlimpseOfNextVideo(GraphScene):
CONFIG = {
"x_axis_label": "",
"y_axis_label": "",
"x_min": 0,
"x_max": 15,
"x_axis_width": 12,
"y_min": 0,
"y_max": 1.0,
"y_axis_height": 6,
"y_tick_frequency": 0.125,
"add_x_coords": True,
"formula_position": ORIGIN,
"dx": 0.2,
}
def setup(self):
super().setup()
self.setup_axes()
self.y_axis.add_numbers(
0.25, 0.5, 0.75, 1,
number_config={
"num_decimal_places": 2,
},
direction=LEFT,
)
if self.add_x_coords:
self.x_axis.add_numbers(*range(1, 15),)
def construct(self):
f1 = self.prior
def f2(x):
return f1(x) * self.likelihood(x)
pe = scipy.integrate.quad(f2, 0, 20)[0]
graph1 = self.get_graph(f1)
graph2 = self.get_graph(f2)
rects1 = self.get_riemann_rectangles(graph1, dx=self.dx)
rects2 = self.get_riemann_rectangles(graph2, dx=self.dx)
rects1.set_color(YELLOW_D)
rects2.set_color(BLUE)
for rects in rects1, rects2:
rects.set_stroke(WHITE, 1)
rects1.save_state()
rects1.stretch(0, 1, about_edge=DOWN)
formula = self.get_formula()
self.play(
FadeInFromDown(formula[:4]),
Restore(rects1, lag_ratio=0.05, run_time=2)
)
self.wait()
self.add(rects1.copy().set_opacity(0.4))
self.play(
FadeInFromDown(formula[4:10]),
Transform(rects1, rects2),
)
self.wait()
self.play(
rects1.stretch, 1 / pe, 1, {"about_edge": DOWN},
Write(formula[10:], run_time=1)
)
self.wait()
def get_formula(self):
formula = TexMobject(
"p(H) p(E|H) \\over p(E)",
tex_to_color_map={
"H": HYPOTHESIS_COLOR,
"E": EVIDENCE_COLOR1,
},
substrings_to_isolate=list("p(|)")
)
formula.move_to(self.formula_position)
return formula
def prior(self, x):
return (x**3 / 6) * np.exp(-x)
def likelihood(self, x):
return np.exp(-0.5 * x)
class ComingUp(Scene):
CONFIG = {
"camera_config": {"background_color": DARK_GREY}
}
def construct(self):
rect = ScreenRectangle()
rect.set_height(6)
rect.set_fill(BLACK, 1)
rect.set_stroke(WHITE, 2)
words = TextMobject("Later...")
words.scale(2)
words.to_edge(UP)
rect.next_to(words, DOWN)
self.add(rect)
self.play(FadeIn(words))
self.wait()
class QuestionSteveConclusion(HeartOfBayesTheorem, DescriptionOfSteve):
def construct(self):
# Setup
steve = Steve()
steve.shift(UP)
self.add(steve)
kt = Group(
ImageMobject("kahneman"),
ImageMobject("tversky"),
)
kt.arrange(DOWN)
kt.set_height(6)
randy = Randolph()
kt.next_to(randy, RIGHT, LARGE_BUFF)
randy.align_to(kt, DOWN)
farmers = VGroup(*[Farmer() for x in range(20)])
farmers.arrange_in_grid(n_cols=5)
people = VGroup(Librarian(), farmers)
people.arrange(RIGHT, aligned_edge=UP)
people.set_height(3)
people.next_to(randy.get_corner(UL), UP)
cross = Cross(people)
cross.set_stroke(RED, 8)
# Question K&T
self.play(
steve.scale, 0.5,
steve.to_corner, DL,
FadeIn(randy),
FadeInFromDown(kt, lag_ratio=0.3),
)
self.play(randy.change, "sassy")
self.wait()
self.play(
FadeInFrom(people, RIGHT, lag_ratio=0.01),
randy.change, "raise_left_hand", people,
)
self.wait()
self.play(
ShowCreation(cross),
randy.change, "angry"
)
self.wait()
# Who is Steve?
people.add(cross)
self.play(
people.scale, 0.3,
people.to_corner, UL,
steve.scale, 1.5,
steve.next_to, randy.get_corner(UL), LEFT,
randy.change, "pondering", steve,
)
self.play(randy.look_at, steve)
self.play(Blink(randy))
kt.generate_target()
steve.generate_target()
steve.target.set_height(0.9 * kt[0].get_height())
group = Group(kt.target[0], steve.target, kt.target[1])
group.arrange(RIGHT)
group.to_edge(RIGHT)
self.play(
MoveToTarget(kt),
MoveToTarget(steve),
randy.shift, 2 * LEFT,
randy.change, 'erm', kt.target,
FadeOutAndShift(people, 2 * LEFT),
)
self.remove(people, cross)
self.play(Blink(randy))
self.wait()
jessy = Randolph(color=BLUE_B)
jessy.next_to(randy, LEFT, MED_LARGE_BUFF)
steve.target.match_height(randy)
steve.target.next_to(randy, RIGHT, MED_LARGE_BUFF)
morty = Mortimer()
morty.next_to(steve.target, RIGHT, MED_LARGE_BUFF)
morty.look_at(steve.target),
jessy.look_at(steve.target),
VGroup(jessy, morty, steve.target).to_edge(DOWN)
pis = VGroup(randy, jessy, morty)
self.play(
LaggedStartMap(FadeOutAndShift, kt, lambda m: (m, 3 * UR)),
MoveToTarget(steve),
randy.to_edge, DOWN,
randy.change, "happy", steve.target,
FadeIn(jessy),
FadeIn(morty),
)
self.play(LaggedStart(*[
ApplyMethod(pi.change, "hooray", steve)
for pi in pis
]))
self.play(Blink(morty))
self.play(Blink(jessy))
# The assumption changes the prior
diagram = self.get_diagram(0.05, 0.4, 0.1)
diagram.nhne_rect.set_fill(DARK_GREY)
diagram.set_height(3.5)
diagram.center().to_edge(UP, buff=MED_SMALL_BUFF)
self.play(
FadeIn(diagram),
*[
ApplyMethod(pi.change, "pondering", diagram)
for pi in pis
],
)
self.play(diagram.set_prior, 0.5)
self.play(Blink(jessy))
self.wait()
self.play(Blink(morty))
self.play(
morty.change, "raise_right_hand", diagram,
ApplyMethod(diagram.set_prior, 0.9, run_time=2),
)
self.play(Blink(randy))
self.wait()
# Likelihood of description
description = self.get_description()
description.scale(0.5)
description.to_corner(UL)
self.play(
FadeIn(description),
*[ApplyMethod(pi.change, "sassy", description) for pi in pis]
)
self.play(
diagram.set_likelihood, 0.2,
run_time=2,
)
self.play(
diagram.set_antilikelihood, 0.5,
run_time=2,
)
self.play(Blink(jessy))
self.play(Blink(randy))
self.wait()
# Focus on diagram
diagram.generate_target()
diagram.target.set_height(6)
diagram.target.move_to(3 * LEFT)
formula = get_bayes_formula()
formula.scale(0.75)
formula.to_corner(UR)
self.play(
FadeInFromDown(formula),
LaggedStart(*[
ApplyMethod(pi.change, "thinking", formula)
for pi in pis
])
)
self.play(Blink(randy))
self.play(
LaggedStartMap(
FadeOutAndShiftDown,
VGroup(description, *pis, steve),
),
MoveToTarget(diagram, run_time=3),
ApplyMethod(
formula.scale, 1.5, {"about_edge": UR},
run_time=2.5,
),
)
self.wait()
kw = {"run_time": 2}
self.play(diagram.set_prior, 0.1, **kw)
self.play(diagram.set_prior, 0.6, **kw)
self.play(diagram.set_likelihood, 0.5, **kw),
self.play(diagram.set_antilikelihood, 0.1, **kw),
self.wait()
class WhoAreYou(Scene):
def construct(self):
words = TextMobject("Who are you?")
self.add(words)
class FadeInHeart(Scene):
def construct(self):
heart = SuitSymbol("hearts")
self.play(FadeInFromDown(heart))
self.play(FadeOut(heart))
class ReprogrammingThought(Scene):
CONFIG = {
"camera_config": {
"background_color": DARKER_GREY,
}
}
def construct(self):
brain = SVGMobject("brain")
brain.set_fill(GREY, 1)
brain.set_sheen(1, UL)
brain.set_stroke(width=0)
arrow = DoubleArrow(ORIGIN, 3 * RIGHT)
formula = get_bayes_formula()
group = VGroup(brain, arrow, formula)
group.arrange(RIGHT)
group.center()
q_marks = TexMobject("???")
q_marks.scale(1.5)
q_marks.next_to(arrow, UP, SMALL_BUFF)
kt = Group(
ImageMobject("kahneman"),
ImageMobject("tversky"),
)
kt.arrange(RIGHT)
kt.set_height(2)
kt.to_corner(UR)
brain_outline = brain.copy()
brain_outline.set_fill(opacity=0)
brain_outline.set_stroke(TEAL, 4)
self.play(FadeInFrom(brain, RIGHT))
self.play(
GrowFromCenter(arrow),
LaggedStartMap(FadeInFromDown, q_marks[0]),
run_time=1
)
self.play(FadeInFrom(formula, LEFT))
self.wait()
kw = {"run_time": 1, "lag_ratio": 0.3}
self.play(LaggedStartMap(FadeInFromDown, kt, **kw))
self.play(LaggedStartMap(FadeOut, kt, **kw))
self.wait()
self.add(brain)
self.play(ShowCreationThenFadeOut(
brain_outline,
lag_ratio=0.01,
run_time=2
))
# Bubble
bubble = ThoughtBubble()
bubble.next_to(brain, UR, SMALL_BUFF)
bubble.shift(2 * DOWN)
diagram = BayesDiagram(0.25, 0.8, 0.5)
diagram.set_height(2.5)
diagram.move_to(bubble.get_bubble_center())
group = VGroup(brain, arrow, q_marks, formula)
self.play(
DrawBorderThenFill(VGroup(*reversed(bubble))),
group.shift, 2 * DOWN,
)
self.play(FadeIn(diagram))
self.wait()
self.play(
q_marks.scale, 1.5,
q_marks.space_out_submobjects, 1.5,
q_marks.set_opacity, 0,
)
self.remove(q_marks)
self.wait()
# Move parts
prior_outline = formula[7:12].copy()
prior_outline.set_stroke(YELLOW, 5, background=True)
like_outline = formula[12:18].copy()
like_outline.set_stroke(BLUE, 5, background=True)
self.play(
FadeIn(prior_outline),
ApplyMethod(diagram.set_prior, 0.5, run_time=2)
)
self.play(FadeOut(prior_outline))
self.play(
FadeIn(like_outline),
ApplyMethod(diagram.set_likelihood, 0.2, run_time=2),
)
self.play(FadeOut(like_outline))
self.wait()
class MassOfEarthEstimates(GlimpseOfNextVideo):
CONFIG = {
"add_x_coords": False,
"formula_position": 2 * UP + 0.5 * RIGHT,
"dx": 0.05,
}
def setup(self):
super().setup()
earth = SVGMobject(
file_name="earth",
height=1.5,
fill_color=BLACK,
)
earth.set_stroke(width=0)
# earth.set_stroke(BLACK, 1, background=True)
circle = Circle(
stroke_width=3,
stroke_color=GREEN,
fill_opacity=1,
fill_color=BLUE_C,
)
circle.replace(earth)
earth.add_to_back(circle)
earth.set_height(0.75)
words = TextMobject("Mass of ")
words.next_to(earth, LEFT)
group = VGroup(words, earth)
group.to_edge(DOWN).shift(2 * RIGHT)
self.add(group)
def get_formula(self):
formula = TexMobject(
"p(M) p(\\text{data}|M) \\over p(\\text{data})",
tex_to_color_map={
"M": HYPOTHESIS_COLOR,
"\\text{data}": EVIDENCE_COLOR1,
},
substrings_to_isolate=list("p(|)")
)
formula.move_to(self.formula_position)
return formula
def prior(self, x, mu=6, sigma=1):
factor = (1 / sigma / np.sqrt(TAU))
return factor * np.exp(-0.5 * ((x - mu) / sigma)**2)
def likelihood(self, x):
return self.prior(x, 5, 1)
class ShowProgrammer(Scene):
CONFIG = {
"camera_config": {
"background_color": DARKER_GREY,
}
}
def construct(self):
programmer = SVGMobject(file_name="programmer")
programmer.set_stroke(width=0)
programmer.set_fill(GREY, 1)
programmer.set_sheen(1, UL)
programmer.set_height(3)
programmer.to_corner(DL)
self.play(FadeInFrom(programmer, DOWN))
self.wait()
class BayesEndScene(PatreonEndScreen):
CONFIG = {
"specific_patrons": [
"Juan Benet",
"Vassili Philippov",
"Burt Humburg",
"D. Sivakumar",
"John Le",
"Matt Russell",
"Scott Gray",
"soekul",
"Steven Braun",
"Tihan Seale",
"Ali Yahya",
"Arthur Zey",
"dave nicponski",
"Joseph Kelly",
"Kaustuv DeBiswas",
"Lambda AI Hardware",
"Lukas Biewald",
"Mark Heising",
"Nicholas Cahill",
"Peter Mcinerney",
"Quantopian",
"Scott Walter, Ph.D.",
"Tauba Auerbach",
"Yana Chernobilsky",
"Yu Jun",
"Lukas -krtek.net- Novy",
"Britt Selvitelle",
"Britton Finley",
"David Gow",
"J",
"Jonathan Wilson",
"Joseph John Cox",
"Magnus Dahlström",
"Matteo Delabre",
"Randy C. Will",
"Ray Hua Wu",
"Ryan Atallah",
"Luc Ritchie",
"1stViewMaths",
"Adam Dřínek",
"Aidan Shenkman",
"Alan Stein",
"Alex Mijalis",
"Alexis Olson",
"Andreas Benjamin Brössel",
"Andrew Busey",
"Andrew Cary",
"Andrew R. Whalley",
"Anthony Turvey",
"Antoine Bruguier",
"Antonio Juarez",
"Arjun Chakroborty",
"Austin Goodman",
"Avi Finkel",
"Awoo",
"Azeem Ansar",
"AZsorcerer",
"Barry Fam",
"Bernd Sing",
"Boris Veselinovich",
"Bradley Pirtle",
"Brian Staroselsky",
"Calvin Lin",
"Chaitanya Upmanu",
"Charles Southerland",
"Charlie N",
"Chenna Kautilya",
"Chris Connett",
"Christian Kaiser",
"Clark Gaebel",
"Cooper Jones",
"Corey Ogburn",
"Danger Dai",
"Daniel Herrera C",
"Dave B",
"Dave Kester",
"David B. Hill",
"David Clark",
"David Pratt",
"DeathByShrimp",
"Delton Ding",
"Dominik Wagner",
"eaglle",
"emptymachine",
"Eric Younge",
"Eryq Ouithaqueue",
"Federico Lebron",
"Fernando Via Canel",
"Frank R. Brown, Jr.",
"Giovanni Filippi",
"Hal Hildebrand",
"Hitoshi Yamauchi",
"Ivan Sorokin",
"j eduardo perez",
"Jacob Baxter",
"Jacob Harmon",
"Jacob Hartmann",
"Jacob Magnuson",
"Jameel Syed",
"James Liao",
"Jason Hise",
"Jayne Gabriele",
"Jeff Linse",
"Jeff Straathof",
"John C. Vesey",
"John Griffith",
"John Haley",
"John V Wertheim",
"Jonathan Heckerman",
"Josh Kinnear",
"Joshua Claeys",
"Kai-Siang Ang",
"Kanan Gill",
"Kartik Cating-Subramanian",
"L0j1k",
"Lee Redden",
"Linh Tran",
"Ludwig Schubert",
"Magister Mugit",
"Mark B Bahu",
"Mark Mann",
"Martin Price",
"Mathias Jansson",
"Matt Godbolt",
"Matt Langford",
"Matt Roveto",
"Matthew Bouchard",
"Matthew Cocke",
"Michael Hardel",
"Michael W White",
"Mirik Gogri",
"Mustafa Mahdi",
"Márton Vaitkus",
"Nikita Lesnikov",
"Omar Zrien",
"Owen Campbell-Moore",
"Patrick Lucas",
"Pedro Igor S. Budib",
"Peter Ehrnstrom",
"rehmi post",
"Rex Godby",
"Richard Barthel",
"Ripta Pasay",
"Rish Kundalia",
"Roman Sergeychik",
"Roobie",
"SansWord Huang",
"Sebastian Garcia",
"Solara570",
"Steven Siddals",
"Stevie Metke",
"Suthen Thomas",
"Tal Einav",
"Ted Suzman",
"The Responsible One",
"Thomas Roets",
"Thomas Tarler",
"Tianyu Ge",
"Tom Fleming",
"Tyler VanValkenburg",
"Valeriy Skobelev",
"Veritasium",
"Vinicius Reis",
"Xuanji Li",
"Yavor Ivanov",
"YinYangBalance.Asia",
],
}
class Thumbnail(Scene):
def construct(self):
diagram = BayesDiagram(0.25, 0.4, 0.1)
diagram.set_height(3)
diagram.add_brace_attrs()
braces = VGroup(
diagram.h_brace,
diagram.he_brace,
diagram.nhe_brace,
)
diagram.add(*braces)
kw = {
"tex_to_color_map": {
"H": YELLOW,
"E": BLUE,
"\\neg": RED,
}
}
labels = VGroup(
TexMobject("P(H)", **kw),
TexMobject("P(E|H)", **kw),
TexMobject("P(E|\\neg H)", **kw),
)
labels.scale(1)
for label, brace, vect in zip(labels, braces, [DOWN, LEFT, RIGHT]):
label.next_to(brace, vect)
diagram.add(*labels)
# diagram.set_height(6)
diagram.to_edge(DOWN, buff=MED_SMALL_BUFF)
diagram.shift(2 * LEFT)
self.add(diagram)
diagram.set_height(FRAME_HEIGHT - 1)
diagram.center().to_edge(DOWN)
for rect in diagram.evidence_split:
rect.set_sheen(0.2, UL)
return
# Formula
formula = get_bayes_formula()
formula.scale(1.5)
formula.to_corner(UL)
frac = VGroup(
diagram.he_rect.copy(),
Line(ORIGIN, 4 * RIGHT).set_stroke(WHITE, 3),
VGroup(
diagram.he_rect.copy(),
TexMobject("+"),
diagram.nhe_rect.copy(),
).arrange(RIGHT)
)
frac.arrange(DOWN)
equals = TexMobject("=")
equals.next_to(formula, RIGHT)
frac.next_to(equals, RIGHT)
self.add(formula)
self.add(equals, frac)
VGroup(formula, equals, frac).to_edge(UP, buff=SMALL_BUFF)