3b1b-manim/active_projects/eop/bayes.py
2019-02-04 14:54:25 -08:00

2254 lines
70 KiB
Python

from big_ol_pile_of_manim_imports import *
#revert_to_original_skipping_status
#########
class BayesOpeningQuote(OpeningQuote):
CONFIG = {
"quote" : [
"Inside every non-Bayesian there \\\\ is a Bayesian struggling to get out."
],
"author" : "Dennis V. Lindley",
}
class IntroducePokerHand(PiCreatureScene, SampleSpaceScene):
CONFIG = {
"community_cards_center" : 1.5*DOWN,
"community_card_values" : ["10S", "QH", "AH", "2C", "5H"],
"your_hand_values" : ["JS", "KC"],
}
def construct(self):
self.add_cards()
self.indicate_straight()
self.show_flush_potential()
self.compute_flush_probability()
self.show_flush_sample_space()
self.talk_through_sample_space()
self.place_high_bet()
self.change_belief()
self.move_community_cards_out_of_the_way()
self.name_bayes_rule()
def add_cards(self):
you, her = self.you, self.her
community_cards = VGroup(*list(map(
PlayingCard, self.community_card_values
)))
community_cards.arrange(RIGHT)
community_cards.move_to(self.community_cards_center)
deck = VGroup(*[
PlayingCard(turned_over = True)
for x in range(5)
])
for i, card in enumerate(deck):
card.shift(i*(0.03*RIGHT + 0.015*DOWN))
deck.next_to(community_cards, LEFT)
you.hand = self.get_hand(you, self.your_hand_values)
her.hand = self.get_hand(her, None)
hand_cards = VGroup(*it.chain(*list(zip(you.hand, her.hand))))
self.add(deck)
for group in hand_cards, community_cards:
for card in group:
card.generate_target()
card.scale(0.01)
card.move_to(deck[-1], UP+RIGHT)
self.play(LaggedStart(MoveToTarget, group, lag_ratio = 0.8))
self.wait()
self.wait()
self.community_cards = community_cards
self.deck = deck
def indicate_straight(self):
you = self.you
community_cards = self.community_cards
you.hand.save_state()
you.hand.generate_target()
for card in you.hand.target:
card.set_height(community_cards.get_height())
selected_community_cards = VGroup(*[card for card in community_cards if card.numerical_value >= 10])
selected_community_cards.submobjects.sort(
key=lambda c: c.numerical_value
)
selected_community_cards.save_state()
for card in selected_community_cards:
card.generate_target()
straight_cards = VGroup(*it.chain(
you.hand.target,
[c.target for c in selected_community_cards]
))
straight_cards.submobjects.sort(
key=lambda c: c.numerical_value
)
straight_cards.arrange(RIGHT, buff = SMALL_BUFF)
straight_cards.next_to(community_cards, UP, aligned_edge = LEFT)
you.hand.target.shift(MED_SMALL_BUFF*UP)
self.play(LaggedStart(
MoveToTarget,
selected_community_cards,
run_time = 1.5
))
self.play(MoveToTarget(you.hand))
self.play(LaggedStart(
ApplyMethod,
straight_cards,
lambda m : (m.set_color, YELLOW),
rate_func = there_and_back,
run_time = 1.5,
lag_ratio = 0.5,
remover = True,
))
self.play(you.change, "hooray", straight_cards)
self.wait(2)
self.play(
selected_community_cards.restore,
you.hand.restore,
you.change_mode, "happy"
)
self.wait()
def show_flush_potential(self):
you, her = self.you, self.her
heart_cards = VGroup(*[c for c in self.community_cards if c.suit == "hearts"])
heart_cards.save_state()
her.hand.save_state()
her.hand.generate_target()
her.hand.target.arrange(RIGHT)
her.hand.target.next_to(heart_cards, UP)
her.hand.target.to_edge(UP)
her.glasses.save_state()
her.glasses.move_to(her.hand.target)
her.glasses.set_fill(opacity = 0)
heart_qs = VGroup()
hearts = VGroup()
q_marks = VGroup()
for target in her.hand.target:
heart = SuitSymbol("hearts")
q_mark = TexMobject("?")
heart_q = VGroup(heart, q_mark)
for mob in heart_q:
mob.set_height(0.5)
heart_q.arrange(RIGHT, buff = SMALL_BUFF)
heart_q.move_to(target)
heart_qs.add(heart, q_mark)
hearts.add(heart)
q_marks.add(q_mark)
self.play(heart_cards.shift, heart_cards.get_height()*UP)
self.play(you.change_mode, "hesitant")
self.play(MoveToTarget(her.hand))
self.play(LaggedStart(DrawBorderThenFill, heart_qs))
self.play(
her.change, "happy",
her.glasses.restore,
)
self.pi_creatures.remove(her)
new_suit_pairs = [
("clubs", "diamonds"),
("diamonds", "spades"),
("spades", "clubs"),
("hearts", "hearts"),
]
for new_suit_pair in new_suit_pairs:
new_symbols = VGroup(*list(map(SuitSymbol, new_suit_pair)))
for new_symbol, heart in zip(new_symbols, hearts):
new_symbol.replace(heart, dim_to_match = 1)
self.play(Transform(
hearts, new_symbols,
submobject_mode = "lagged_start"
))
self.wait()
self.play(FadeOut(heart_qs))
self.play(
heart_cards.restore,
her.hand.restore,
you.change_mode, "pondering",
)
self.q_marks = q_marks
def compute_flush_probability(self):
you, her = self.you, self.her
equation = TexMobject(
"{ {10 \\choose 2}", "\\over", "{45 \\choose 2} }",
"=", "{45 \\over 990}", "\\approx", "4.5\\%"
)
equation.next_to(self.community_cards, UP, buff = LARGE_BUFF)
percentage = equation.get_part_by_tex("4.5")
ten = VGroup(*equation[0][1:3])
num_hearts = TextMobject("\\# Remaining hearts")
num_hearts.scale(0.75)
num_hearts.next_to(
ten, UP, aligned_edge = LEFT
)
num_hearts.to_edge(UP)
num_hearts.set_color(RED)
num_hearts_arrow = Arrow(
num_hearts.get_bottom(), ten.get_right(),
color = RED, buff = SMALL_BUFF
)
fourty_five = VGroup(*equation[2][1:3])
num_cards = TextMobject("\\# Remaining cards")
num_cards.scale(0.75)
num_cards.next_to(fourty_five, LEFT)
num_cards.to_edge(LEFT)
num_cards.set_color(BLUE)
num_cards_arrow = Arrow(
num_cards, fourty_five,
color = BLUE, buff = SMALL_BUFF
)
self.play(LaggedStart(FadeIn, equation))
self.wait(2)
self.play(
FadeIn(num_hearts),
ShowCreation(num_hearts_arrow),
ten.set_color, RED,
)
self.play(
FadeIn(num_cards),
ShowCreation(num_cards_arrow),
fourty_five.set_color, BLUE
)
self.wait(3)
equation.remove(percentage)
self.play(*list(map(FadeOut, [
equation,
num_hearts, num_hearts_arrow,
num_cards, num_cards_arrow,
])))
self.percentage = percentage
def show_flush_sample_space(self):
you, her = self.you, self.her
percentage = self.percentage
sample_space = self.get_sample_space()
sample_space.add_title("Your belief")
sample_space.move_to(VGroup(you.hand, her.hand))
sample_space.to_edge(UP, buff = MED_SMALL_BUFF)
p = 1./22
sample_space.divide_horizontally(
p, colors = [SuitSymbol.CONFIG["red"], BLUE_E]
)
braces, labels = sample_space.get_side_braces_and_labels([
percentage.get_tex_string(), "95.5\\%"
])
top_label, bottom_label = labels
self.play(
FadeIn(sample_space),
ReplacementTransform(percentage, top_label)
)
self.play(*list(map(GrowFromCenter, [
brace for brace in braces
])))
self.wait(2)
self.play(Write(bottom_label))
self.wait(2)
self.sample_space = sample_space
def talk_through_sample_space(self):
her = self.her
sample_space = self.sample_space
top_part, bottom_part = self.sample_space.horizontal_parts
flush_hands, non_flush_hands = hand_lists = [
[self.get_hand(her, keys) for keys in key_list]
for key_list in [
[("3H", "8H"), ("4H", "5H"), ("JH", "KH")],
[("AC", "6D"), ("3D", "6S"), ("JH", "4C")],
]
]
for hand_list, part in zip(hand_lists, [top_part, bottom_part]):
self.play(Indicate(part, scale_factor = 1))
for hand in hand_list:
hand.save_state()
hand.scale(0.01)
hand.move_to(part.get_right())
self.play(hand.restore)
self.wait()
self.wait()
self.play(*list(map(FadeOut, it.chain(*hand_lists))))
def place_high_bet(self):
you, her = self.you, self.her
pre_money = VGroup(*[
VGroup(*[
TexMobject("\\$")
for x in range(10)
]).arrange(RIGHT, buff = SMALL_BUFF)
for y in range(4)
]).arrange(UP, buff = SMALL_BUFF)
money = VGroup(*it.chain(*pre_money))
money.set_color(GREEN)
money.scale(0.8)
money.next_to(her.hand, DOWN)
for dollar in money:
dollar.save_state()
dollar.scale(0.01)
dollar.move_to(her.get_boundary_point(RIGHT))
dollar.set_fill(opacity = 0)
self.play(LaggedStart(
ApplyMethod,
money,
lambda m : (m.restore,),
run_time = 5,
))
self.play(you.change_mode, "confused")
self.wait()
self.money = money
def change_belief(self):
numbers = self.sample_space.horizontal_parts.labels
rect = Rectangle(stroke_width = 0)
rect.set_fill(BLACK, 1)
rect.stretch_to_fit_width(numbers.get_width())
rect.stretch_to_fit_height(self.sample_space.get_height())
rect.move_to(numbers, UP)
self.play(FadeIn(rect))
anims = self.get_horizontal_division_change_animations(0.2)
anims.append(Animation(rect))
self.play(
*anims,
run_time = 3,
rate_func = there_and_back
)
self.play(FadeOut(rect))
def move_community_cards_out_of_the_way(self):
cards = self.community_cards
cards.generate_target()
cards.target.arrange(
RIGHT, buff = -cards[0].get_width() + MED_SMALL_BUFF,
)
cards.target.move_to(self.deck)
cards.target.to_edge(LEFT)
self.sample_space.add_braces_and_labels()
self.play(
self.deck.scale, 0.7,
self.deck.next_to, cards.target, UP,
self.deck.to_edge, LEFT,
self.sample_space.shift, 3*DOWN,
MoveToTarget(cards)
)
def name_bayes_rule(self):
title = TextMobject("Bayes' rule")
title.set_color(BLUE)
title.to_edge(UP)
subtitle = TextMobject("Update ", "prior ", "beliefs")
subtitle.scale(0.8)
subtitle.next_to(title, DOWN)
prior_word = subtitle.get_part_by_tex("prior")
numbers = self.sample_space.horizontal_parts.labels
rect = SurroundingRectangle(numbers, color = GREEN)
arrow = Arrow(prior_word.get_bottom(), rect.get_top())
arrow.set_color(GREEN)
words = TextMobject(
"Maybe she really \\\\ does have a flush $\\dots$",
alignment = ""
)
words.scale(0.7)
words.next_to(self.money, DOWN, aligned_edge = LEFT)
self.play(
Write(title, run_time = 2),
self.you.change_mode, "pondering"
)
self.wait()
self.play(FadeIn(subtitle))
self.play(prior_word.set_color, GREEN)
self.play(
ShowCreation(rect),
ShowCreation(arrow)
)
self.wait(3)
self.play(Write(words))
self.wait(3)
######
def create_pi_creatures(self):
shift_val = 3
you = PiCreature(color = BLUE_D)
her = PiCreature(color = BLUE_B).flip()
for pi in you, her:
pi.scale(0.5)
you.to_corner(UP+LEFT)
her.to_corner(UP+RIGHT)
you.make_eye_contact(her)
glasses = SunGlasses(her)
her.glasses = glasses
self.you = you
self.her = her
return VGroup(you, her)
def get_hand(self, pi_creature, keys = None):
if keys is not None:
hand = VGroup(*list(map(PlayingCard, keys)))
else:
hand = VGroup(*[
PlayingCard(turned_over = True)
for x in range(2)
])
hand.scale(0.7)
card1, card2 = hand
vect = np.sign(pi_creature.get_center()[0])*LEFT
card2.move_to(card1)
card2.shift(MED_SMALL_BUFF*RIGHT + SMALL_BUFF*DOWN)
hand.next_to(
pi_creature, vect,
buff = MED_LARGE_BUFF,
aligned_edge = UP
)
return hand
class HowDoesPokerWork(TeacherStudentsScene):
def construct(self):
self.student_says(
"Wait, how does \\\\ poker work again?",
target_mode = "confused",
run_time = 1
)
self.change_student_modes(*["confused"]*3)
self.wait(2)
class YourGutKnowsBayesRule(TeacherStudentsScene):
def construct(self):
self.teacher_says(
"Your gut knows \\\\ Bayes' rule.",
run_time = 1
)
self.change_student_modes("confused", "gracious", "guilty")
self.wait(3)
class UpdatePokerPrior(SampleSpaceScene):
CONFIG = {
"double_heart_template" : "HH",
"cash_string" : "\\$\\$\\$",
}
def construct(self):
self.add_sample_space()
self.add_top_conditionals()
self.react_to_top_conditionals()
self.add_bottom_conditionals()
self.ask_where_conditionals_come_from()
self.vary_conditionals()
self.show_restricted_space()
self.write_P_flush_given_bet()
self.reshape_rectangles()
self.compare_prior_to_posterior()
self.preview_tweaks()
self.tweak_non_flush_case()
self.tweak_flush_case()
self.tweak_prior()
self.compute_posterior()
def add_sample_space(self):
p = 1./22
sample_space = SampleSpace(fill_opacity = 0)
sample_space.shift(LEFT)
sample_space.divide_horizontally(p, colors = [
SuitSymbol.CONFIG["red"], BLUE_E
])
labels = self.get_prior_labels(p)
braces_and_labels = sample_space.get_side_braces_and_labels(labels)
self.play(
LaggedStart(FadeIn, sample_space),
Write(braces_and_labels)
)
self.wait()
sample_space.add(braces_and_labels)
self.sample_space = sample_space
def add_top_conditionals(self):
top_part = self.sample_space.horizontal_parts[0]
color = average_color(YELLOW, GREEN, GREEN)
p = 0.97
top_part.divide_vertically(p, colors = [color, BLUE])
label = self.get_conditional_label(p, True)
brace, _ignore = top_part.get_top_braces_and_labels([label])
explanation = TextMobject(
"Probability of", "high bet", "given", "flush"
)
explanation.set_color_by_tex("high bet", GREEN)
explanation.set_color_by_tex("flush", RED)
explanation.scale(0.6)
explanation.next_to(label, UP)
self.play(
FadeIn(top_part.vertical_parts),
FadeIn(label),
GrowFromCenter(brace),
)
self.play(Write(explanation, run_time = 3))
self.wait(2)
self.sample_space.add(brace, label)
self.top_explanation = explanation
self.top_conditional_rhs = label[-1]
def react_to_top_conditionals(self):
her = PiCreature(color = BLUE_B).flip()
her.next_to(self.sample_space, RIGHT)
her.to_edge(RIGHT)
glasses = SunGlasses(her)
glasses.save_state()
glasses.shift(UP)
glasses.set_fill(opacity = 0)
her.glasses = glasses
self.play(FadeIn(her))
self.play(glasses.restore)
self.play(
her.change_mode, "happy",
Animation(glasses)
)
self.wait(2)
self.her = her
def add_bottom_conditionals(self):
her = self.her
bottom_part = self.sample_space.horizontal_parts[1]
p = 0.3
bottom_part.divide_vertically(p, colors = [GREEN_E, BLUE_E])
label = self.get_conditional_label(p, False)
brace, _ignore = bottom_part.get_bottom_braces_and_labels([label])
explanation = TextMobject(
"Probability of", "high bet", "given", "no flush"
)
explanation.set_color_by_tex("high bet", GREEN)
explanation.set_color_by_tex("no flush", RED)
explanation.scale(0.6)
explanation.next_to(label, DOWN)
self.play(DrawBorderThenFill(bottom_part.vertical_parts))
self.play(
GrowFromCenter(brace),
FadeIn(label)
)
self.play(
her.change_mode, "shruggie",
MaintainPositionRelativeTo(her.glasses, her.eyes)
)
self.wait()
self.play(*[
ReplacementTransform(
VGroup(*label[i1:i2]).copy(),
VGroup(explanation[j]),
run_time = 2,
rate_func = squish_rate_func(smooth, a, a+0.5)
)
for a, (i1, i2, j) in zip(np.linspace(0, 0.5, 4), [
(0, 1, 0),
(1, 2, 1),
(2, 3, 2),
(3, 6, 3),
])
])
self.wait()
self.play(Write(VGroup(*label[-2:])))
self.wait(2)
self.play(*list(map(FadeOut, [her, her.glasses])))
self.sample_space.add(brace, label)
self.bottom_explanation = explanation
self.bottom_conditional_rhs = label[-1]
def ask_where_conditionals_come_from(self):
randy = Randolph().flip()
randy.scale(0.75)
randy.to_edge(RIGHT)
randy.shift(2*DOWN)
words = TextMobject("Where do these \\\\", "numbers", "come from?")
numbers_word = words.get_part_by_tex("numbers")
numbers_word.set_color(YELLOW)
words.scale(0.7)
bubble = ThoughtBubble(height = 3, width = 4)
bubble.pin_to(randy)
bubble.shift(MED_LARGE_BUFF*RIGHT)
bubble.add_content(words)
numbers = VGroup(
self.top_conditional_rhs,
self.bottom_conditional_rhs
)
numbers.save_state()
arrows = VGroup(*[
Arrow(
numbers_word.get_left(),
num.get_right(),
buff = 2*SMALL_BUFF
)
for num in numbers
])
questions = VGroup(*list(map(TextMobject, [
"Does she bluff?",
"How much does she have?",
"Does she take risks?",
"What's her model of me?",
"\\vdots"
])))
questions.arrange(DOWN, aligned_edge = LEFT)
questions[-1].next_to(questions[-2], DOWN)
questions.scale(0.7)
questions.next_to(randy, UP)
questions.shift_onto_screen()
self.play(
randy.change_mode, "confused",
ShowCreation(bubble),
Write(words, run_time = 2)
)
self.play(*list(map(ShowCreation, arrows)))
self.play(numbers.set_color, YELLOW)
self.play(Blink(randy))
self.play(randy.change_mode, "maybe")
self.play(*list(map(FadeOut, [
bubble, words, arrows
])))
for question in questions:
self.play(
FadeIn(question),
randy.look_at, question
)
self.wait()
self.play(Blink(randy))
self.wait()
self.play(
randy.change_mode, "pondering",
FadeOut(questions)
)
self.randy = randy
def vary_conditionals(self):
randy = self.randy
rects = VGroup(*[
SurroundingRectangle(
VGroup(explanation),
buff = SMALL_BUFF,
)
for explanation, rhs in zip(
[self.top_explanation, self.bottom_explanation],
[self.top_conditional_rhs, self.bottom_conditional_rhs],
)
])
new_conditionals = [
(0.91, 0.4),
(0.83, 0.1),
(0.99, 0.2),
(0.97, 0.3),
]
self.play(*list(map(ShowCreation, rects)))
self.play(FadeOut(rects))
for i, value in enumerate(it.chain(*new_conditionals)):
self.play(
randy.look_at, rects[i%2],
*self.get_conditional_change_anims(i%2, value)
)
if i%2 == 1:
self.wait()
self.play(FadeOut(randy))
def show_restricted_space(self):
high_bet_space, low_bet_space = [
VGroup(*[
self.sample_space.horizontal_parts[i].vertical_parts[j]
for i in range(2)
])
for j in range(2)
]
words = TexMobject("P(", self.cash_string, ")")
words.set_color_by_tex(self.cash_string, GREEN)
words.next_to(self.sample_space, RIGHT)
low_bet_space.generate_target()
for submob in low_bet_space.target:
submob.set_color(average_color(
submob.get_color(), *[BLACK]*4
))
arrows = VGroup(*[
Arrow(
words.get_left(),
submob.get_edge_center(vect),
color = submob.get_color()
)
for submob, vect in zip(high_bet_space, [DOWN, RIGHT])
])
self.play(MoveToTarget(low_bet_space))
self.play(
Write(words),
*list(map(ShowCreation, arrows))
)
self.wait()
for rect in high_bet_space:
self.play(Indicate(rect, scale_factor = 1))
self.play(*list(map(FadeOut, [words, arrows])))
self.high_bet_space = high_bet_space
def write_P_flush_given_bet(self):
posterior_tex = TexMobject(
"P(", self.double_heart_template,
"|", self.cash_string, ")"
)
posterior_tex.scale(0.7)
posterior_tex.set_color_by_tex(self.cash_string, GREEN)
self.insert_double_heart(posterior_tex)
rects = self.high_bet_space.copy()
rects = [rects[0].copy()] + list(rects)
for rect in rects:
rect.generate_target()
numerator = rects[0].target
plus = TexMobject("+")
denominator = VGroup(rects[1].target, plus, rects[2].target)
denominator.arrange(RIGHT, buff = SMALL_BUFF)
frac_line = TexMobject("\\over")
frac_line.stretch_to_fit_width(denominator.get_width())
fraction = VGroup(numerator, frac_line, denominator)
fraction.arrange(DOWN)
arrow = TexMobject("\\downarrow")
group = VGroup(posterior_tex, arrow, fraction)
group.arrange(DOWN)
group.to_corner(UP+RIGHT)
self.play(LaggedStart(FadeIn, posterior_tex))
self.play(Write(arrow))
self.play(MoveToTarget(rects[0]))
self.wait()
self.play(*it.chain(
list(map(Write, [frac_line, plus])),
list(map(MoveToTarget, rects[1:]))
))
self.wait(3)
self.play(*list(map(FadeOut, [arrow, fraction] + rects)))
self.posterior_tex = posterior_tex
def reshape_rectangles(self):
post_rects = self.get_posterior_rectangles()
prior_rects = self.get_prior_rectangles()
braces, labels = self.get_posterior_rectangle_braces_and_labels(
post_rects, [self.posterior_tex.copy()]
)
height_rect = SurroundingRectangle(braces)
self.play(
ReplacementTransform(
prior_rects.copy(), post_rects,
run_time = 2,
),
)
self.wait(2)
self.play(ReplacementTransform(self.posterior_tex, labels[0]))
self.posterior_tex = labels[0]
self.play(GrowFromCenter(braces))
self.wait()
self.play(ShowCreation(height_rect))
self.play(FadeOut(height_rect))
self.wait()
self.post_rects = post_rects
def compare_prior_to_posterior(self):
prior_tex = self.sample_space.horizontal_parts.labels[0]
post_tex = self.posterior_tex
prior_rect, post_rect = [
SurroundingRectangle(tex, stroke_width = 2)
for tex in [prior_tex, post_tex]
]
post_words = TextMobject("Posterior", "probability")
post_words.scale(0.8)
post_words.to_corner(UP+RIGHT)
post_arrow = Arrow(
post_words[0].get_bottom(), post_tex.get_top(),
color = WHITE
)
self.play(ShowCreation(prior_rect))
self.wait()
self.play(ReplacementTransform(prior_rect, post_rect))
self.wait()
self.play(FadeOut(post_rect))
self.play(Indicate(post_tex.get_part_by_tex(self.cash_string)))
self.wait()
self.play(
Write(post_words),
ShowCreation(post_arrow)
)
self.wait()
self.play(post_words[1].fade, 0.8)
self.wait(2)
self.play(*list(map(FadeOut, [post_words, post_arrow])))
def preview_tweaks(self):
post_rects = self.post_rects
new_value_lists = [
(0.85, 0.1, 0.11),
(0.97, 0.3, 1./22),
]
for new_values in new_value_lists:
for i, value in zip(list(range(2)), new_values):
self.play(*self.get_conditional_change_anims(
i, value, post_rects
))
self.play(*self.get_prior_change_anims(
new_values[-1], post_rects
))
self.wait(2)
def tweak_non_flush_case(self):
her = self.her
her.scale_in_place(0.7)
her.change_mode("plain")
her.shift(DOWN)
her.glasses = SunGlasses(her)
post_rects = self.post_rects
posterior = VGroup(post_rects.braces, post_rects.labels)
prior_rects = self.get_prior_rectangles()
risk_averse_words = TextMobject(
"Suppose risk \\\\ averse \\dots"
)
risk_averse_words.scale(0.7)
risk_averse_words.next_to(her, DOWN)
risk_averse_words.shift_onto_screen()
arrows = VGroup(*[
Arrow(ORIGIN, LEFT, tip_length = SMALL_BUFF)
for x in range(3)
])
arrows.arrange(DOWN)
arrows.next_to(prior_rects[1], RIGHT, SMALL_BUFF)
self.wait(2)
self.play(*list(map(FadeIn, [her, her.glasses])))
self.play(LaggedStart(FadeIn, risk_averse_words))
self.play(her.change_mode, "sad", Animation(her.glasses))
self.wait()
self.play(ShowCreation(arrows))
self.play(
*it.chain(
self.get_conditional_change_anims(1, 0.1, post_rects),
[Animation(arrows)]
),
run_time = 3
)
self.play(FadeOut(arrows))
self.wait(2)
post_surrounding_rect = SurroundingRectangle(posterior)
self.play(ShowCreation(post_surrounding_rect))
self.play(FadeOut(post_surrounding_rect))
self.wait()
self.play(
FadeOut(risk_averse_words),
*self.get_conditional_change_anims(1, 0.3, post_rects),
run_time = 2
)
def tweak_flush_case(self):
her = self.her
post_rects = self.post_rects
self.play(
her.change_mode, "erm", Animation(her.glasses)
)
self.play(
*self.get_conditional_change_anims(0, 0.47, post_rects),
run_time = 3
)
self.wait(3)
self.play(*self.get_conditional_change_anims(
0, 0.97, post_rects
))
self.wait()
def tweak_prior(self):
her = self.her
post_rects = self.post_rects
self.play(
her.change_mode, "happy", Animation(her.glasses)
)
self.play(
*self.get_prior_change_anims(0.3, post_rects),
run_time = 2
)
self.wait(3)
self.play(
*self.get_prior_change_anims(1./22, post_rects),
run_time = 2
)
self.play(*list(map(FadeOut, [her, her.glasses])))
def compute_posterior(self):
prior_rects = self.get_prior_rectangles()
post_tex = self.posterior_tex
prior_rhs_group = self.get_prior_rhs_group()
fraction = TexMobject(
"{(0.045)", "(0.97)", "\\over",
"(0.995)", "(0.3)", "+", "(0.045)", "(0.97)}"
)
products = [
VGroup(*[
fraction.get_parts_by_tex(tex)[i]
for tex in tex_list
])
for i, tex_list in [
(0, ["0.045", "0.97"]),
(0, ["0.995", "0.3"]),
(1, ["0.045", "0.97"]),
]
]
for i in 0, 2:
products[i].set_color(prior_rects[0].get_color())
products[1].set_color(prior_rects[1].get_color())
fraction.scale(0.65)
fraction.to_corner(UP+RIGHT, buff = MED_SMALL_BUFF)
arrow_kwargs = {
"color" : WHITE,
"tip_length" : 0.15,
}
rhs = TexMobject("\\approx", "0.13")
rhs.scale(0.8)
rhs.next_to(post_tex, RIGHT)
to_rhs_arrow = Arrow(
fraction.get_bottom(), rhs.get_top(),
**arrow_kwargs
)
pre_top_rect_products = VGroup(
prior_rhs_group[0], self.top_conditional_rhs
)
pre_bottom_rect_products = VGroup(
prior_rhs_group[1], self.bottom_conditional_rhs
)
self.play(Indicate(prior_rects[0], scale_factor = 1))
self.play(*[
ReplacementTransform(
mob.copy(), term,
run_time = 2,
)
for mob, term in zip(
pre_top_rect_products, products[0]
)
])
self.play(Write(fraction.get_part_by_tex("over")))
for pair in zip(pre_top_rect_products, products[0]):
self.play(*list(map(Indicate, pair)))
self.wait()
self.wait()
self.play(Indicate(prior_rects[1], scale_factor = 1))
self.play(*[
ReplacementTransform(
mob.copy(), term,
run_time = 2,
)
for mob, term in zip(
pre_bottom_rect_products, products[1]
)
])
self.wait()
for pair in zip(pre_bottom_rect_products, products[1]):
self.play(*list(map(Indicate, pair)))
self.wait()
self.play(
Write(fraction.get_part_by_tex("+")),
ReplacementTransform(products[0].copy(), products[2])
)
self.wait()
self.play(ShowCreation(to_rhs_arrow))
self.play(Write(rhs))
self.wait(3)
######
def get_prior_labels(self, value):
p_str = "%0.3f"%value
q_str = "%0.3f"%(1-value)
labels = [
TexMobject(
"P(", s, self.double_heart_template, ")",
"= ", num
)
for s, num in (("", p_str), ("\\text{not }", q_str))
]
for label in labels:
label.scale(0.7)
self.insert_double_heart(label)
return labels
def get_prior_rhs_group(self):
labels = self.sample_space.horizontal_parts.labels
return VGroup(*[label[-1] for label in labels])
def get_conditional_label(self, value, given_flush = True):
label = TexMobject(
"P(", self.cash_string, "|",
"" if given_flush else "\\text{not }",
self.double_heart_template, ")",
"=", str(value)
)
self.insert_double_heart(label)
label.set_color_by_tex(self.cash_string, GREEN)
label.scale(0.7)
return label
def insert_double_heart(self, tex_mob):
double_heart = SuitSymbol("hearts")
double_heart.add(SuitSymbol("hearts"))
double_heart.arrange(RIGHT, buff = SMALL_BUFF)
double_heart.get_tex_string = lambda : self.double_heart_template
template = tex_mob.get_part_by_tex(self.double_heart_template)
double_heart.replace(template)
tex_mob.submobjects[tex_mob.index_of_part(template)] = double_heart
return tex_mob
def get_prior_change_anims(self, value, post_rects = None):
space = self.sample_space
parts = space.horizontal_parts
anims = self.get_horizontal_division_change_animations(
value, new_label_kwargs = {
"labels" : self.get_prior_labels(value)
}
)
if post_rects is not None:
anims += self.get_posterior_rectangle_change_anims(post_rects)
return anims
def get_conditional_change_anims(
self, sub_sample_space_index, value,
post_rects = None
):
given_flush = (sub_sample_space_index == 0)
label = self.get_conditional_label(value, given_flush)
return SampleSpaceScene.get_conditional_change_anims(
self, sub_sample_space_index, value, post_rects,
new_label_kwargs = {"labels" : [label]},
)
class BayesRuleInMemory(Scene):
def construct(self):
randy = Randolph()
randy.to_corner(DOWN+LEFT)
bubble = ThoughtBubble(height = 4)
bubble.pin_to(randy)
B = "\\text{Belief}"
D = "\\text{Data}"
rule = TexMobject(
"P(", B, "|", D, ")", "=",
"P(", "B", ")",
"{P(", D, "|", B, ")", "\\over", "P(", D, ")}"
)
rule.set_color_by_tex(B, RED)
rule.set_color_by_tex(D, GREEN)
rule.next_to(randy, RIGHT, LARGE_BUFF, UP)
rule.generate_target()
bubble.add_content(rule.target)
screen_rect = ScreenRectangle()
screen_rect.next_to(randy, UP+RIGHT)
self.add(randy)
self.play(
LaggedStart(FadeIn, rule),
randy.change, "erm", rule
)
self.play(Blink(randy))
self.play(
ShowCreation(bubble),
MoveToTarget(rule),
randy.change, "pondering",
)
self.wait()
self.play(rule.fade, 0.7, run_time = 2)
self.play(randy.change, "confused", rule)
self.play(Blink(randy))
self.wait(2)
self.play(
FadeOut(VGroup(bubble, rule)),
randy.change, "pondering", screen_rect,
)
self.play(
randy.look_at, screen_rect.get_right(),
ShowCreation(screen_rect),
)
self.wait(4)
class NextVideoWrapper(TeacherStudentsScene):
CONFIG = {
"title" : "Upcoming chapter: Bayesian networks"
}
def construct(self):
title = TextMobject(self.title)
title.scale(0.8)
title.to_edge(UP, buff = SMALL_BUFF)
screen = ScreenRectangle(height = 4)
screen.next_to(title, DOWN)
title.save_state()
title.shift(DOWN)
title.set_fill(opacity = 0)
self.play(
title.restore,
self.teacher.change, "raise_right_hand"
)
self.play(ShowCreation(screen))
self.change_student_modes(
*["pondering"]*3,
look_at_arg = screen
)
self.play(Animation(screen))
self.wait(5)
class BayesianNetworkPreview(Scene):
def construct(self):
self.add_network()
self.show_propogation(self.network.nodes[0])
self.show_propogation(self.network.nodes[-1])
def add_network(self):
radius = MED_SMALL_BUFF
node = Circle(color = WHITE, radius = radius)
node.shift(2*DOWN)
nodes = VGroup(*[
node.copy().shift(x*RIGHT + y*UP)
for x, y in [
(-1, 0),
(1, 0),
(-2, 2),
(0, 2),
(2, 2),
(-2, 4),
(0, 4),
]
])
for node in nodes:
node.children = VGroup()
node.parents = VGroup()
node.outgoing_edges = VGroup()
edge_index_pairs = [
(2, 0),
(3, 0),
(3, 1),
(4, 1),
(5, 2),
(6, 3),
]
edges = VGroup()
for i1, i2 in edge_index_pairs:
n1, n2 = nodes[i1], nodes[i2]
edge = Arrow(
n1.get_center(),
n2.get_center(),
buff = radius,
color = WHITE,
)
n1.outgoing_edges.add(edge)
edges.add(edge)
n1.children.add(n2)
n2.parents.add(n1)
network = VGroup(nodes, edges)
network.nodes = nodes
network.edges = edges
self.add(network)
self.network = network
def show_propogation(self, node):
self.set_network_fills()
all_ghosts = VGroup()
curr_nodes = [node]
covered_nodes = set()
self.play(GrowFromCenter(node.fill))
self.remove(node.fill)
while curr_nodes:
next_nodes = set([])
anims = []
for node in curr_nodes:
node.ghost = node.fill.copy().fade()
self.add(node.ghost)
all_ghosts.add(node.ghost)
connected_nodes = [n for n in it.chain(node.children, node.parents) if n not in covered_nodes]
for next_node in connected_nodes:
if next_node in covered_nodes:
continue
next_nodes.add(next_node)
anims.append(Transform(
node.fill.copy(), next_node.fill,
remover = True
))
if len(connected_nodes) == 0:
anims.append(FadeOut(node.fill))
if anims:
self.play(*anims)
covered_nodes.update(curr_nodes)
curr_nodes = list(next_nodes)
self.wait()
self.play(FadeOut(all_ghosts))
def set_network_fills(self):
for node in self.network.nodes:
node.fill = self.get_fill(node)
def get_fill(self, node):
fill = node.copy()
fill.set_fill(YELLOW, 1)
fill.set_stroke(width = 0)
return fill
class GeneralizeBayesRule(SampleSpaceScene):
def construct(self):
self.add_sample_space()
self.add_title()
self.add_posterior_rectangles()
self.add_bayes_rule()
self.talk_through_terms()
self.name_likelihood()
self.dont_memorize()
self.show_space_restriction()
def add_sample_space(self):
sample_space = SampleSpace(
full_space_config = {
"height" : 3,
"width" : 3,
"fill_opacity" : 0
}
)
sample_space.divide_horizontally(0.4)
sample_space.horizontal_parts.set_fill(opacity = 0)
labels = [
TexMobject("P(", "B", ")"),
TexMobject("P(\\text{not }", "B", ")"),
]
for label in labels:
label.scale(0.7)
self.color_label(label)
sample_space.get_side_braces_and_labels(labels)
sample_space.add_braces_and_labels()
parts = sample_space.horizontal_parts
values = [0.8, 0.4]
given_strs = ["", "\\text{not }"]
color_pairs = [(GREEN, BLUE), (GREEN_E, BLUE_E)]
vects = [UP, DOWN]
for tup in zip(parts, values, given_strs, color_pairs, vects):
part, value, given_str, colors, vect = tup
part.divide_vertically(value, colors = colors)
part.vertical_parts.set_fill(opacity = 0.8)
label = TexMobject(
"P(", "I", "|", given_str, "B", ")"
)
label.scale(0.7)
self.color_label(label)
part.get_subdivision_braces_and_labels(
part.vertical_parts, [label], vect
)
sample_space.add(
part.vertical_parts.braces,
part.vertical_parts.labels,
)
sample_space.to_edge(LEFT)
self.add(sample_space)
self.sample_space = sample_space
def add_title(self):
title = TextMobject(
"Updating", "Beliefs", "from new", "Information"
)
self.color_label(title)
title.scale(0.8)
title.to_corner(UP+LEFT)
self.add(title)
def add_posterior_rectangles(self):
prior_rects = self.get_prior_rectangles()
post_rects = self.get_posterior_rectangles()
label = TexMobject("P(", "B", "|", "I", ")")
label.scale(0.7)
self.color_label(label)
braces, labels = self.get_posterior_rectangle_braces_and_labels(
post_rects, [label]
)
self.play(ReplacementTransform(
prior_rects.copy(), post_rects,
run_time = 2
))
self.play(
GrowFromCenter(braces),
Write(label)
)
self.post_rects = post_rects
self.posterior_tex = label
def add_bayes_rule(self):
rule = TexMobject(
"=", "{P(", "B", ")", "P(", "I", "|", "B", ")",
"\\over", "P(", "I", ")}",
)
self.color_label(rule)
rule.scale(0.7)
rule.next_to(self.posterior_tex, RIGHT)
bayes_rule_words = TextMobject("Bayes' rule")
bayes_rule_words.next_to(VGroup(*rule[1:]), UP, LARGE_BUFF)
bayes_rule_words.shift_onto_screen()
self.play(FadeIn(rule))
self.play(Write(bayes_rule_words))
self.wait(2)
self.bayes_rule_words = bayes_rule_words
self.bayes_rule = rule
def talk_through_terms(self):
prior = self.sample_space.horizontal_parts.labels[0]
posterior = self.posterior_tex
prior_target = VGroup(*self.bayes_rule[1:4])
likelihood = VGroup(*self.bayes_rule[4:9])
P_I = VGroup(*self.bayes_rule[-3:])
prior_word = TextMobject("Prior")
posterior_word = TextMobject("Posterior")
words = [prior_word, posterior_word]
for word in words:
word.set_color(YELLOW)
word.scale(0.7)
prior_rect = SurroundingRectangle(prior)
posterior_rect = SurroundingRectangle(posterior)
for rect in prior_rect, posterior_rect:
rect.set_stroke(YELLOW, 2)
prior_word.next_to(prior, UP, LARGE_BUFF)
posterior_word.next_to(posterior, DOWN, LARGE_BUFF)
for word in words:
word.shift_onto_screen()
prior_arrow = Arrow(
prior_word.get_bottom(), prior.get_top(),
tip_length = 0.15
)
posterior_arrow = Arrow(
posterior_word.get_top(), posterior.get_bottom(),
tip_length = 0.15
)
self.play(
Write(prior_word),
ShowCreation(prior_arrow),
ShowCreation(prior_rect),
)
self.wait()
self.play(Transform(
prior.copy(), prior_target,
run_time = 2,
path_arc = -np.pi/3,
remover = True,
))
self.wait()
parts = self.sample_space[0].vertical_parts
self.play(
Indicate(likelihood),
Indicate(parts.labels),
Indicate(parts.braces),
)
self.wait()
self.play(Indicate(P_I))
self.play(FocusOn(self.sample_space[0][0]))
for i in range(2):
self.play(Indicate(
self.sample_space[i][0],
scale_factor = 1
))
self.wait()
self.play(
Write(posterior_word),
ShowCreation(posterior_arrow),
ShowCreation(posterior_rect),
)
self.prior_label = VGroup(prior_word, prior_arrow, prior_rect)
self.posterior_label = VGroup(posterior_word, posterior_arrow, posterior_rect)
self.likelihood = likelihood
def name_likelihood(self):
likelihoods = [
self.sample_space[0].vertical_parts.labels[0],
self.likelihood
]
rects = [
SurroundingRectangle(mob, buff = SMALL_BUFF)
for mob in likelihoods
]
name = TextMobject("Likelihood")
name.scale(0.7)
name.next_to(self.posterior_tex, UP, 1.5*LARGE_BUFF)
arrows = [
Arrow(
name, rect.get_edge_center(vect),
tip_length = 0.15
)
for rect, vect in zip(rects, [RIGHT, UP])
]
VGroup(name, *arrows+rects).set_color(YELLOW)
morty = Mortimer()
morty.scale(0.5)
morty.next_to(rects[1], UP, buff = 0)
morty.shift(SMALL_BUFF*RIGHT)
self.play(
self.bayes_rule_words.to_edge, UP,
Write(name),
*list(map(ShowCreation, arrows+rects))
)
self.wait()
self.play(FadeIn(morty))
self.play(morty.change, "confused", name)
self.play(Blink(morty))
self.play(morty.look, DOWN)
self.wait()
self.play(morty.look_at, name)
self.play(Blink(morty))
self.play(morty.change, "shruggie")
self.play(FadeOut(VGroup(name, *arrows+rects)))
self.play(FadeOut(morty))
self.play(FadeOut(self.posterior_label))
self.play(FadeOut(self.prior_label))
def dont_memorize(self):
rule = VGroup(*self.bayes_rule[1:])
word = TextMobject("Memorize")
word.scale(0.7)
word.next_to(rule, DOWN)
cross = VGroup(
Line(UP+LEFT, DOWN+RIGHT),
Line(UP+RIGHT, DOWN+LEFT),
)
cross.set_stroke(RED, 6)
cross.replace(word, stretch = True)
self.play(Write(word))
self.wait()
self.play(ShowCreation(cross))
self.wait()
self.play(FadeOut(VGroup(cross, word)))
self.play(FadeOut(self.bayes_rule))
self.play(
FadeOut(self.post_rects),
FadeOut(self.post_rects.braces),
FadeOut(self.post_rects.labels),
)
def show_space_restriction(self):
prior_rects = self.get_prior_rectangles()
non_I_rects = VGroup(*[
self.sample_space[i][1]
for i in range(2)
])
post_rects = self.post_rects
self.play(non_I_rects.fade, 0.8)
self.play(LaggedStart(
ApplyMethod,
prior_rects,
lambda m : (m.set_color, YELLOW),
rate_func = there_and_back,
lag_ratio = 0.7
))
self.wait(2)
self.play(ReplacementTransform(
prior_rects.copy(), post_rects,
run_time = 2
))
self.play(*list(map(FadeIn, [
post_rects.braces, post_rects.labels
])))
self.wait()
self.play(*self.get_conditional_change_anims(1, 0.2, post_rects))
self.play(*self.get_conditional_change_anims(0, 0.6, post_rects))
self.wait()
self.play(*it.chain(
self.get_division_change_animations(
self.sample_space,
self.sample_space.horizontal_parts,
0.1
),
self.get_posterior_rectangle_change_anims(post_rects)
))
self.wait(3)
####
def color_label(self, label):
label.set_color_by_tex("B", RED)
label.set_color_by_tex("I", GREEN)
class MoreExamples(TeacherStudentsScene):
def construct(self):
self.teacher_says("More examples!", target_mode = "hooray")
self.change_student_modes(*["hooray"]*3)
self.wait(2)
class MusicExample(SampleSpaceScene, PiCreatureScene):
def construct(self):
self.introduce_musician()
self.add_prior()
self.record_track()
self.add_bottom_conditionl()
self.friend_gives_compliment()
self.friends_dont_like()
self.false_compliment()
self.add_top_conditionl()
self.get_positive_review()
self.restrict_space()
self.show_posterior_rectangles()
self.show_prior_rectangle_areas()
self.show_posterior_probability()
self.intuition_of_positive_feedback()
self.make_friends_honest()
self.fade_out_post_rect()
self.get_negative_feedback()
self.compare_prior_to_post_given_negative()
self.intuition_of_negative_feedback()
def introduce_musician(self):
randy = self.pi_creature
randy.change_mode("soulful_musician")
randy.arms = randy.get_arm_copies()
guitar = randy.guitar = Guitar()
guitar.move_to(randy)
guitar.shift(0.31*RIGHT + 0.6*UP)
randy.change_mode("plain")
self.play(
randy.change_mode, "soulful_musician",
path_arc = np.pi/6,
)
self.play(
Animation(randy),
DrawBorderThenFill(guitar),
Animation(randy.arms)
)
randy.add(guitar, randy.arms)
self.wait()
self.play_notes(guitar)
self.change_pi_creature_with_guitar("concerned_musician")
self.wait(2)
self.play(
randy.scale, 0.7,
randy.to_corner, UP+LEFT,
)
self.play_notes(guitar)
def add_prior(self):
sample_space = SampleSpace()
sample_space.shift(DOWN)
sample_space.divide_horizontally(0.8, colors = [MAROON_D, BLUE_E])
labels = VGroup(
TexMobject("P(S) = ", "0.8"),
TexMobject("P(\\text{not } S) = ", "0.2"),
)
labels.scale(0.7)
braces, labels = sample_space.get_side_braces_and_labels(labels)
VGroup(sample_space, braces, labels).to_edge(LEFT)
words = list(map(TextMobject, [
"Blunt honesty", "Some confidence"
]))
for word, part in zip(words, sample_space.horizontal_parts):
word.scale(0.6)
word.move_to(part)
self.play(LaggedStart(FadeIn, sample_space, run_time = 1))
self.play(*list(map(GrowFromCenter, braces)))
for label in labels:
self.play(Write(label, run_time = 2))
self.wait()
for word, mode in zip(words, ["maybe", "soulful_musician"]):
self.play(LaggedStart(FadeIn, word, run_time = 1))
self.change_pi_creature_with_guitar(mode)
self.wait()
self.wait()
self.play(*list(map(FadeOut, words)))
self.sample_space = sample_space
def record_track(self):
randy = self.pi_creature
friends = VGroup(*[
PiCreature(mode = "happy", color = color).flip()
for color in (BLUE_B, GREY_BROWN, MAROON_E)
])
friends.scale(0.6)
friends.arrange(RIGHT)
friends.next_to(randy, RIGHT, LARGE_BUFF, DOWN)
friends.to_edge(RIGHT)
for friend in friends:
friend.look_at(randy.eyes)
headphones = VGroup(*list(map(Headphones, friends)))
self.play(FadeIn(friends))
self.pi_creatures.add(*friends)
self.play(
FadeIn(headphones),
Animation(friends)
)
self.play_notes(randy.guitar)
self.play(LaggedStart(
ApplyMethod, friends,
lambda pi : (pi.change, "hooray"),
run_time = 2,
))
self.friends = friends
self.headphones = headphones
def add_bottom_conditionl(self):
p = 0.99
bottom_part = self.sample_space[1]
bottom_part.divide_vertically(p, colors = [GREEN_E, YELLOW])
label = self.get_conditional_label(p, False)
braces, labels = bottom_part.get_bottom_braces_and_labels([label])
brace = braces[0]
self.play(FadeIn(bottom_part.vertical_parts))
self.play(GrowFromCenter(brace))
self.play(Write(label))
self.wait()
def friend_gives_compliment(self):
friends = self.friends
bubble = SpeechBubble(
height = 1.25, width = 3, direction = RIGHT,
fill_opacity = 0,
)
content = TextMobject("Phenomenal!")
content.scale(0.75)
bubble.add_content(content)
VGroup(bubble, content).next_to(friends, LEFT, SMALL_BUFF)
VGroup(bubble, content).to_edge(UP, SMALL_BUFF)
self.play(LaggedStart(
ApplyMethod, friends,
lambda pi : (pi.change_mode, "conniving")
))
self.wait()
self.play(
ShowCreation(bubble),
Write(bubble.content, run_time = 1),
ApplyMethod(friends[0].change_mode, "hooray"),
LaggedStart(
ApplyMethod, VGroup(*friends[1:]),
lambda pi : (pi.change_mode, "happy")
),
)
self.wait(2)
self.play(*list(map(FadeOut, [bubble, content])))
def friends_dont_like(self):
friends = self.friends
pi1, pi2, pi3 = friends
for friend in friends:
friend.generate_target()
pi1.target.change("guilty", pi2.eyes)
pi2.target.change("hesitant", pi1.eyes)
pi3.target.change("pondering", pi2.eyes)
self.play(LaggedStart(
MoveToTarget, friends
))
self.change_pi_creature_with_guitar("concerned_musician")
self.wait()
def false_compliment(self):
friend = self.friends[0]
bubble = SpeechBubble(
height = 1.25, width = 4.5, direction = RIGHT,
fill_opacity = 0,
)
content = TextMobject("The beat was consistent.")
content.scale(0.75)
bubble.add_content(content)
VGroup(bubble, content).next_to(friend, LEFT, SMALL_BUFF)
VGroup(bubble, content).to_edge(UP, SMALL_BUFF)
self.play(
friend.change_mode, "maybe",
ShowCreation(bubble),
Write(content)
)
self.change_pi_creature_with_guitar("happy")
self.wait()
self.play(*list(map(FadeOut, [bubble, content])))
self.bubble = bubble
def add_top_conditionl(self):
p = 0.9
top_part = self.sample_space[0]
top_part.divide_vertically(p, colors = [TEAL_E, RED_E])
label = self.get_conditional_label(p, True)
braces, labels = top_part.get_top_braces_and_labels([label])
brace = braces[0]
self.play(FadeIn(top_part.vertical_parts))
self.play(GrowFromCenter(brace))
self.play(Write(label, run_time = 2))
self.wait()
def get_positive_review(self):
friends = self.friends
self.change_pi_creature_with_guitar(
"soulful_musician",
LaggedStart(
ApplyMethod, friends,
lambda pi : (pi.change, "happy"),
run_time = 1,
)
)
self.play_notes(self.pi_creature.guitar)
def restrict_space(self):
positive_space, negative_space = [
VGroup(*[
self.sample_space[i][j]
for i in range(2)
])
for j in range(2)
]
negative_space.save_state()
self.play(negative_space.fade, 0.8)
self.play(LaggedStart(
ApplyMethod, positive_space,
lambda m : (m.set_color, YELLOW),
rate_func = there_and_back,
run_time = 2,
lag_ratio = 0.7,
))
self.wait()
self.negative_space = negative_space
def show_posterior_rectangles(self):
prior_rects = self.get_prior_rectangles()
post_rects = self.get_posterior_rectangles()
label = TexMobject("P(S | ", "\\checkmark", ")")
label.scale(0.7)
label.set_color_by_tex("\\checkmark", GREEN)
braces, labels = self.get_posterior_rectangle_braces_and_labels(
post_rects, [label]
)
brace = braces[0]
self.play(ReplacementTransform(
prior_rects.copy(), post_rects,
run_time = 2
))
self.play(GrowFromCenter(brace))
self.play(Write(label))
self.wait()
self.post_rects = post_rects
self.post_tex = label
def show_prior_rectangle_areas(self):
prior_rects = self.get_prior_rectangles()
products = VGroup(
TexMobject("(", "0.8", ")(", "0.9", ")"),
TexMobject("(", "0.2", ")(", "0.99", ")"),
)
top_product, bottom_product = products
for product, rect in zip(products, prior_rects):
product.scale(0.7)
product.move_to(rect)
side_labels = self.sample_space.horizontal_parts.labels
top_labels = self.sample_space[0].vertical_parts.labels
bottom_labels = self.sample_space[1].vertical_parts.labels
self.play(
ReplacementTransform(
side_labels[0][-1].copy(),
top_product[1],
),
ReplacementTransform(
top_labels[0][-1].copy(),
top_product[3],
),
Write(VGroup(*top_product[::2]))
)
self.wait(2)
self.play(
ReplacementTransform(
side_labels[1][-1].copy(),
bottom_product[1],
),
ReplacementTransform(
bottom_labels[0][-1].copy(),
bottom_product[3],
),
Write(VGroup(*bottom_product[::2]))
)
self.wait(2)
self.products = products
def show_posterior_probability(self):
post_tex = self.post_tex
rhs = TexMobject("\\approx", "0.78")
rhs.scale(0.7)
rhs.next_to(post_tex, RIGHT)
ratio = TexMobject(
"{(0.8)(0.9)", "\\over",
"(0.8)(0.9)", "+", "(0.2)(0.99)}"
)
ratio.scale(0.6)
ratio.next_to(VGroup(post_tex, rhs), DOWN, LARGE_BUFF)
ratio.to_edge(RIGHT)
arrow_kwargs = {
"tip_length" : 0.15,
"color" : WHITE,
"buff" : 2*SMALL_BUFF,
}
to_ratio_arrow = Arrow(
post_tex.get_bottom(), ratio.get_top(), **arrow_kwargs
)
to_rhs_arrow = Arrow(
ratio.get_top(), rhs[1].get_bottom(), **arrow_kwargs
)
prior_rects = self.get_prior_rectangles()
self.play(
ShowCreation(to_ratio_arrow),
FadeIn(ratio)
)
self.wait(2)
for mob in prior_rects, prior_rects[0]:
self.play(
mob.set_color, YELLOW,
Animation(self.products),
rate_func = there_and_back,
run_time = 2
)
self.wait()
self.wait()
self.play(ShowCreation(to_rhs_arrow))
self.play(Write(rhs, run_time = 1))
self.wait(2)
self.post_rhs = rhs
self.ratio_group = VGroup(ratio, to_ratio_arrow, to_rhs_arrow)
def intuition_of_positive_feedback(self):
friends = self.friends
prior_num = self.sample_space.horizontal_parts.labels[0][-1]
prior_num_ghost = prior_num.copy().set_fill(opacity = 0.5)
post_num = self.post_rhs[-1]
prior_rect = SurroundingRectangle(prior_num)
post_rect = SurroundingRectangle(post_num)
self.play(ShowCreation(prior_rect))
self.play(Transform(
prior_num_ghost, post_num,
remover = True,
path_arc = -np.pi/6,
run_time = 2,
))
self.play(ShowCreation(post_rect))
self.wait(2)
for mode, time in ("shruggie", 2), ("hesitant", 0):
self.play(LaggedStart(
ApplyMethod, friends,
lambda pi : (pi.change, mode),
run_time = 2,
))
self.wait(time)
self.play(*list(map(FadeOut, [
prior_rect, post_rect,
self.ratio_group, self.post_rhs
])))
self.prior_num_rect = prior_rect
def make_friends_honest(self):
post_rects = self.post_rects
self.play(FadeOut(self.products))
for value in 0.5, 0.1, 0.9:
label = self.get_conditional_label(value)
self.play(*self.get_top_conditional_change_anims(
value, post_rects,
new_label_kwargs = {"labels" : [label]},
), run_time = 2)
self.wait(2)
def fade_out_post_rect(self):
self.play(*list(map(FadeOut, [
self.post_rects,
self.post_rects.braces,
self.post_rects.labels,
])))
self.play(self.negative_space.restore)
def get_negative_feedback(self):
friends = self.friends
old_prior_rects = self.get_prior_rectangles()
for part in self.sample_space.horizontal_parts:
part.vertical_parts.submobjects.reverse()
new_prior_rects = self.get_prior_rectangles()
post_rects = self.get_posterior_rectangles()
label = TexMobject(
"P(S | \\text{not } ", "\\checkmark", ")",
"\\approx", "0.98"
)
label.scale(0.7)
label.set_color_by_tex("\\checkmark", GREEN)
braces, labels = self.get_posterior_rectangle_braces_and_labels(
post_rects, [label]
)
brace = braces[0]
self.play(old_prior_rects.fade, 0.8)
self.play(LaggedStart(
ApplyMethod, friends,
lambda pi : (pi.change, "pondering", post_rects),
run_time = 1
))
self.wait()
self.play(ReplacementTransform(
new_prior_rects.copy(), post_rects,
run_time = 2
))
self.play(GrowFromCenter(brace))
self.wait(2)
self.play(Write(label))
self.wait(3)
self.post_rects = post_rects
def compare_prior_to_post_given_negative(self):
post_num = self.post_rects.labels[0][-1]
post_num_rect = SurroundingRectangle(post_num)
self.play(ShowCreation(self.prior_num_rect))
self.wait()
self.play(ShowCreation(post_num_rect))
self.wait()
self.post_num_rect = post_num_rect
def intuition_of_negative_feedback(self):
friends = self.friends
randy = self.pi_creature
bubble = self.bubble
modes = ["sassy", "pleading", "horrified"]
for friend, mode in zip(friends, modes):
friend.generate_target()
friend.target.change(mode, randy.eyes)
content = TextMobject("Horrible. Just horrible.")
content.scale(0.6)
bubble.add_content(content)
self.play(*list(map(MoveToTarget, friends)))
self.play(
ShowCreation(bubble),
Write(bubble.content)
)
self.change_pi_creature_with_guitar("sad")
self.wait()
self.change_pi_creature_with_guitar("concerned_musician")
self.wait(3)
######
def create_pi_creature(self):
randy = Randolph()
randy.left_arm_range = [.36, .45]
self.randy = randy
return randy
def get_conditional_label(self, value, given_suck = True):
positive_str = "\\checkmark"
label = TexMobject(
"P(", positive_str, "|",
"" if given_suck else "\\text{not }",
"S", ")",
"=", str(value)
)
label.set_color_by_tex(positive_str, GREEN)
label.scale(0.7)
return label
def change_pi_creature_with_guitar(self, target_mode, *added_anims):
randy = self.pi_creature
randy.remove(randy.arms, randy.guitar)
target = randy.copy()
target.change_mode(target_mode)
target.arms = target.get_arm_copies()
target.guitar = randy.guitar.copy()
for pi in randy, target:
pi.add(pi.guitar, pi.arms)
self.play(Transform(randy, target), *added_anims)
def play_notes(self, guitar):
note = SVGMobject(file_name = "8th_note")
note.set_height(0.5)
note.set_stroke(width = 0)
note.set_fill(BLUE, 1)
note.move_to(guitar)
note.shift(MED_SMALL_BUFF*(DOWN+2*LEFT))
notes = VGroup(*[note.copy() for x in range(10)])
sine_wave = FunctionGraph(np.sin, x_min = -5, x_max = 5)
sine_wave.scale(0.75)
sine_wave.rotate(np.pi/6, about_point = ORIGIN)
sine_wave.shift(
notes.get_center() - \
sine_wave.point_from_proportion(0)
)
self.play(LaggedStart(
MoveAlongPath, notes,
lambda n : (n, sine_wave),
path_arc = np.pi/2,
run_time = 4,
lag_ratio = 0.5,
rate_func = lambda t : t,
))
class FinalWordsOnRule(SampleSpaceScene):
def construct(self):
self.add_sample_space()
self.add_uses()
self.tweak_values()
def add_sample_space(self):
sample_space = self.sample_space = SampleSpace()
prior = 0.2
top_conditional = 0.8
bottom_condional = 0.3
sample_space.divide_horizontally(prior)
sample_space[0].divide_vertically(
top_conditional, colors = [GREEN, RED]
)
sample_space[1].divide_vertically(
bottom_condional, colors = [GREEN_E, RED_E]
)
B = "\\text{Belief}"
D = "\\text{Data}"
P_B = TexMobject("P(", B, ")")
P_D_given_B = TexMobject("P(", D, "|", B, ")")
P_D_given_not_B = TexMobject(
"P(", D, "|", "\\text{not }", B, ")"
)
P_B_given_D = TexMobject("P(", B, "|", D, ")")
labels = VGroup(P_B, P_D_given_B, P_D_given_not_B, P_B_given_D)
for label in labels:
label.scale(0.7)
label.set_color_by_tex(B, BLUE)
label.set_color_by_tex(D, GREEN)
prior_rects = self.get_prior_rectangles()
post_rects = self.get_posterior_rectangles()
for i in range(2):
sample_space[i][1].fade(0.7)
braces = VGroup()
bs, ls = sample_space.get_side_braces_and_labels([P_B])
braces.add(*bs)
bs, ls = sample_space[0].get_top_braces_and_labels([P_D_given_B])
braces.add(*bs)
bs, ls = sample_space[1].get_bottom_braces_and_labels([P_D_given_not_B])
braces.add(*bs)
bs, ls = self.get_posterior_rectangle_braces_and_labels(
post_rects, [P_B_given_D]
)
braces.add(*bs)
group = VGroup(sample_space, braces, labels, post_rects)
group.to_corner(DOWN + LEFT)
self.add(group)
self.post_rects = post_rects
def add_uses(self):
uses = TextMobject(
"Machine learning, ",
"scientific inference, $\\dots$",
)
uses.to_edge(UP)
for use in uses:
self.play(Write(use, run_time = 2))
self.wait()
def tweak_values(self):
post_rects = self.post_rects
new_value_lists = [
(0.85, 0.1, 0.11),
(0.3, 0.9, 0.4),
(0.97, 0.3, 1./22),
]
for new_values in new_value_lists:
for i, value in zip(list(range(2)), new_values):
self.play(*self.get_conditional_change_anims(
i, value, post_rects
))
self.wait()
self.play(*it.chain(
self.get_horizontal_division_change_animations(new_values[-1]),
self.get_posterior_rectangle_change_anims(post_rects)
))
self.wait()
self.wait(2)
class FootnoteWrapper(NextVideoWrapper):
CONFIG = {
"title" : "Thoughts on the classic Bayes example"
}
class PatreonThanks(PatreonThanks):
CONFIG = {
"specific_patrons" : [
"Ali Yahya",
"Burt Humburg",
"CrypticSwarm",
"Juan Benet",
"Mark Zollo",
"James Park",
"Erik Sundell",
"Yana Chernobilsky",
"Kaustuv DeBiswas",
"Kathryn Schmiedicke",
"Karan Bhargava",
"Ankit Agarwal",
"Yu Jun",
"Dave Nicponski",
"Damion Kistler",
"Markus Persson",
"Yoni Nazarathy",
"Ed Kellett",
"Joseph John Cox",
"Dan Buchoff",
"Luc Ritchie",
"Michael McGuffin",
"John Haley",
"Mourits de Beer",
"Ankalagon",
"Eric Lavault",
"Tomohiro Furusawa",
"Boris Veselinovich",
"Julian Pulgarin",
"Jeff Linse",
"Cooper Jones",
"Ryan Dahl",
"Mark Govea",
"Robert Teed",
"Jason Hise",
"Meshal Alshammari",
"Bernd Sing",
"Nils Schneider",
"James Thornton",
"Mustafa Mahdi",
"Mathew Bramson",
"Jerry Ling",
"Vecht",
"Shimin Kuang",
"Rish Kundalia",
"Achille Brighton",
"Ripta Pasay",
]
}
class Thumbnail(SampleSpaceScene):
def construct(self):
title = TextMobject("Bayes' rule")
title.scale(2)
title.to_edge(UP)
self.add(title)
prior_label = TexMobject("P(", "H", ")")
post_label = TexMobject("P(", "H", "|", "D", ")")
for label in prior_label, post_label:
label.set_color_by_tex("H", YELLOW)
label.set_color_by_tex("D", GREEN)
label.scale(1.5)
sample_space = self.get_sample_space()
sample_space.set_height(4.5)
sample_space.divide_horizontally(0.3)
sample_space[0].divide_vertically(0.8, colors = [GREEN, BLUE])
sample_space[1].divide_vertically(0.3, colors = [GREEN_E, BLUE_E])
sample_space.get_side_braces_and_labels([prior_label])
sample_space.add_braces_and_labels()
post_rects = self.get_posterior_rectangles()
group = self.get_posterior_rectangle_braces_and_labels(
post_rects, [post_label]
)
post_rects.add(group)
VGroup(sample_space, post_rects).next_to(title, DOWN, LARGE_BUFF)
self.add(sample_space, post_rects)