diff --git a/eop/bayes.py b/eop/bayes.py index 95d37e97..fbe69eea 100644 --- a/eop/bayes.py +++ b/eop/bayes.py @@ -2193,8 +2193,88 @@ class FootnoteWrapper(NextVideoWrapper): "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.highlight_by_tex("H", YELLOW) + label.highlight_by_tex("D", GREEN) + label.scale(1.5) + + sample_space = self.get_sample_space() + sample_space.scale_to_fit_height(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) diff --git a/eop/bayes_footnote.py b/eop/bayes_footnote.py index a1bb0919..4e3e7346 100644 --- a/eop/bayes_footnote.py +++ b/eop/bayes_footnote.py @@ -36,9 +36,11 @@ 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" : "", + "author" : "Daniel Kahneman", } class Introduction(TeacherStudentsScene): @@ -832,25 +834,81 @@ class DepressingForMedicalTestDesigners(TestScene): class HowMuchCanYouChangeThisPrior(ShowRestrictedSpace, PiCreatureScene): def construct(self): - self.play(LaggedStart( - FadeIn, self.pi_creatures, - run_time = 4, - lag_ratio = 0.7, - )) - for x in range(2): - self.joint_blink(shuffle = False) - self.dither(2) + self.single_out_new_sick_one() + self.show_subgroups() + self.isolate_special_group() def create_pi_creatures(self): - title = TextMobject("Tiny, tiny prior") - title.to_edge(UP) creatures = self.get_all_creatures() - creatures.submobjects = list(it.chain(*creatures)) creatures.scale_to_fit_height(6.5) - creatures.next_to(title, DOWN) - self.add(title) + 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.highlight(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 = 2*SPACE_WIDTH/subgroups.get_width() + height_factor = 2*SPACE_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.dither() + 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.dither() + + 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.scale_to_fit_height, 6, + special_group.center, + ) + self.dither() + class ShowTheFormula(TeacherStudentsScene): CONFIG = { "seconds_to_blink" : 3, @@ -1059,6 +1117,13 @@ class StatisticsVsEmpathy(PiCreatureScene): 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, @@ -1067,6 +1132,14 @@ class StatisticsVsEmpathy(PiCreatureScene): )) self.play(randy.change, "pondering", morty.eyes) self.dither() + self.play(Write(prior)) + self.dither() + self.play( + prior.scale, 0.1, + prior.set_fill, None, 0, + prior.move_to, randy.eyes + ) + self.dither() self.play( PiCreatureBubbleIntroduction( randy, sick_group, @@ -1094,7 +1167,12 @@ class StatisticsVsEmpathy(PiCreatureScene): target_sick_group, target_mode = "pleading", ) - self.dither(3) + self.dither(2) + + self.play(prior.restore) + for new_prior in priors[1:]: + self.play(Transform(prior, new_prior, run_time = 0.5)) + self.dither() ###### @@ -1105,6 +1183,21 @@ class StatisticsVsEmpathy(PiCreatureScene): 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.dither() + self.play(ShowCreation(cross)) + self.play( + FadeIn(telepathy), + VGroup(disease, cross).shift, 2*UP + ) + self.dither() + class PlaneCrashProbability(Scene): def construct(self): plane_prob = TexMobject( @@ -1133,10 +1226,14 @@ class PlaneCrashProbability(Scene): 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.ask_about_chances() + self.revert_to_original_skipping_status() self.say_you_probably_got_lucky() def show_mind_reading_powers(self): @@ -1146,19 +1243,33 @@ class IntroduceTelepathyExample(StatisticsVsEmpathy): self.add(title) self.read_mind(randy, morty) - self.play(randy.change, "happy", morty.eyes) 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.dither() + 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(range(100)), - ).next_to(morty, UP, LARGE_BUFF, RIGHT) + Integer(random.choice(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: @@ -1186,8 +1297,6 @@ class IntroduceTelepathyExample(StatisticsVsEmpathy): look_at_arg = morty.eyes )) self.dither() - self.play(morty.change, "confused", randy.eyes) - self.dither() def ask_about_chances(self): probability = TexMobject( @@ -1198,6 +1307,8 @@ class IntroduceTelepathyExample(StatisticsVsEmpathy): probability.highlight_by_tex("Correct", GREEN) probability.to_edge(UP) + self.play(morty.change, "confused", randy.eyes) + self.dither() self.play(ReplacementTransform( self.title, VGroup(*it.chain(*probability)) )) @@ -1255,6 +1366,53 @@ class IntroduceTelepathyExample(StatisticsVsEmpathy): )) self.remove(arcs) +class CompareNumbersInBothExamples(Scene): + def construct(self): + v_line = Line(UP, DOWN).scale(SPACE_HEIGHT) + v_line.shift(MED_LARGE_BUFF*LEFT) + h_line = Line(LEFT, RIGHT).scale(SPACE_WIDTH) + 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*SPACE_WIDTH/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.highlight_by_tex("Sick", BLUE) + mob.highlight_by_tex("Powers", BLUE) + mob.highlight_by_tex("+", GREEN) + mob.highlight_by_tex("Correct", GREEN) + mob.scale(0.8) + mob.shift(vect*SPACE_WIDTH/2) + + self.play( + LaggedStart(FadeIn, titles, lag_ratio = 0.7), + *map(ShowCreation, [h_line, v_line]) + ) + self.dither() + self.play(FadeIn(priors)) + self.dither() + self.play(FadeIn(likelihoods)) + self.dither(3) + + class NonchalantReactionToPositiveTest(TestScene): def construct(self): randy = self.pi_creature diff --git a/topics/common_scenes.py b/topics/common_scenes.py index 74774a61..36f55e34 100644 --- a/topics/common_scenes.py +++ b/topics/common_scenes.py @@ -2,7 +2,7 @@ from helpers import * from scene.scene import Scene -from animation.simple_animations import Write, DrawBorderThenFill +from animation.simple_animations import Write, DrawBorderThenFill, LaggedStart from animation.transform import FadeIn, FadeOut, ApplyMethod from mobject.vectorized_mobject import VGroup from mobject.tex_mobject import TexMobject, TextMobject @@ -91,7 +91,7 @@ class PatreonThanks(Scene): "Ripta Pasay", "Felipe Diniz", ], - "patron_group_size" : 10, + "max_patron_group_size" : 20, "patron_scale_val" : 0.7, } @@ -99,16 +99,12 @@ class PatreonThanks(Scene): morty = Mortimer() morty.next_to(ORIGIN, DOWN) - n_patrons = len(self.specific_patrons) - special_thanks = TextMobject("Special thanks") - special_thanks.highlight(YELLOW) - special_thanks.to_edge(UP) - patreon_logo = PatreonLogo() - patreon_logo.next_to(morty, UP, buff = MED_LARGE_BUFF) + patreon_logo.to_edge(UP) + n_patrons = len(self.specific_patrons) patrons = map(TextMobject, self.specific_patrons) - num_groups = float(len(patrons)) / self.patron_group_size + num_groups = float(len(patrons)) / self.max_patron_group_size proportion_range = np.linspace(0, 1, num_groups + 1) indices = (len(patrons)*proportion_range).astype('int') patron_groups = [ @@ -117,30 +113,38 @@ class PatreonThanks(Scene): ] for i, group in enumerate(patron_groups): - group.arrange_submobjects(DOWN, aligned_edge = LEFT) - group.scale(self.patron_scale_val) - group.to_edge(LEFT if i%2 == 0 else RIGHT) + left_group = VGroup(*group[:len(group)/2]) + right_group = VGroup(*group[len(group)/2:]) + for subgroup, vect in (left_group, LEFT), (right_group, RIGHT): + subgroup.arrange_submobjects(DOWN, aligned_edge = LEFT) + subgroup.scale(self.patron_scale_val) + subgroup.to_edge(vect) - self.play( - morty.change_mode, "gracious", - DrawBorderThenFill(patreon_logo), - ) - self.play(Write(special_thanks, run_time = 1)) - print len(patron_groups) + last_group = None for i, group in enumerate(patron_groups): - anims = [ - FadeIn( - group, run_time = 2, - submobject_mode = "lagged_start", - lag_factor = 4, + anims = [] + if last_group is not None: + self.play( + FadeOut(last_group), + morty.look, UP+LEFT + ) + else: + anims += [ + DrawBorderThenFill(patreon_logo), + ] + self.play( + LaggedStart( + FadeIn, group, + run_time = 2, ), - morty.look_at, group.get_top(), - ] - if i >= 2: - anims.append(FadeOut(patron_groups[i-2])) - self.play(*anims) - self.play(morty.look_at, group.get_bottom()) + morty.change, "gracious", group.get_corner(UP+LEFT), + *anims + ) + self.play(morty.look_at, group.get_corner(DOWN+LEFT)) + self.play(morty.look_at, group.get_corner(UP+RIGHT)) + self.play(morty.look_at, group.get_corner(DOWN+RIGHT)) self.play(Blink(morty)) + last_group = group diff --git a/topics/geometry.py b/topics/geometry.py index e2f11cfd..52bd7dee 100644 --- a/topics/geometry.py +++ b/topics/geometry.py @@ -1,7 +1,7 @@ from helpers import * from mobject import Mobject -from mobject.vectorized_mobject import VMobject +from mobject.vectorized_mobject import VMobject, VGroup class Arc(VMobject): CONFIG = { @@ -413,6 +413,18 @@ class PictureInPictureFrame(Rectangle): ) self.scale_to_fit_height(height) +class Cross(VGroup): + CONFIG = { + "stroke_color" : RED, + "stroke_width" : 6, + } + def __init__(self, mobject, **kwargs): + VGroup.__init__(self, + Line(UP+LEFT, DOWN+RIGHT), + Line(UP+RIGHT, DOWN+LEFT), + ) + self.replace(mobject, stretch = True) + self.set_stroke(self.stroke_color, self.stroke_width) class Grid(VMobject): CONFIG = { diff --git a/topics/objects.py b/topics/objects.py index ef1bf7ae..2de7aa1b 100644 --- a/topics/objects.py +++ b/topics/objects.py @@ -8,7 +8,8 @@ from mobject.tex_mobject import TextMobject, TexMobject from animation import Animation from animation.simple_animations import Rotating -from topics.geometry import Circle, Line, Rectangle, Square, Arc, Polygon +from topics.geometry import Circle, Line, Rectangle, Square, \ + Arc, Polygon, SurroundingRectangle from topics.three_dimensions import Cube class Guitar(SVGMobject): @@ -228,20 +229,16 @@ class Laptop(VGroup): class PatreonLogo(SVGMobject): CONFIG = { "file_name" : "patreon_logo", - "fill_color" : "#ff5900", + "fill_color" : "#F96854", + # "fill_color" : WHITE, "fill_opacity" : 1, "stroke_width" : 0, - "height" : 2, + "width" : 4, "propogate_style_to_family" : True } def __init__(self, **kwargs): SVGMobject.__init__(self, **kwargs) - outer, inner = self.split() - # outer.add_subpath(inner.points) - # self.remove(inner) - inner.set_fill(BLACK, opacity = 1) - inner.set_stroke(self.fill_color, width = 0.5) - self.scale_to_fit_height(self.height) + self.scale_to_fit_width(self.width) self.center() class VideoIcon(SVGMobject):