diff --git a/from_3b1b/active/bayes/beta2.py b/from_3b1b/active/bayes/beta2.py index dcd39c21..d7cee960 100644 --- a/from_3b1b/active/bayes/beta2.py +++ b/from_3b1b/active/bayes/beta2.py @@ -1,6 +1,7 @@ from manimlib.imports import * from from_3b1b.active.bayes.beta_helpers import * from from_3b1b.active.bayes.beta1 import * +from from_3b1b.old.hyperdarts import Dartboard import scipy.stats @@ -441,7 +442,7 @@ class LabelH(Scene): self.wait() -class Underline(Scene): +class DrawUnderline(Scene): def construct(self): line = Line(2 * LEFT, 2 * RIGHT) line.set_stroke(PINK, 5) @@ -1396,7 +1397,7 @@ class ShowLimitToPdf(Scene): class FiniteVsContinuum(Scene): def construct(self): # Title - f_title = TextMobject("Finite context") + f_title = TextMobject("Discrete context") f_title.set_height(0.5) f_title.to_edge(UP) f_underline = Underline(f_title) @@ -1406,7 +1407,7 @@ class FiniteVsContinuum(Scene): # Equations dice = get_die_faces()[::2] - cards = [PlayingCard("A" + sym) for sym in "SHCD"] + cards = [PlayingCard(letter + "H") for letter in "A35"] eqs = VGroup( self.get_union_equation(dice), @@ -1474,8 +1475,6 @@ class FiniteVsContinuum(Scene): self.play(ShowCreation(e_cross)) self.wait() - self.embed() - def get_union_equation(self, mobs): mob_copies1 = VGroup() mob_copies2 = VGroup() @@ -1675,9 +1674,444 @@ class HalfFiniteHalfContinuous(Scene): self.wait(30) +class SumToIntegral(Scene): + def construct(self): + # Titles + titles = VGroup( + TextMobject("Discrete context"), + TextMobject("Continuous context"), + ) + titles.set_height(0.5) + for title, vect in zip(titles, [LEFT, RIGHT]): + title.move_to(vect * FRAME_WIDTH / 4) + title.to_edge(UP, buff=MED_SMALL_BUFF) + + v_line = Line(UP, DOWN).set_height(FRAME_HEIGHT) + h_line = Line(LEFT, RIGHT).set_width(FRAME_WIDTH) + h_line.next_to(titles, DOWN) + h_line.set_x(0) + v_line.center() + + self.play( + ShowCreation(VGroup(h_line, v_line)), + LaggedStartMap( + FadeInFrom, titles, + lambda m: (m, -0.2 * m.get_center()[0] * RIGHT), + run_time=1, + lag_ratio=0.1, + ), + ) + self.wait() + + # Sum and int + kw = {"tex_to_color_map": {"S": BLUE}} + s_sym = TexMobject("\\sum", "_{x \\in S} P(x)", **kw) + i_sym = TexMobject("\\int_{S} p(x)", "\\text{d}x", **kw) + syms = VGroup(s_sym, i_sym) + syms.scale(2) + for sym, title in zip(syms, titles): + sym.shift(-sym[-1].get_center()) + sym.match_x(title) + + arrow = Arrow( + s_sym[0].get_corner(UP), + i_sym[0].get_corner(UP), + path_arc=-90 * DEGREES, + ) + arrow.set_color(YELLOW) + + self.play(Write(s_sym, run_time=1)) + anims = [ShowCreation(arrow)] + for i, j in [(0, 0), (2, 1), (3, 2)]: + source = s_sym[i].deepcopy() + target = i_sym[j] + target.save_state() + source.generate_target() + target.replace(source, stretch=True) + source.target.replace(target, stretch=True) + target.set_opacity(0) + source.target.set_opacity(0) + anims += [ + Restore(target, path_arc=-60 * DEGREES), + MoveToTarget(source, path_arc=-60 * DEGREES), + ] + self.play(LaggedStart(*anims)) + self.play(FadeInFromDown(i_sym[3])) + self.add(i_sym) + self.wait() + self.play( + FadeOutAndShift(arrow, UP), + syms.next_to, h_line, DOWN, {"buff": MED_LARGE_BUFF}, + syms.match_x, syms, + ) + + # Add curve area in editing + # Add bar chart + axes = Axes( + x_min=0, + x_max=10, + y_min=0, + y_max=7, + y_axis_config={ + "unit_size": 0.75, + } + ) + axes.set_width(0.5 * FRAME_WIDTH - 1) + axes.next_to(s_sym, DOWN) + axes.y_axis.add_numbers(2, 4, 6) + + bars = VGroup() + for x, y in [(1, 1), (4, 3), (7, 2)]: + bar = Rectangle() + bar.set_stroke(WHITE, 1) + bar.set_fill(BLUE_D, 1) + line = Line(axes.c2p(x, 0), axes.c2p(x + 2, y)) + bar.replace(line, stretch=True) + bars.add(bar) + + addition_formula = TexMobject(*"1+3+2") + addition_formula.space_out_submobjects(2.1) + addition_formula.next_to(bars, UP) + + for bar in bars: + bar.save_state() + bar.stretch(0, 1, about_edge=DOWN) + + self.play( + Write(axes), + LaggedStartMap(Restore, bars), + LaggedStartMap(FadeInFromDown, addition_formula), + ) + self.wait() + + # Confusion + morty = Mortimer() + morty.to_corner(DR) + morty.look_at(i_sym) + self.play( + *map(FadeOut, [axes, bars, addition_formula]), + FadeIn(morty) + ) + self.play(morty.change, "maybe") + self.play(Blink(morty)) + self.play(morty.change, "confused", i_sym.get_right()) + self.play(Blink(morty)) + self.wait() + + # Focus on integral + self.play( + Uncreate(VGroup(v_line, h_line)), + FadeOutAndShift(titles, UP), + FadeOutAndShift(morty, RIGHT), + FadeOutAndShift(s_sym, LEFT), + i_sym.center, + i_sym.to_edge, LEFT + ) + + arrows = VGroup() + for vect in [UP, DOWN]: + corner = i_sym[-1].get_corner(RIGHT + vect) + arrows.add(Arrow( + corner, + corner + 2 * RIGHT + 2 * vect, + path_arc=-np.sign(vect[1]) * 60 * DEGREES, + )) + + self.play(*map(ShowCreation, arrows)) + + # Types of integration + dist = scipy.stats.beta(7 + 1, 3 + 1) + axes_pair = VGroup() + graph_pair = VGroup() + for arrow in arrows: + axes = get_beta_dist_axes(y_max=5, y_unit=1) + axes.set_width(4) + axes.next_to(arrow.get_end(), RIGHT) + graph = axes.get_graph(dist.pdf) + graph.set_stroke(BLUE, 2) + graph.set_fill(BLUE_E, 0) + graph.make_smooth() + axes_pair.add(axes) + graph_pair.add(graph) + + r_axes, l_axes = axes_pair + r_graph, l_graph = graph_pair + r_name = TextMobject("Riemann\\\\Integration") + r_name.next_to(r_axes, RIGHT) + l_name = TextMobject("Lebesgue\\\\Integration$^*$") + l_name.next_to(l_axes, RIGHT) + footnote = TextMobject("*a bit more complicated than\\\\these bars make it look") + footnote.match_width(l_name) + footnote.next_to(l_name, DOWN) + + self.play(LaggedStart( + FadeIn(r_axes), + FadeIn(r_graph), + FadeIn(r_name), + FadeIn(l_axes), + FadeIn(l_graph), + FadeIn(l_name), + run_time=1, + )) + + # Approximation bars + def get_riemann_rects(dx, axes=r_axes, func=dist.pdf): + bars = VGroup() + for x in np.arange(0, 1, dx): + bar = Rectangle() + line = Line( + axes.c2p(x, 0), + axes.c2p(x + dx, func(x)), + ) + bar.replace(line, stretch=True) + bar.set_stroke(BLUE_E, width=10 * dx, opacity=1) + bar.set_fill(BLUE, 0.5) + bars.add(bar) + return bars + + def get_lebesgue_bars(dy, axes=l_axes, func=dist.pdf, mx=0.7, y_max=dist.pdf(0.7)): + bars = VGroup() + for y in np.arange(dy, y_max + dy, dy): + x0 = binary_search(func, y, 0, mx) or mx + x1 = binary_search(func, y, mx, 1) or mx + line = Line(axes.c2p(x0, y - dy), axes.c2p(x1, y)) + bar = Rectangle() + bar.set_stroke(RED_E, 0) + bar.set_fill(RED_E, 0.5) + bar.replace(line, stretch=True) + bars.add(bar) + return bars + + r_bar_groups = [] + l_bar_groups = [] + Ns = [10, 20, 40, 80, 160] + Ms = [2, 4, 8, 16, 32] + for N, M in zip(Ns, Ms): + r_bar_groups.append(get_riemann_rects(dx=1 / N)) + l_bar_groups.append(get_lebesgue_bars(dy=1 / M)) + self.play( + FadeIn(r_bar_groups[0], lag_ratio=0.1), + FadeIn(l_bar_groups[0], lag_ratio=0.1), + FadeIn(footnote), + ) + self.wait() + for rbg0, rbg1, lbg0, lbg1 in zip(r_bar_groups, r_bar_groups[1:], l_bar_groups, l_bar_groups[1:]): + self.play( + ReplacementTransform( + rbg0, rbg1, + lag_ratio=1 / len(rbg0), + run_time=2, + ), + ReplacementTransform( + lbg0, lbg1, + lag_ratio=1 / len(lbg0), + run_time=2, + ), + ) + self.wait() + self.play( + FadeOut(r_bar_groups[-1]), + FadeOut(l_bar_groups[-1]), + r_graph.set_fill, BLUE_E, 1, + l_graph.set_fill, RED_E, 1, + ) + + +class MeasureTheoryLeadsTo(Scene): + def construct(self): + words = TextMobject("Measure Theory") + words.set_color(RED) + arrow = Vector(DOWN) + arrow.next_to(words, DOWN, buff=SMALL_BUFF) + arrow.set_stroke(width=7) + arrow.rotate(45 * DEGREES, about_point=arrow.get_start()) + self.play( + FadeInFrom(words, DOWN), + GrowArrow(arrow), + UpdateFromAlphaFunc(arrow, lambda m, a: m.set_opacity(a)), + ) + self.wait() + + +class WhenIWasFirstLearning(TeacherStudentsScene): + def construct(self): + self.teacher.change_mode("raise_right_hand") + self.play( + self.get_student_changes("pondering", "thinking", "tease"), + self.teacher.change, "thinking", + ) + + younger = BabyPiCreature(color=GREY_BROWN) + younger.set_height(2) + younger.move_to(self.students, DL) + + self.look_at(self.screen) + self.wait() + self.play( + ReplacementTransform(self.teacher, younger), + LaggedStartMap( + FadeOutAndShift, self.students, + lambda m: (m, DOWN), + ) + ) + + # Bubble + bubble = ThoughtBubble() + bubble[-1].set_fill(GREEN_SCREEN, 1) + bubble.move_to(younger.get_corner(UR), DL) + + self.play( + Write(bubble), + younger.change, "maybe", bubble.get_bubble_center(), + ) + self.play(Blink(younger)) + for mode in ["confused", "angry", "pondering", "maybe"]: + self.play(younger.change, mode) + for x in range(2): + self.wait() + if random.random() < 0.5: + self.play(Blink(younger)) + + +class PossibleYetProbabilityZero(Scene): + def construct(self): + poss = TextMobject("Possible") + prob = TextMobject("Probability = 0") + total = TextMobject("P(dart hits somewhere) = 1") + # total[1].next_to(total[0][0], RIGHT) + words = VGroup(poss, prob, total) + words.scale(1.5) + words.arrange(DOWN, aligned_edge=LEFT, buff=MED_LARGE_BUFF) + + self.play(Write(poss, run_time=0.5)) + self.wait() + self.play(FadeInFrom(prob, UP)) + self.wait() + self.play(FadeInFrom(total, UP)) + self.wait() + + +class TiePossibleToDensity(Scene): + def construct(self): + poss = TextMobject("Possibility") + prob = TextMobject("Probability", " $>$ 0") + dens = TextMobject("Probability \\emph{density}", " $>$ 0") + dens[0].set_color(BLUE) + implies = TexMobject("\\Rightarrow") + implies2 = implies.copy() + + poss.next_to(implies, LEFT) + prob.next_to(implies, RIGHT) + dens.next_to(implies, RIGHT) + cross = Cross(implies) + + self.camera.frame.scale(0.7, about_point=dens.get_center()) + + self.add(poss) + self.play( + FadeInFrom(prob, LEFT), + Write(implies, run_time=1) + ) + self.wait() + self.play(ShowCreation(cross)) + self.wait() + + self.play( + VGroup(implies, cross, prob).shift, UP, + FadeIn(implies2), + FadeIn(dens), + ) + self.wait() + + self.embed() + + +class DrawBigRect(Scene): + def construct(self): + rect = Rectangle(width=7, height=2.5) + rect.set_stroke(RED, 5) + rect.to_edge(RIGHT) + + words = TextMobject("Not how to\\\\think about it") + words.set_color(RED) + words.align_to(rect, LEFT) + words.to_edge(UP) + + arrow = Arrow( + words.get_bottom(), + rect.get_top(), + buff=0.25, + color=RED, + ) + + self.play(ShowCreation(rect)) + self.play( + FadeInFromDown(words), + GrowArrow(arrow), + ) + self.wait() + + class Thumbnail(Scene): def construct(self): - pass + dartboard = Dartboard() + axes = NumberPlane( + x_min=-1.25, + x_max=1.25, + y_min=-1.25, + y_max=1.25, + axis_config={ + "unit_size": 0.5 * dartboard.get_width(), + "tick_frequency": 0.25, + }, + x_line_frequency=1.0, + y_line_frequency=1.0, + ) + group = VGroup(dartboard, axes) + group.to_edge(LEFT, buff=0) + + # Arrow + arrow = Vector(DR, max_stroke_width_to_length_ratio=np.inf) + arrow.move_to(axes.c2p(PI / 10, np.exp(1) / 10), DR) + arrow.scale(1.5, about_edge=DR) + arrow.set_stroke(WHITE, 10) + + black_arrow = arrow.copy() + black_arrow.set_color(BLACK) + black_arrow.set_stroke(width=20) + + arrow.points[0] += 0.025 * DR + + # Coords + coords = TexMobject("(x, y) = (0.31415\\dots, 0.27182\\dots)") + coords.set_width(5.5) + coords.set_stroke(BLACK, 10, background=True) + coords.next_to(axes.get_bottom(), UP, buff=0) + + # Words + words = VGroup( + TextMobject("Probability = 0"), + TextMobject("$\\dots$but still possible"), + ) + for word in words: + word.set_width(6) + words.arrange(DOWN, buff=MED_LARGE_BUFF) + words.next_to(axes, RIGHT) + words.to_edge(UP, buff=LARGE_BUFF) + + # Pi + morty = Mortimer() + morty.to_corner(DR) + morty.change("confused", words) + + self.add(group) + self.add(black_arrow) + self.add(arrow) + self.add(coords) + self.add(words) + self.add(morty) + + self.embed() class Part2EndScreen(PatreonEndScreen): diff --git a/from_3b1b/active/bayes/beta3.py b/from_3b1b/active/bayes/beta3.py index f0e2eaf3..86ccbe13 100644 --- a/from_3b1b/active/bayes/beta3.py +++ b/from_3b1b/active/bayes/beta3.py @@ -1,22 +1,16 @@ from manimlib.imports import * from from_3b1b.active.bayes.beta_helpers import * from from_3b1b.active.bayes.beta1 import * -from from_3b1b.active.bayes.beta2 import * +from from_3b1b.active.bayes.beta2 import ShowLimitToPdf import scipy.stats OUTPUT_DIRECTORY = "bayes/beta3" -class WeightedCoin(Scene): +class RemindOfWeightedCoin(Scene): def construct(self): - # Coin grid - bools = 50 * [True] + 50 * [False] - random.shuffle(bools) - grid = get_coin_grid(bools) - - sorted_grid = VGroup(*grid) - sorted_grid.submobjects.sort(key=lambda m: m.symbol) + # Largely copied from beta2 # Prob label p_label = get_prob_coin_label() @@ -24,165 +18,164 @@ class WeightedCoin(Scene): p_label.to_edge(UP) rhs = p_label[-1] - rhs_box = SurroundingRectangle(rhs, color=RED) - rhs_label = TextMobject("Not necessarily") - rhs_label.next_to(rhs_box, DOWN, LARGE_BUFF) - rhs_label.to_edge(RIGHT) - rhs_label.match_color(rhs_box) + q_box = get_q_box(rhs) + p_label.add(q_box) - rhs_arrow = Arrow( - rhs_label.get_top(), - rhs_box.get_right(), - buff=SMALL_BUFF, - path_arc=60 * DEGREES, - color=rhs_box.get_color() - ) + self.add(p_label) - # Introduce coin - self.play(FadeIn( - grid, - run_time=2, - rate_func=linear, - lag_ratio=3 / len(grid), + # Coin grid + def get_random_coin_grid(p): + bools = np.random.random(100) < p + grid = get_coin_grid(bools) + return grid + + grid = get_random_coin_grid(0.5) + grid.next_to(p_label, DOWN, MED_LARGE_BUFF) + + self.play(LaggedStartMap( + FadeIn, grid, + lag_ratio=2 / len(grid), + run_time=3, )) self.wait() - self.play( - grid.set_height, 5, - grid.to_edge, DOWN, - FadeInFromDown(p_label) - ) - for coin in grid: - coin.generate_target() - sorted_coins = list(grid) - sorted_coins.sort(key=lambda m: m.symbol) - for c1, c2 in zip(sorted_coins, grid): - c1.target.move_to(c2) + # Label as h + brace = Brace(q_box, DOWN, buff=SMALL_BUFF) + h_label = TexMobject("h") + h_label.next_to(brace, DOWN) + eq = TexMobject("=") + eq.next_to(h_label, RIGHT) + h_decimal = DecimalNumber(0.5) + h_decimal.next_to(eq, RIGHT) self.play( - FadeIn(rhs_label, lag_ratio=0.1), - ShowCreation(rhs_arrow), - ShowCreation(rhs_box), - LaggedStartMap( - MoveToTarget, grid, - path_arc=30 * DEGREES, - lag_ratio=0.01, - ), + GrowFromCenter(brace), + FadeInFrom(h_label, UP), + grid.scale, 0.8, {"about_edge": DOWN}, ) + self.wait() # Alternate weightings - old_grid = VGroup(*sorted_coins) - rhs_junk_on_screen = True - for value in [0.2, 0.9, 0.31]: - n = int(100 * value) - new_grid = get_coin_grid([True] * n + [False] * (100 - n)) - new_grid.replace(grid) + tail_grid = get_random_coin_grid(0) + head_grid = get_random_coin_grid(1) + grid70 = get_random_coin_grid(0.7) + alt_grids = [tail_grid, head_grid, grid70] + for ag in alt_grids: + ag.replace(grid) - anims = [] - if rhs_junk_on_screen: - anims += [ - FadeOut(rhs_box), - FadeOut(rhs_label), - FadeOut(rhs_arrow), - ] - rhs_junk_on_screen = False + for coins in [grid, *alt_grids]: + for coin in coins: + coin.generate_target() + coin.target.rotate(90 * DEGREES, axis=UP) + coin.target.set_opacity(0) - self.wait() - self.play( - FadeOutAndShift( - old_grid, - 0.1 * DOWN, - lag_ratio=0.01, - run_time=1.5 - ), - FadeIn(new_grid, lag_ratio=0.01, run_time=1.5), - ChangeDecimalToValue(rhs, value), - *anims, - ) - old_grid = new_grid + def get_grid_swap_anims(g1, g2): + return [ + LaggedStartMap(MoveToTarget, g1, lag_ratio=0.02, run_time=1.5, remover=True), + LaggedStartMap(MoveToTarget, g2, lag_ratio=0.02, run_time=1.5, rate_func=reverse_smooth), + ] - long_rhs = DecimalNumber( - 0.31415926, - num_decimal_places=8, - show_ellipsis=True, - ) - long_rhs.match_height(rhs) - long_rhs.move_to(rhs, DL) - - self.play(ShowIncreasingSubsets(long_rhs, rate_func=linear)) - self.wait() - - # You just don't know - box = get_q_box(rhs) - - self.remove(rhs) self.play( - FadeOut(old_grid, lag_ratio=0.1), - FadeOutAndShift(long_rhs, 0.1 * RIGHT, lag_ratio=0.1), - Write(box), + FadeIn(eq), + UpdateFromAlphaFunc(h_decimal, lambda m, a: m.set_opacity(a)), + ChangeDecimalToValue(h_decimal, 0, run_time=2), + *get_grid_swap_anims(grid, tail_grid) ) - p_label.add(box) self.wait() - - # 7/10 heads - bools = [True] * 7 + [False] * 3 - random.shuffle(bools) - coins = VGroup(*[ - get_coin("H" if heads else "T") - for heads in bools - ]) - coins.arrange(RIGHT) - coins.set_height(0.7) - coins.next_to(p_label, DOWN, buff=LARGE_BUFF) - - heads_arrows = VGroup(*[ - Vector( - 0.5 * UP, - max_stroke_width_to_length_ratio=15, - max_tip_length_to_length_ratio=0.4, - ).next_to(coin, DOWN) - for coin in coins - if coin.symbol == "H" - ]) - numbers = VGroup(*[ - Integer(i + 1).next_to(arrow, DOWN, SMALL_BUFF) - for i, arrow in enumerate(heads_arrows) - ]) - - for coin in coins: - coin.save_state() - coin.stretch(0, 0) - coin.set_opacity(0) - - self.play(LaggedStartMap(Restore, coins), run_time=1) self.play( - ShowIncreasingSubsets(heads_arrows), - ShowIncreasingSubsets(numbers), - rate_func=linear, + ChangeDecimalToValue(h_decimal, 1, run_time=1.5), + *get_grid_swap_anims(tail_grid, head_grid) + ) + self.wait() + self.play( + ChangeDecimalToValue(h_decimal, 0.7, run_time=1.5), + *get_grid_swap_anims(head_grid, grid70) ) self.wait() - # Plot - # axes = scaled_pdf_axes(scale_factor=2) + # Graph axes = scaled_pdf_axes() axes.to_edge(DOWN, buff=MED_SMALL_BUFF) axes.y_axis.numbers.set_opacity(0) axes.y_axis_label.set_opacity(0) + h_lines = VGroup() + for y in range(15): + h_line = Line(axes.c2p(0, y), axes.c2p(1, y)) + h_lines.add(h_line) + h_lines.set_stroke(WHITE, 0.5, opacity=0.5) + axes.add(h_lines) + x_axis_label = p_label[:4].copy() x_axis_label.set_height(0.4) x_axis_label.next_to(axes.c2p(1, 0), UR, buff=SMALL_BUFF) axes.x_axis.add(x_axis_label) - n_heads = 7 - n_tails = 3 - graph = get_beta_graph(axes, n_heads, n_tails) - dist = scipy.stats.beta(n_heads + 1, n_tails + 1) - true_graph = axes.get_graph(dist.pdf) + n_heads_tracker = ValueTracker(3) + n_tails_tracker = ValueTracker(3) - region = get_region_under_curve(axes, true_graph, 0.6, 0.8) - region.set_fill(GREY, 0.85) - region.set_stroke(YELLOW, 1) + def get_graph(axes=axes, nht=n_heads_tracker, ntt=n_tails_tracker): + dist = scipy.stats.beta(nht.get_value() + 1, ntt.get_value() + 1) + graph = axes.get_graph(dist.pdf, step_size=0.05) + graph.set_stroke(BLUE, 3) + graph.set_fill(BLUE_E, 1) + return graph + + graph = always_redraw(get_graph) + + area_label = TextMobject("Area = 1") + area_label.set_height(0.5) + area_label.move_to(axes.c2p(0.5, 1)) + + # pdf label + pdf_label = TextMobject("probability ", "density ", "function") + pdf_label.next_to(axes.input_to_graph_point(0.5, graph), UP) + pdf_target_template = TextMobject("p", "d", "f") + pdf_target_template.next_to(axes.input_to_graph_point(0.7, graph), UR) + pdf_label.generate_target() + for part, letter2 in zip(pdf_label.target, pdf_target_template): + for letter1 in part: + letter1.move_to(letter2) + part[1:].set_opacity(0) + + # Add plot + self.add(axes, *self.mobjects) + self.play( + FadeOut(eq), + FadeOut(h_decimal), + LaggedStartMap(MoveToTarget, grid70, run_time=1, remover=True), + FadeIn(axes), + ) + self.play( + DrawBorderThenFill(graph), + FadeIn(area_label, rate_func=squish_rate_func(smooth, 0.5, 1), run_time=2), + Write(pdf_label, run_time=1), + ) + self.wait() + + # Region + lh_tracker = ValueTracker(0.7) + rh_tracker = ValueTracker(0.7) + + def get_region(axes=axes, graph=graph, lh_tracker=lh_tracker, rh_tracker=rh_tracker): + lh = lh_tracker.get_value() + rh = rh_tracker.get_value() + region = get_region_under_curve(axes, graph, lh, rh) + region.set_fill(GREY, 0.85) + region.set_stroke(YELLOW, 1) + return region + + region = always_redraw(get_region) + + region_area_label = DecimalNumber(num_decimal_places=3) + region_area_label.next_to(axes.c2p(0.7, 0), UP, MED_LARGE_BUFF) + + def update_ra_label(label, nht=n_heads_tracker, ntt=n_tails_tracker, lht=lh_tracker, rht=rh_tracker): + dist = scipy.stats.beta(nht.get_value() + 1, ntt.get_value() + 1) + area = dist.cdf(rht.get_value()) - dist.cdf(lht.get_value()) + label.set_value(area) + + region_area_label.add_updater(update_ra_label) range_label = VGroup( TexMobject("0.6 \\le"), @@ -203,73 +196,119 @@ class WeightedCoin(Scene): pp_label.arrange(RIGHT, buff=SMALL_BUFF) pp_label.move_to(axes.c2p(0.3, 3)) - self.play( - FadeOut(heads_arrows), - FadeOut(numbers), - Write(axes), - DrawBorderThenFill(graph), - ) self.play( FadeIn(pp_label[::2]), - FadeIn(region), + MoveToTarget(pdf_label), + FadeOut(area_label), ) self.wait() self.play(TransformFromCopy(p_label[:4], range_label[1])) - self.play(GrowFromPoint(range_label[0], axes.c2p(0.6, 0))) - self.play(GrowFromPoint(range_label[2], axes.c2p(0.8, 0))) + self.wait() + self.play(TransformFromCopy(axes.x_axis.numbers[2], range_label[0])) + self.play(TransformFromCopy(axes.x_axis.numbers[3], range_label[2])) self.wait() - # Remind what the title is - title = TextMobject( - "Probabilities", "of", "Probabilities" + self.add(region) + self.play( + lh_tracker.set_value, 0.6, + rh_tracker.set_value, 0.8, + UpdateFromAlphaFunc( + region_area_label, + lambda m, a: m.set_opacity(a), + rate_func=squish_rate_func(smooth, 0.25, 1) + ), + run_time=3, + ) + self.wait() + + # 7/10 heads + bools = [True] * 7 + [False] * 3 + random.shuffle(bools) + coins = VGroup(*[ + get_coin("H" if heads else "T") + for heads in bools + ]) + coins.arrange(RIGHT) + coins.set_height(0.7) + coins.next_to(h_label, DOWN, buff=MED_LARGE_BUFF) + + heads = [c for c in coins if c.symbol == "H"] + numbers = VGroup(*[ + Integer(i + 1).set_height(0.2).next_to(coin, DOWN, SMALL_BUFF) + for i, coin in enumerate(heads) + ]) + + for coin in coins: + coin.save_state() + coin.rotate(90 * DEGREES, UP) + coin.set_opacity(0) + + pp_label.generate_target() + pp_label.target.set_height(0.5) + pp_label.target.next_to(axes.c2p(0, 2), RIGHT, MED_LARGE_BUFF) + + self.play( + LaggedStartMap(Restore, coins), + MoveToTarget(pp_label), + run_time=1, + ) + self.play(ShowIncreasingSubsets(numbers)) + self.wait() + + # Move plot + self.play( + n_heads_tracker.set_value, 7, + n_tails_tracker.set_value, 3, + FadeOut(pdf_label, rate_func=squish_rate_func(smooth, 0, 0.5)), + run_time=2 ) - title.arrange(DOWN, aligned_edge=LEFT) - title.next_to(axes.c2p(0, 0), UR, buff=MED_LARGE_BUFF) - title.align_to(pp_label, LEFT) - - self.play(ShowIncreasingSubsets(title, rate_func=linear)) self.wait() - self.play(FadeOut(title)) # How does the answer change with more data new_bools = [True] * 63 + [False] * 27 random.shuffle(new_bools) - bools += new_bools + bools = [c.symbol == "H" for c in coins] + new_bools grid = get_coin_grid(bools) grid.set_height(3.5) + grid.next_to(axes.c2p(0, 3), RIGHT, MED_LARGE_BUFF) - pp_label.generate_target() - pp_label.target.next_to(p_label, DOWN, MED_LARGE_BUFF) - pp_label.target.match_x(pp_label) - rhs.set_opacity(0) - - grid.next_to(pp_label.target, DOWN, MED_LARGE_BUFF, aligned_edge=LEFT) - - self.remove(coins) - self.add(p_label) self.play( - ReplacementTransform(coins, grid[:10], path_arc=-30 * DEGREES), + FadeOut(numbers), + ReplacementTransform(coins, grid[:10]), + ) + self.play( FadeIn(grid[10:], lag_ratio=0.1, rate_func=linear), - MoveToTarget(pp_label, path_arc=-30 * DEGREES), - p_label.align_to, pp_label.target, LEFT, + pp_label.next_to, grid, DOWN, + ) + self.wait() + self.add(graph, region, region_area_label, p_label, q_box, brace, h_label) + self.play( + n_heads_tracker.set_value, 70, + n_tails_tracker.set_value, 30, + ) + self.wait() + origin = axes.c2p(0, 0) + self.play( + axes.y_axis.stretch, 0.5, 1, {"about_point": origin}, + h_lines.stretch, 0.5, 1, {"about_point": origin}, ) self.wait() - n_heads = 70 - n_tails = 30 - graph100 = get_beta_graph(axes, n_heads, n_tails) - dist = scipy.stats.beta(n_heads + 1, n_tails + 1) - true_graph100 = axes.get_graph(dist.pdf, step_size=0.01) - region100 = get_region_under_curve(axes, true_graph100, 0.6, 0.8) - region100.match_style(region) - - self.play( - graph.set_opacity, 0.2, - region.set_opacity, 0.2, - FadeIn(graph100), - FadeIn(region100), - ) - self.play(FadeIn(region100)) + # Shift the shape around + pairs = [ + (70 * 3, 30 * 3), + (35, 15), + (35 + 20, 15 + 20), + (7, 3), + (70, 30), + ] + for nh, nt in pairs: + self.play( + n_heads_tracker.set_value, nh, + n_tails_tracker.set_value, nt, + run_time=2, + ) + self.wait() # End self.embed() @@ -300,6 +339,110 @@ class ComplainAboutSimplisticModel(ExternallyAnimatedScene): pass +class BayesianFrequentistDivide(Scene): + def construct(self): + # Setup Bayesian vs. Frequentist divide + b_label = TextMobject("Bayesian") + f_label = TextMobject("Frequentist") + labels = VGroup(b_label, f_label) + for label, vect in zip(labels, [LEFT, RIGHT]): + label.set_height(0.7) + label.move_to(vect * FRAME_WIDTH / 4) + label.to_edge(UP, buff=0.35) + + h_line = Line(LEFT, RIGHT) + h_line.set_width(FRAME_WIDTH) + h_line.next_to(labels, DOWN) + v_line = Line(UP, DOWN) + v_line.set_height(FRAME_HEIGHT) + v_line.center() + + for label in labels: + label.save_state() + label.set_y(0) + self.play( + FadeInFrom(label, -normalize(label.get_center())), + ) + self.wait() + self.play( + ShowCreation(VGroup(v_line, h_line)), + *map(Restore, labels), + ) + self.wait() + + # Overlay ShowBayesianUpdating in editing + # Frequentist list (ignore?) + kw = { + "tex_to_color_map": { + "$p$-value": YELLOW, + "$H_0$": PINK, + "$\\alpha$": BLUE, + }, + "alignment": "", + } + freq_list = VGroup( + TextMobject("1. State a null hypothesis $H_0$", **kw), + TextMobject("2. Choose a test statistic,\\\\", "$\\qquad$ compute its value", **kw), + TextMobject("3. Calculate a $p$-value", **kw), + TextMobject("4. Choose a significance value $\\alpha$", **kw), + TextMobject("5. Reject $H_0$ if $p$-value\\\\", "$\\qquad$ is less than $\\alpha$", **kw), + ) + + freq_list.set_width(0.5 * FRAME_WIDTH - 1) + freq_list.arrange(DOWN, buff=MED_LARGE_BUFF, aligned_edge=LEFT) + freq_list.move_to(FRAME_WIDTH * RIGHT / 4) + freq_list.to_edge(DOWN, buff=LARGE_BUFF) + + # Frequentist icon + axes = get_beta_dist_axes(y_max=5, y_unit=1) + axes.set_width(0.5 * FRAME_WIDTH - 1) + axes.move_to(FRAME_WIDTH * RIGHT / 4 + DOWN) + + dist = scipy.stats.norm(0.5, 0.1) + graph = axes.get_graph(dist.pdf) + graphs = VGroup() + for x_min, x_max in [(0, 0.3), (0.3, 0.7), (0.7, 1.0)]: + graph = axes.get_graph(dist.pdf, x_min=x_min, x_max=x_max) + graph.add_line_to(axes.c2p(x_max, 0)) + graph.add_line_to(axes.c2p(x_min, 0)) + graph.add_line_to(graph.get_start()) + graphs.add(graph) + + graphs.set_stroke(width=0) + graphs.set_fill(RED, 1) + graphs[1].set_fill(GREY_D, 1) + + H_words = VGroup(*[TextMobject("Reject\\\\$H_0$") for x in range(2)]) + for H_word, graph, vect in zip(H_words, graphs[::2], [RIGHT, LEFT]): + H_word.next_to(graph, UP, MED_LARGE_BUFF) + arrow = Arrow( + H_word.get_bottom(), + graph.get_center() + 0.75 * vect, + buff=SMALL_BUFF + ) + H_word.add(arrow) + + H_words.set_color(RED) + self.add(H_words) + + self.add(axes) + self.add(graphs) + + self.embed() + + # Transition to 2x2 + # Go back to prior + # Label uniform prior + # Talk about real coin prior + # Update ad infinitum + + +class ArgumentBetweenBayesianAndFrequentist(Scene): + def construct(self): + pass + + +# From version 1 class ShowBayesianUpdating(Scene): CONFIG = { "true_p": 0.72, @@ -597,7 +740,6 @@ class ShowBayesianUpdating(Scene): return p_label, prob, prob_box -# From version 1 class HighlightReviewPartsReversed(HighlightReviewParts): CONFIG = { "reverse_order": True,