from manimlib.imports import * from active_projects.eop.bayes import IntroducePokerHand SICKLY_GREEN = "#9BBD37" class BayesClassicExampleOpeningQuote(OpeningQuote): CONFIG = { "quote" : [ "When faced with a difficult question, we often " \ "answer an easier one instead, usually without " \ "noticing the substitution.", ], "author" : "Daniel Kahneman", } class Introduction(TeacherStudentsScene): def construct(self): self.hold_up_example() self.write_counter_intuitive() self.comment_on_crazy_results() self.put_it_first() self.swap_example_order() self.other_culprit() def hold_up_example(self): everyone = self.get_pi_creatures() self.teacher.change_mode("raise_right_hand") rect = ScreenRectangle() rect.set_stroke(YELLOW, 2) rect.to_edge(UP) randy = Randolph() randy.scale(0.7) name = TextMobject(r""" Bayes' theorem \\ disease example """) name.next_to(rect.get_top(), DOWN, SMALL_BUFF) randy.next_to(name, DOWN) example = VGroup(rect, name, randy) self.remove(everyone) self.add(name, randy) self.play( randy.change_mode, "sick", randy.set_color, SICKLY_GREEN ) self.play(ShowCreation(rect)) self.play( FadeIn(everyone), example.scale, 0.5, example.next_to, self.teacher.get_corner(UP+LEFT), UP, ) self.wait(2) self.example = example def write_counter_intuitive(self): bayes = TextMobject("Bayes") arrow = TexMobject("\\leftrightarrow") intuition = TextMobject("Intuition") group = VGroup(bayes, arrow, intuition) group.arrange(RIGHT, buff = SMALL_BUFF) group.scale(0.8) group.next_to(self.example, UP, buff = SMALL_BUFF) group.shift_onto_screen() cross = VGroup( Line(UP+LEFT, DOWN+RIGHT), Line(UP+RIGHT, DOWN+LEFT), ) cross.replace(arrow, stretch = True) cross.set_stroke(RED, 6) group.add(cross) self.play(*list(map(FadeIn, [bayes, intuition]))) self.play(Write(arrow)) self.play(ShowCreation(cross)) self.change_student_modes(*["confused"]*3) self.wait(2) self.bayes_to_intuition = group def comment_on_crazy_results(self): disease_group = VGroup(self.example, self.bayes_to_intuition) disease_group.save_state() self.teacher_says( "Who doesn't love \\\\ crazy results?", target_mode = "hooray", added_anims = [disease_group.to_corner, UP+LEFT] ) self.wait(2) self.disease_group = disease_group def put_it_first(self): poker_example = self.get_poker_example() music_example = self.get_music_example() disease_group = self.disease_group self.play( disease_group.restore, disease_group.to_edge, LEFT, RemovePiCreatureBubble( self.teacher, target_mode = "hesitant" ) ) self.change_student_modes( *["pondering"]*3, look_at_arg = disease_group ) poker_example.next_to(self.example, RIGHT) music_example.next_to(poker_example, RIGHT) examples = VGroup(poker_example, music_example) brace = Brace(examples, UP) bayes_to_intuition = VGroup(*list(map(TextMobject, [ "Bayes", "$\\leftrightarrow$", "Intuition" ]))) bayes_to_intuition.arrange(RIGHT, buff = SMALL_BUFF) bayes_to_intuition.next_to(brace, UP, SMALL_BUFF) check = TexMobject("\\checkmark") check.set_color(GREEN) check.next_to(bayes_to_intuition[1], UP, SMALL_BUFF) for example in examples: self.play(FadeIn(example)) self.wait() self.play(GrowFromCenter(brace)) self.play(FadeIn(bayes_to_intuition)) self.play(Write(check)) self.wait(2) self.intuitive_examples = VGroup( examples, brace, bayes_to_intuition, check ) def swap_example_order(self): intuitive_examples = self.intuitive_examples disease_group = VGroup( self.example, self.bayes_to_intuition ) self.play( disease_group.next_to, self.teacher.get_corner(UP+LEFT), UP, disease_group.shift, LEFT, intuitive_examples.scale, 0.7, intuitive_examples.to_corner, UP+LEFT, self.teacher.change_mode, "sassy" ) def other_culprit(self): bayes = self.bayes_to_intuition[0] something_else = TextMobject("Something else") something_else.set_color(YELLOW) something_else.set_height(bayes.get_height()) something_else.move_to(bayes, RIGHT) new_group = VGroup( something_else, *self.bayes_to_intuition[1:] ) self.play(bayes.to_edge, UP) self.play(Write(something_else)) self.play(new_group.next_to, self.example, UP, SMALL_BUFF) self.change_student_modes( "erm", "confused", "hesitant", added_anims = [self.teacher.change_mode, "happy"] ) self.wait(3) ##### def get_poker_example(self): rect = self.get_example_rect() values = IntroducePokerHand.CONFIG["community_card_values"] community_cards = VGroup(*list(map(PlayingCard, values))) community_cards.arrange(RIGHT) 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) cards = VGroup(deck, community_cards) cards.set_width(rect.get_width() - 2*SMALL_BUFF) cards.next_to(rect.get_bottom(), UP, MED_SMALL_BUFF) probability = TexMobject( "P(", "\\text{Flush}", "|", "\\text{High bet}", ")" ) probability.set_color_by_tex("Flush", RED) probability.set_color_by_tex("High bet", GREEN) probability.scale(0.5) probability.next_to(rect.get_top(), DOWN) return VGroup(rect, probability, cards) def get_music_example(self): rect = self.get_example_rect() musician = Randolph(mode = "soulful_musician") musician.left_arm_range = [.36, .45] musician.arms = musician.get_arm_copies() guitar = musician.guitar = Guitar() guitar.move_to(musician) guitar.shift(0.31*RIGHT + 0.6*UP) musician.add(guitar, musician.arms) musician.set_height(0.7*rect.get_height()) musician.next_to(rect.get_bottom(), UP, SMALL_BUFF) probability = TexMobject( "P(", "\\text{Suck }", "|", "\\text{ Good review}", ")" ) probability.set_color_by_tex("Suck", RED) probability.set_color_by_tex("Good", GREEN) probability.scale(0.5) probability.next_to(rect.get_top(), DOWN) return VGroup(rect, musician, probability) def get_example_rect(self): rect = self.example[0].copy() rect.set_color(WHITE) return rect class OneInOneThousandHaveDisease(Scene): def construct(self): title = TextMobject("1 in 1{,}000") title.to_edge(UP) creature = PiCreature() all_creatures = VGroup(*[ VGroup(*[ creature.copy() for y in range(25) ]).arrange(DOWN, SMALL_BUFF) for x in range(40) ]).arrange(RIGHT, SMALL_BUFF) all_creatures.set_width(FRAME_WIDTH - 4) all_creatures.next_to(title, DOWN) randy = all_creatures[0][0] all_creatures[0].remove(randy) randy.change_mode("sick") randy.set_color(SICKLY_GREEN) randy.save_state() randy.set_height(3) randy.center() randy.change_mode("plain") randy.set_color(BLUE) self.add(randy) self.play( randy.change_mode, "sick", randy.set_color, SICKLY_GREEN ) self.play(Blink(randy)) self.play(randy.restore) self.play( Write(title), LaggedStartMap(FadeIn, all_creatures, run_time = 3) ) self.wait() class TestScene(PiCreatureScene): def get_result(self, creature, word, color): arrow = self.get_test_arrow() test_result = TextMobject(word) test_result.set_color(color) test_result.next_to(arrow.get_end(), RIGHT) group = VGroup(arrow, test_result) group.next_to(creature, RIGHT, aligned_edge = UP) return group def get_positive_result(self, creature): return self.get_result(creature, "Diseased", SICKLY_GREEN) def get_negative_result(self, creature): return self.get_result(creature, "Healthy", GREEN) def get_test_arrow(self): arrow = Arrow( LEFT, RIGHT, color = WHITE, ) word = TextMobject("Test") word.scale(0.8) word.next_to(arrow, UP, buff = 0) arrow.add(word) return arrow def create_pi_creature(self): randy = Randolph() randy.next_to(ORIGIN, LEFT) return randy class TestDiseaseCase(TestScene): def construct(self): randy = self.pi_creature randy.change_mode("sick") randy.set_color(SICKLY_GREEN) result = self.get_positive_result(randy) accuracy = TextMobject("100\\% Accuracy") accuracy.next_to(VGroup(randy, result), UP) accuracy.to_edge(UP) self.add(randy) self.play(FadeIn(result[0])) self.play(Write(result[1])) self.play(FadeIn(accuracy)) self.wait() class TestNonDiseaseCase(TestScene): def construct(self): randy = self.pi_creature randy.change_mode("happy") randy.next_to(ORIGIN, LEFT) result = self.get_negative_result(randy) accuracy = TextMobject("99\\% Accuracy") accuracy.next_to(VGroup(randy, result), UP) accuracy.to_edge(UP) all_creatures = VGroup(*[ VGroup(*[ randy.copy() for y in range(10) ]).arrange(DOWN) for y in range(10) ]).arrange(RIGHT) all_creatures.set_height(6) all_creatures.to_corner(DOWN+LEFT) last_guy = all_creatures[-1][-1] rect = SurroundingRectangle(last_guy, buff = 0) rect.set_color(YELLOW) self.add(randy, accuracy) self.play(FadeIn(result[0])) self.play(Write(result[1])) self.play(Blink(randy)) self.play( ReplacementTransform(randy, all_creatures[0][0]), LaggedStartMap(FadeIn, all_creatures, run_time = 2), FadeOut(result) ) self.play(ShowCreation(rect)) self.play( last_guy.set_height, 2, last_guy.next_to, all_creatures, RIGHT ) result = self.get_positive_result(last_guy) false_positive = TextMobject("False positive") false_positive.scale(0.8) false_positive.next_to(result, UP, LARGE_BUFF) false_positive.to_edge(RIGHT) arrow = Arrow( false_positive.get_bottom(), result[1].get_top(), buff = SMALL_BUFF ) self.play(FadeIn(result)) self.play( FadeIn(false_positive), ShowCreation(arrow), last_guy.change, "confused", result, ) for x in range(2): self.play(Blink(last_guy)) self.wait(2) class ReceivePositiveResults(TestScene): def construct(self): status = TextMobject("Health status: ???") status.to_edge(UP) randy = self.pi_creature result = self.get_positive_result(randy) accuracy = TextMobject("99\% Accuracy") accuracy.next_to(result[1], DOWN, LARGE_BUFF) accuracy.set_color(YELLOW) self.add(status, randy) self.play(FadeIn(result[0])) self.wait() self.play(Write(result[1])) self.play(randy.change, "maybe", result) self.wait(2) self.play( randy.change, "pleading", accuracy, Write(accuracy, run_time = 1) ) self.wait() self.play(randy.change, "sad", accuracy) self.wait(2) class AskAboutRephrasingQuestion(TeacherStudentsScene): def construct(self): self.teacher_says( "What if we rephrased \\\\ the question?", run_time = 1 ) self.wait(3) class RephraseQuestion(Scene): def construct(self): words = VGroup(*list(map(TextMobject, [ r"1 in $1{,000}$ chance \\ of having disease", r"1 in $100$ \\ false positive rate.", r"""\underline{\phantom{1 in 10}} chance \\ of having disease \\ after testing positive. """, ]))) words.arrange(RIGHT, buff = LARGE_BUFF) words.set_width(2*(FRAME_X_RADIUS - MED_LARGE_BUFF)) prior = TextMobject("Prior") prior.set_color(GREEN) prior.next_to(words[0], UP, 1.5*LARGE_BUFF) prior_arrow = Arrow(prior, words[0]) prior_arrow.set_color(prior.get_color()) posterior = TextMobject("Posterior") posterior.next_to(words[2], UP) posterior.shift( (prior.get_center() - posterior.get_center())[1]*UP ) posterior.set_color(YELLOW) posterior_arrow = Arrow(posterior, words[2]) posterior_arrow.set_color(posterior.get_color()) self.add(words[0]) self.play( LaggedStartMap(FadeIn, prior), ShowCreation(prior_arrow), run_time = 1 ) self.wait() self.play(FadeIn(words[1])) self.wait() self.play(FadeIn(words[2])) self.play( LaggedStartMap(FadeIn, posterior), ShowCreation(posterior_arrow), run_time = 1 ) self.wait(2) class TryUnitSquareVisual(SampleSpaceScene): def construct(self): sample_space = self.get_sample_space() self.add_prior_division() self.add(sample_space) self.add_conditional_divisions() prior_label = sample_space.horizontal_parts.labels[0] final_labels = self.final_labels hard_to_see = TextMobject("Hard to see") hard_to_see.scale(0.7) hard_to_see.next_to(prior_label, UP) hard_to_see.to_edge(UP) hard_to_see.set_color(YELLOW) arrow = Arrow(hard_to_see, prior_label) self.wait() anims = self.get_division_change_animations( sample_space, sample_space.horizontal_parts, 0.001, new_label_kwargs = {"labels" : final_labels} ) self.play(*anims, run_time = 2) self.wait() self.play( Write(hard_to_see, run_time = 2), ShowCreation(arrow) ) self.wait(2) def add_prior_division(self): sample_space = self.sample_space sample_space.divide_horizontally(0.1) initial_labels, final_labels = [ VGroup( TexMobject("P(\\text{Disease})", s1), TexMobject("P(\\text{Not disease})", s2), ).scale(0.7) for s1, s2 in (("", ""), ("= 0.001", "= 0.999")) ] sample_space.get_side_braces_and_labels(initial_labels) sample_space.add_braces_and_labels() self.final_labels = final_labels def add_conditional_divisions(self): sample_space = self.sample_space top_part, bottom_part = sample_space.horizontal_parts top_brace = Brace(top_part, UP) top_label = TexMobject( "P(", "+", "|", "\\text{Disease}", ")", "=", "1" ) top_label.scale(0.7) top_label.next_to(top_brace, UP) top_label.set_color_by_tex("+", GREEN) self.play(GrowFromCenter(top_brace)) self.play(FadeIn(top_label)) self.wait() bottom_part.divide_vertically( 0.95, colors = [BLUE_E, YELLOW_E] ) bottom_label = TexMobject( "P(", "+", "|", "\\text{Not disease}", ")", "=", "1" ) bottom_label.scale(0.7) bottom_label.set_color_by_tex("+", GREEN) braces, labels = bottom_part.get_bottom_braces_and_labels( [bottom_label] ) bottom_brace = braces[0] self.play( FadeIn(bottom_part.vertical_parts), GrowFromCenter(bottom_brace), ) self.play(FadeIn(bottom_label)) self.wait() class ShowRestrictedSpace(Scene): CONFIG = { "n_rows" : 25, "n_cols" : 40, "n_false_positives" : 10, "false_positive_color" : YELLOW_E, } def construct(self): self.add_all_creatures() self.show_accurate_positive_result() self.show_false_positive_conditional() self.show_false_positive_individuals() self.fade_out_negative_result_individuals() self.show_posterior_probability() self.contrast_with_prior() def add_all_creatures(self): title = TextMobject("$1{,}000$ individuals") title.to_edge(UP) all_creatures = self.get_all_creatures() sick_one = all_creatures.sick_one healthy_creatures = all_creatures.healthy_creatures sick_one.save_state() sick_one_words = TextMobject("1 sick") sick_one_words.next_to(sick_one, RIGHT) sick_one_words.to_edge(RIGHT) sick_one_words.set_color(SICKLY_GREEN) sick_one_arrow = Arrow( sick_one_words, sick_one, color = SICKLY_GREEN ) healthy_words = TextMobject("999 healthy") healthy_words.next_to(sick_one_words, UP, MED_LARGE_BUFF) healthy_words.shift_onto_screen() healthy_words.set_color(BLUE) self.add(title) self.play(LaggedStartMap(FadeIn, all_creatures)) self.play( FadeIn(sick_one_words), ShowCreation(sick_one_arrow) ) self.play( sick_one.set_height, 2, sick_one.next_to, sick_one_words, DOWN, sick_one.to_edge, RIGHT, ) self.wait() self.play(sick_one.restore) self.play( Write(healthy_words), LaggedStartMap( ApplyMethod, healthy_creatures, lambda m : (m.shift, MED_SMALL_BUFF*UP), rate_func = there_and_back, lag_ratio = 0.2, ) ) self.wait() self.play(FadeOut(title)) self.all_creatures = all_creatures self.healthy_creatures = healthy_creatures self.sick_one = sick_one self.sick_one_label = VGroup(sick_one_words, sick_one_arrow) self.healthy_ones_label = healthy_words def show_accurate_positive_result(self): equation = TexMobject( "P(", "\\text{Test positive }", "|", "\\text{ sick}", ")", "=", "100\\%" ) equation.set_color_by_tex("positive", YELLOW) equation.set_color_by_tex("sick", SICKLY_GREEN) equation.to_corner(UP+LEFT) self.play(Write(equation, run_time = 1)) self.wait(2) self.disease_conditional = equation def show_false_positive_conditional(self): equation = TexMobject( "P(", "\\text{Test positive }", "|", "\\text{ healthy}", ")", "=", "1\\%" ) equation.set_color_by_tex("positive", YELLOW) equation.set_color_by_tex("healthy", BLUE) equation.to_corner(UP+LEFT) self.play(ReplacementTransform( self.disease_conditional, equation )) self.wait() self.healthy_conditional = equation def show_false_positive_individuals(self): all_creatures = self.all_creatures false_positives = VGroup( *all_creatures[-1][1:1+self.n_false_positives] ) self.healthy_creatures.remove(*false_positives) brace = Brace(false_positives, RIGHT) words = TextMobject("10 False positives") words.scale(0.8) words.next_to(brace, RIGHT) self.play( GrowFromCenter(brace), LaggedStartMap( ApplyMethod, false_positives, lambda pi : (pi.set_color, self.false_positive_color), run_time = 1 ) ) self.play(Write(words)) self.wait() self.false_positives = false_positives self.false_positives_brace = brace self.false_positives_words = words def fade_out_negative_result_individuals(self): to_fade = VGroup( self.healthy_creatures, self.healthy_conditional, self.sick_one_label, self.healthy_ones_label, ) movers = VGroup(self.sick_one, *self.false_positives) movers.generate_target() movers.target.set_width(1) movers.target.arrange(RIGHT) movers.target.shift(DOWN) brace = Brace(VGroup(*movers.target[1:])) words = TextMobject("You are one of these") words.to_edge(UP) arrows = [ Arrow(words.get_bottom(), movers.target[i].get_top()) for i in (0, -1) ] self.play(FadeOut(to_fade)) self.play( MoveToTarget(movers), ReplacementTransform(self.false_positives_brace, brace), self.false_positives_words.next_to, brace, DOWN ) self.play( Write(words, run_time = 2), ShowCreation(arrows[0]) ) self.play(Transform( *arrows, run_time = 4, rate_func = there_and_back )) self.play(*list(map(FadeOut, [words, arrows[0]]))) self.brace = brace def show_posterior_probability(self): posterior = TexMobject( "P(", "\\text{Sick }", "|", "\\text{ Positive test result}", ")", "\\approx \\frac{1}{11}", "\\approx 9\\%" ) posterior.set_color_by_tex("Sick", SICKLY_GREEN) posterior.set_color_by_tex("Positive", YELLOW) posterior.to_edge(UP) posterior.shift(LEFT) self.play(FadeIn(posterior)) self.wait(2) self.posterior = posterior def contrast_with_prior(self): prior = TexMobject( "P(", "\\text{Sick}", ")", "= 0.1\\%" ) prior.set_color_by_tex("Sick", SICKLY_GREEN) prior.move_to(self.posterior, UP+RIGHT) self.revert_to_original_skipping_status() self.play( Write(prior, run_time = 1), self.posterior.shift, DOWN, ) arrow = Arrow( prior.get_right(), self.posterior.get_right(), path_arc = -np.pi, ) times_90 = TexMobject("\\times 90") times_90.next_to(arrow, RIGHT) self.play(ShowCreation(arrow)) self.play(Write(times_90, run_time = 1)) self.wait(2) ###### def get_all_creatures(self): creature = PiCreature() all_creatures = VGroup(*[ VGroup(*[ creature.copy() for y in range(self.n_rows) ]).arrange(DOWN, SMALL_BUFF) for x in range(self.n_cols) ]).arrange(RIGHT, SMALL_BUFF) all_creatures.set_height(5) all_creatures.center().to_edge(LEFT) healthy_creatures = VGroup(*it.chain(*all_creatures)) sick_one = all_creatures[-1][0] sick_one.change_mode("sick") sick_one.set_color(SICKLY_GREEN) healthy_creatures.remove(sick_one) all_creatures.sick_one = sick_one all_creatures.healthy_creatures = healthy_creatures return all_creatures class DepressingForMedicalTestDesigners(TestScene): def construct(self): self.remove(self.pi_creature) self.show_99_percent_accuracy() self.reject_test() def show_99_percent_accuracy(self): title = TextMobject("99\\% Accuracy") title.to_edge(UP) title.generate_target() title.target.to_corner(UP+LEFT) checks = VGroup(*[ VGroup(*[ TexMobject("\\checkmark").set_color(GREEN) for y in range(10) ]).arrange(DOWN) for x in range(10) ]).arrange(RIGHT) cross = TexMobject("\\times") cross.replace(checks[-1][-1]) cross.set_color(RED) Transform(checks[-1][-1], cross).update(1) checks.set_height(6) checks.next_to(title, DOWN) checks.generate_target() checks.target.scale(0.5) checks.target.next_to(title.target, DOWN) self.add(title) self.play(Write(checks)) self.wait(2) self.play(*list(map(MoveToTarget, [title, checks]))) def reject_test(self): randy = self.pi_creature randy.to_edge(DOWN) result = self.get_positive_result(randy) self.play(FadeIn(randy)) self.play( FadeIn(result), randy.change_mode, "pondering" ) self.wait() self.say( "Whatever, I'm 91\\% \\\\ sure that's wrong", target_mode = "shruggie" ) self.wait(2) class HowMuchCanYouChangeThisPrior(ShowRestrictedSpace, PiCreatureScene): def construct(self): self.single_out_new_sick_one() self.show_subgroups() self.isolate_special_group() def create_pi_creatures(self): creatures = self.get_all_creatures() creatures.set_height(6.5) creatures.center() creatures.submobjects = list(it.chain(*creatures)) self.add(creatures) self.sick_one = creatures.sick_one return creatures def single_out_new_sick_one(self): creatures = self.pi_creatures sick_one = self.sick_one new_sick_one = sick_one.copy() new_sick_one.shift(1.3*sick_one.get_width()*RIGHT) sick_one.change_mode("plain") sick_one.set_color(BLUE_E) self.add(new_sick_one) self.sick_one = new_sick_one def show_subgroups(self): subgroups = VGroup(*[ VGroup(*it.chain( self.pi_creatures[i:i+5], self.pi_creatures[i+25:i+25+5:] )) for i in range(0, 1000) if i%5 == 0 and (i/25)%2 == 0 ]) special_group = subgroups[-5] special_group.add(self.sick_one) subgroups.generate_target() width_factor = FRAME_WIDTH/subgroups.get_width() height_factor = FRAME_HEIGHT/subgroups.get_height() subgroups.target.stretch_in_place(width_factor, 0) subgroups.target.stretch_in_place(height_factor, 1) for subgroup in subgroups.target: subgroup.stretch_in_place(1./width_factor, 0) subgroup.stretch_in_place(1./height_factor, 1) self.wait() self.play(MoveToTarget(subgroups)) subgroups.remove(special_group) rects = VGroup(*[ SurroundingRectangle( group, buff = 0, color = GREEN ) for group in subgroups ]) special_rect = SurroundingRectangle( special_group, buff = 0, color = RED ) self.play(FadeIn(rects), FadeIn(special_rect)) self.wait() self.to_fade = VGroup(subgroups, rects) self.special_group = special_group self.special_rect = special_rect def isolate_special_group(self): to_fade, special_group = self.to_fade, self.special_group self.play(FadeOut(to_fade)) self.play( FadeOut(self.special_rect), special_group.set_height, 6, special_group.center, ) self.wait() class ShowTheFormula(TeacherStudentsScene): CONFIG = { "seconds_to_blink" : 3, } def construct(self): scale_factor = 0.7 sick = "\\text{sick}" positive = "\\text{positive}" formula = TexMobject( "P(", sick, "\\,|\\,", positive, ")", "=", "{\\quad P(", sick, "\\text{ and }", positive, ") \\quad", "\\over", "P(", positive, ")}", "=", "{1/1{,}000", "\\over", "1/1{,}000", "+", "(999/1{,}000)(0.01)}" ) formula.scale(scale_factor) formula.next_to(self.pi_creatures, UP, LARGE_BUFF) formula.shift_onto_screen(buff = MED_LARGE_BUFF) equals_group = formula.get_parts_by_tex("=") equals_indices = [ formula.index_of_part(equals) for equals in equals_group ] lhs = VGroup(*formula[:equals_indices[0]]) initial_formula = VGroup(*formula[:equals_indices[1]]) initial_formula.save_state() initial_formula.shift(3*RIGHT) over = formula.get_part_by_tex("\\over") num_start_index = equals_indices[0] + 1 num_end_index = formula.index_of_part(over) numerator = VGroup( *formula[num_start_index:num_end_index] ) numerator_rect = SurroundingRectangle(numerator) alt_numerator = TexMobject( "P(", sick, ")", "P(", positive, "\\,|\\,", sick, ")" ) alt_numerator.scale(scale_factor) alt_numerator.move_to(numerator) number_fraction = VGroup(*formula[equals_indices[-1]:]) rhs = TexMobject("\\approx 0.09") rhs.scale(scale_factor) rhs.move_to(equals_group[-1], LEFT) rhs.set_color(YELLOW) for mob in formula, alt_numerator: mob.set_color_by_tex(sick, SICKLY_GREEN) mob.set_color_by_tex(positive, YELLOW) #Ask question self.student_says("What does the \\\\ formula look like here?") self.play(self.teacher.change, "happy") self.wait() self.play( Write(lhs), RemovePiCreatureBubble( self.students[1], target_mode = "pondering", ), self.teacher.change, "raise_right_hand", self.students[0].change, "pondering", self.students[2].change, "pondering", ) self.wait() #Show initial formula lhs_copy = lhs.copy() self.play( LaggedStartMap( FadeIn, initial_formula, lag_ratio = 0.7 ), Animation(lhs_copy, remover = True), ) self.wait(2) self.play(ShowCreation(numerator_rect)) self.play(FadeOut(numerator_rect)) self.wait() self.play(Transform(numerator, alt_numerator)) initial_formula.add(*numerator) formula.add(*numerator) self.wait(3) #Show number_fraction self.play( initial_formula.move_to, initial_formula.saved_state, FadeIn(VGroup(*number_fraction[:3])) ) self.wait(2) self.play(LaggedStartMap( FadeIn, VGroup(*number_fraction[3:]), run_time = 3, lag_ratio = 0.7 )) self.wait(2) #Show rhs self.play(formula.shift, UP) self.play(Write(rhs)) self.change_student_modes(*["happy"]*3) self.look_at(rhs) self.wait(2) class SourceOfConfusion(Scene): CONFIG = { "arrow_width" : 5, } def construct(self): self.add_progression() self.ask_question() self.write_bayes_rule() self.shift_arrow() def add_progression(self): prior = TexMobject("P(", "S", ")", "= 0.001") arrow = Arrow(ORIGIN, self.arrow_width*RIGHT) posterior = TexMobject("P(", "S", "|", "+", ")", "= 0.09") for mob in prior, posterior: mob.set_color_by_tex("S", SICKLY_GREEN) mob.set_color_by_tex("+", YELLOW) progression = VGroup(prior, arrow, posterior) progression.arrange(RIGHT) progression.shift(DOWN) bayes_rule_words = TextMobject("Bayes' rule") bayes_rule_words.next_to(arrow, UP, buff = 0) arrow.add(bayes_rule_words) for mob, word in (prior, "Prior"), (posterior, "Posterior"): brace = Brace(mob, DOWN) label = brace.get_text(word) mob.add(brace, label) self.add(progression) self.progression = progression self.bayes_rule_words = bayes_rule_words def ask_question(self): question = TextMobject( "Where do math and \\\\ intuition disagree?" ) question.to_corner(UP+LEFT) question_arrow = Arrow( question.get_bottom(), self.bayes_rule_words.get_top(), color = WHITE ) self.play(Write(question)) self.wait() self.play(ShowCreation(question_arrow)) self.question = question self.question_arrow = question_arrow def write_bayes_rule(self): words = self.bayes_rule_words words_rect = SurroundingRectangle(words) rule = TexMobject( "P(", "S", "|", "+", ")", "=", "P(", "S", ")", "{P(", "+", "|", "S", ")", "\\over", "P(", "+", ")}" ) rule.set_color_by_tex("S", SICKLY_GREEN) rule.set_color_by_tex("+", YELLOW) rule.to_corner(UP+RIGHT) rule_rect = SurroundingRectangle(rule) rule_rect.set_color(BLUE) rule.save_state() rule.replace(words_rect) rule.scale_in_place(0.9) rule.set_fill(opacity = 0) self.play(ShowCreation(words_rect)) self.play( ReplacementTransform(words_rect, rule_rect), rule.restore, run_time = 2 ) self.wait(3) def shift_arrow(self): new_arrow = Arrow( self.question.get_bottom(), self.progression[0].get_top(), color = WHITE ) self.play(Transform( self.question_arrow, new_arrow )) self.wait(2) class StatisticsVsEmpathy(PiCreatureScene): def construct(self): randy, morty = self.randy, self.morty sick_one = PiCreature() sick_one.scale(0.5) sick_group = VGroup( sick_one, VectorizedPoint(sick_one.get_bottom()) ) priors = VGroup(*[ TexMobject("%.1f"%p+ "\\%").move_to(ORIGIN, RIGHT) for p in np.arange(0.1, 2.0, 0.1) ]) priors.next_to(randy, UP+LEFT, LARGE_BUFF) prior = priors[0] prior.save_state() self.play(PiCreatureSays( morty, "1 in 1{,}000 people \\\\ have this disease.", look_at_arg = randy.eyes )) self.play(randy.change, "pondering", morty.eyes) self.wait() self.play(Write(prior)) self.wait() self.play( prior.scale, 0.1, prior.set_fill, None, 0, prior.move_to, randy.eyes ) self.wait() self.play( PiCreatureBubbleIntroduction( randy, sick_group, target_mode = "guilty", bubble_class = ThoughtBubble, content_introduction_class = FadeIn, look_at_arg = sick_one, ), RemovePiCreatureBubble(morty) ) self.play( sick_one.change_mode, "sick", sick_one.set_color, SICKLY_GREEN ) self.wait() probably_me = TextMobject("That's probably \\\\ me") probably_me.next_to(sick_one, DOWN) target_sick_group = VGroup( sick_one.copy(), probably_me ) target_sick_group.scale(0.8) self.pi_creature_thinks( target_sick_group, target_mode = "pleading", ) self.wait(2) self.play(prior.restore) for new_prior in priors[1:]: self.play(Transform(prior, new_prior, run_time = 0.5)) self.wait() ###### def create_pi_creatures(self): randy = self.randy = Randolph() morty = self.morty = Mortimer() randy.to_edge(DOWN).shift(3*LEFT) morty.to_edge(DOWN).shift(3*RIGHT) return VGroup(randy, morty) class LessMedicalExample(Scene): def construct(self): disease = TexMobject("P(\\text{Having a disease})") telepathy = TexMobject("P(\\text{Telepathy})") cross = Cross(disease) self.add(disease) self.wait() self.play(ShowCreation(cross)) self.play( FadeIn(telepathy), VGroup(disease, cross).shift, 2*UP ) self.wait() class PlaneCrashProbability(Scene): def construct(self): plane_prob = TexMobject( "P(\\text{Dying in a }", "\\text{plane}", "\\text{ crash})", "\\approx", "1/", "11{,}000{,}000" ) plane_prob.set_color_by_tex("plane", BLUE) car_prob = TexMobject( "P(\\text{Dying in a }", "\\text{car}", "\\text{ crash})", "\\approx", "1/", "5{,}000" ) car_prob.set_color_by_tex("car", YELLOW) plane_prob.shift(UP) car_prob.shift( plane_prob.get_part_by_tex("approx").get_center() -\ car_prob.get_part_by_tex("approx").get_center() +\ DOWN ) self.play(Write(plane_prob)) self.wait(2) self.play(ReplacementTransform( plane_prob.copy(), car_prob )) self.wait(2) class IntroduceTelepathyExample(StatisticsVsEmpathy): def construct(self): self.force_skipping() self.show_mind_reading_powers() self.claim_mind_reading_powers() self.generate_random_number() self.guess_number_correctly() self.ask_about_chances() self.revert_to_original_skipping_status() self.say_you_probably_got_lucky() def show_mind_reading_powers(self): randy, morty = self.randy, self.morty title = TextMobject("1 in 1{,}000 read minds") title.to_edge(UP) self.add(title) self.read_mind(randy, morty) self.title = title def claim_mind_reading_powers(self): randy, morty = self.randy, self.morty self.play(PiCreatureSays( randy, "I have the gift.", run_time = 1, look_at_arg = morty.eyes, )) self.wait() self.play(RemovePiCreatureBubble( randy, target_mode = "happy", look_at_arg = morty.eyes )) def generate_random_number(self): morty = self.morty bubble = morty.get_bubble("", direction = LEFT) numbers = [ Integer(random.choice(list(range(100)))) for x in range(30) ] numbers.append(Integer(67)) for number in numbers: number.next_to(morty, UP, LARGE_BUFF, RIGHT) for number in numbers: self.add(number) Scene.wait(self, 0.1) self.remove(number) self.play( ShowCreation(bubble), number.move_to, bubble.get_bubble_center(), DOWN+LEFT, morty.change, "pondering", ) self.wait() morty.bubble = bubble self.number = number def guess_number_correctly(self): randy, morty = self.randy, self.morty number_copy = self.number.copy() self.read_mind(randy, morty) self.play(PiCreatureSays( randy, number_copy, target_mode = "hooray", look_at_arg = morty.eyes )) self.wait() def ask_about_chances(self): probability = TexMobject( "P(", "\\text{Telepath }", "|", "\\text{ Correct}", ")", "=", "???" ) probability.set_color_by_tex("Telepath", BLUE) probability.set_color_by_tex("Correct", GREEN) probability.to_edge(UP) self.play(morty.change, "confused", randy.eyes) self.wait() self.play(ReplacementTransform( self.title, VGroup(*it.chain(*probability)) )) self.wait() def say_you_probably_got_lucky(self): randy, morty = self.randy, self.morty self.play( PiCreatureSays( morty, "You probably \\\\ got lucky.", target_mode = "sassy", look_at_arg = randy.eyes, bubble_kwargs = {"height" : 3, "width" : 4} ), RemovePiCreatureBubble( randy, target_mode = "plain", look_at_arg = morty.eyes, ) ) self.wait(2) ### def read_mind(self, pi1, pi2): self.play(pi1.change, "telepath", pi2.eyes) self.send_mind_waves(pi1, pi2) self.send_mind_waves(pi2, pi1) self.wait() def send_mind_waves(self, pi1, pi2): angle = np.pi/3 n_arcs = 5 vect = pi2.eyes.get_center() - pi1.eyes.get_center() vect[1] = 0 arc = Arc(angle = angle) arc.rotate(-angle/2 + angle_of_vector(vect), about_point = ORIGIN) arc.scale(3) arcs = VGroup(*[arc.copy() for x in range(n_arcs)]) arcs.move_to(pi2.eyes.get_center(), vect) arcs.set_stroke(BLACK, 0) for arc in arcs: arc.save_state() arcs.scale(0.1) arcs.move_to(pi1.eyes, vect) arcs.set_stroke(WHITE, 4) self.play(LaggedStartMap( ApplyMethod, arcs, lambda m : (m.restore,), lag_ratio = 0.7, )) self.remove(arcs) class CompareNumbersInBothExamples(Scene): def construct(self): v_line = Line(UP, DOWN).scale(FRAME_Y_RADIUS) v_line.shift(MED_LARGE_BUFF*LEFT) h_line = Line(LEFT, RIGHT).scale(FRAME_X_RADIUS) h_line.to_edge(UP, buff = 1.25*LARGE_BUFF) titles = VGroup() for word, vect in ("Disease", LEFT), ("Telepathy", RIGHT): title = TextMobject("%s example"%word) title.shift(vect*FRAME_X_RADIUS/2.0) title.to_edge(UP) titles.add(title) priors = VGroup(*[ TexMobject( "P(", "\\text{%s}"%s, ")", "= 1/1{,}000}" ) for s in ("Sick", "Powers") ]) likelihoods = VGroup(*[ TexMobject( "P(", "\\text{%s}"%s1, "|", "\\text{Not }", "\\text{%s}"%s2, ")", "=", "1/100" ) for s1, s2 in [("+", "Sick"), ("Correct", "Powers")] ]) priors.next_to(likelihoods, UP, LARGE_BUFF) for group in priors, likelihoods: for mob, vect in zip(group, [LEFT, RIGHT]): mob.set_color_by_tex("Sick", BLUE) mob.set_color_by_tex("Powers", BLUE) mob.set_color_by_tex("+", GREEN) mob.set_color_by_tex("Correct", GREEN) mob.scale(0.8) mob.shift(vect*FRAME_X_RADIUS/2) self.play( LaggedStartMap(FadeIn, titles, lag_ratio = 0.7), *list(map(ShowCreation, [h_line, v_line])) ) self.wait() self.play(FadeIn(priors)) self.wait() self.play(FadeIn(likelihoods)) self.wait(3) class NonchalantReactionToPositiveTest(TestScene): def construct(self): randy = self.pi_creature randy.shift(DOWN+2*RIGHT) result = self.get_positive_result(randy) accuracy = TextMobject("99\\% Accuracy") accuracy.set_color(YELLOW) accuracy.next_to(result, DOWN, LARGE_BUFF, RIGHT) self.add(accuracy) self.play(Write(result, run_time = 2)) self.play(randy.change, "pondering", result) self.wait() words = TextMobject("Pssht, I'm probably fine.") words.scale(0.8) self.pi_creature_says( words, target_mode = "shruggie", bubble_kwargs = { "direction" : RIGHT, "width" : 6, "height" : 3, }, content_introduction_class = FadeIn, ) self.wait(4) class OneInOneThousandHaveDiseaseCopy(OneInOneThousandHaveDisease): pass class ExampleMeasuresDisbeliefInStatistics(Introduction): def construct(self): self.teacher.shift(LEFT) self.hold_up_example() self.write_counter_intuitive() self.write_new_theory() self.either_way() def write_new_theory(self): bayes_to_intuition = self.bayes_to_intuition cross = bayes_to_intuition[-1] bayes_to_intuition.remove(cross) statistics_to_belief = TextMobject( "Statistics ", "$\\leftrightarrow$", " Belief" ) statistics_to_belief.scale(0.8) arrow = statistics_to_belief.get_part_by_tex("arrow") statistics_to_belief.next_to(self.example, UP) self.revert_to_original_skipping_status() self.play(bayes_to_intuition.to_edge, UP) self.play( LaggedStartMap(FadeIn, statistics_to_belief), cross.move_to, arrow ) self.change_student_modes( *["pondering"]*3, look_at_arg = statistics_to_belief ) self.wait(3) statistics_to_belief.add(cross) self.statistics_to_belief = statistics_to_belief def either_way(self): b_to_i = self.bayes_to_intuition s_to_b = self.statistics_to_belief self.play(FadeOut(self.example)) self.play( self.teacher.change_mode, "raise_left_hand", self.teacher.look, UP+RIGHT, b_to_i.next_to, self.teacher, UP, b_to_i.to_edge, RIGHT, MED_SMALL_BUFF, ) self.wait(2) self.play( self.teacher.change_mode, "raise_right_hand", self.teacher.look, UP+LEFT, s_to_b.next_to, self.teacher, UP, s_to_b.shift, LEFT, b_to_i.shift, 2*UP ) self.wait(2) class AlwaysPictureTheSpaceOfPossibilities(PiCreatureScene): def construct(self): self.pi_creature_thinks( "", bubble_kwargs = { "height" : 4.5, "width" : 8, } ) self.wait(3) def create_pi_creature(self): return Randolph().to_corner(DOWN+LEFT) class Thumbnail(Scene): def construct(self): title = TextMobject("Why is this \\\\ counterintuitive?") title.scale(2) title.to_edge(UP) randy = Randolph(mode = "sick", color = SICKLY_GREEN) # randy.look_at(title) randy.scale(1.5) randy.shift(3*LEFT) randy.to_edge(DOWN) prob = TexMobject("P(", "\\text{Sick}", "|", "\\text{Test+}", ")") prob.scale(2) prob.set_color_by_tex("Sick", YELLOW) prob.set_color_by_tex("Test", GREEN) prob.next_to(randy, RIGHT) self.add(title, randy, prob)