# Der Quellcode zum Video 'Elementare Momente' # # wurde entwickelt von # # Oğulcan Aşık # Chavoush Kalhor # Christian Müller # # an der Heinrich-Heine-Universität Düsseldorf. # # Dieses Werk ist lizenziert unter einer Creative Commons Namensnennung-Weitergabe unter gleichen Bedingungen 4.0 International Lizenz. # Um eine Kopie der Lizenz zu erhalten, besuchen Sie https://creativecommons.org/licenses/by-sa/4.0/. # # SPDX-License-Identifier: CC-BY-SA-4.0 from manim import * from typing import Iterable import math from math import sqrt ORCAblue = "#002b44" ORCAred = "#e61300" ORCAgreen = "#4cb011" ORCAgrey = "#f5f5f5" ORCAwhite = "#ffffff" config.background_color = "#c4d1d7" config.max_files_cached = -1 # video sections and learning outcomes sections = Tex( r"\textsf{\textbf{Elementare Momente}}\\", r"\textsf{\textbf{Kapitel 1: \\ Der Erwartungswert}}\\", r"\textsf{\textbf{Kapitel 2: \\ Rechenregeln für den Erwartungswert}}\\", r"\textsf{\textbf{Kapitel 3: \\ Die Varianz}}\\", r"\textsf{\textbf{Kapitel 4: \\ Rechenregeln für die Varianz}}\\", r"\textsf{\textbf{Zusammenfassung}}", color=ORCAblue, ) outcomes = Tex( r"\textsf{\textbf{In diesem Video}}", r"\begin{itemize} \item \textsf{Bedeutung von Erwartungswert und Varianz verstehen} \end{itemize}", r"\begin{itemize} \item \textsf{Formeln für Erwartungswert und Varianz motivieren} \end{itemize}", r"\begin{itemize} \item \textsf{grundlegende Rechenregeln herleiten} \end{itemize}", color=ORCAblue, ) # animate video sections and learning outcomes class MomentsSection0(Scene): # scene 0: introduction and learning outcomes def construct(self): sections[0].set(font_size=56).move_to(ORIGIN) outcomes[0].to_edge(UL) outcomes[1:4].to_edge(LEFT) logo_license = SVGMobject(file_name="..\Anderes\Logo_CC_BY_SA.svg", width=1.5) self.add(logo_license.to_edge(DR)) self.wait() self.play(Write(sections[0])) self.wait() self.play(FadeOut(sections[0])) self.wait() self.play(FadeIn(outcomes[0])) self.wait() self.play(FadeIn(outcomes[1])) self.wait() self.play(FadeIn(outcomes[2])) self.wait() self.play(FadeIn(outcomes[3])) self.wait() self.play(*[FadeOut(mobject) for mobject in self.mobjects]) class MomentsSection1(Scene): def construct(self): sections[1].move_to(ORIGIN) self.wait() self.play(Write(sections[1])) self.wait() self.play(FadeOut(sections[1])) self.wait() class MomentsSection2(Scene): def construct(self): sections[2].move_to(ORIGIN) self.wait() self.play(Write(sections[2])) self.wait() self.play(FadeOut(sections[2])) self.wait() class MomentsSection3(Scene): def construct(self): sections[3].move_to(ORIGIN) self.wait() self.play(Write(sections[3])) self.wait() self.play(FadeOut(sections[3])) self.wait() class MomentsSection4(Scene): def construct(self): sections[4].move_to(ORIGIN) self.wait() self.play(Write(sections[4])) self.wait() self.play(FadeOut(sections[4])) self.wait() class MomentsSection5(Scene): def construct(self): sections[5].move_to(ORIGIN) self.wait() self.play(Write(sections[5])) self.wait() self.play(FadeOut(sections[5])) self.wait() # begin with regular animations # background axes axes_global = Axes( x_range=[0, 7, 1], y_range=[0, 0.5, 1], x_length=7, y_length=3, tips=False, axis_config={"include_ticks": False, "color": ORCAblue}, x_axis_config={"stroke_width": 4}, y_axis_config={"stroke_width": 0}, ).add_coordinates(range(7), color=ORCAblue) # triangle marker for expected value ev_indicator = ( Triangle( fill_color=ORCAblue, fill_opacity=0.5, ) .set_stroke(ORCAblue, width=1) .scale(0.5) .move_to(axes_global.get_x_axis().number_to_point(3.5) + DOWN * 0.4) ) # function for drawing bar charts def bar_chart( axes, x_values: Iterable[float], y_values: Iterable[float], x_range, bar_width=1, **kwargs, ): if len(x_values) == len(y_values): bars = VGroup() for i, val in enumerate(zip(x_values, y_values)): x, y = val[0:2] if x < x_range[0] or x > x_range[1]: continue if i == 0: bar_w = min( abs(axes.c2p(x, 0)[0] - axes.c2p(x_values[i + 1], 0)[0]), abs(axes.c2p(x, 0)[0] - axes.c2p(x_range[0], 0)[0]), ) elif i == len(x_values) - 1: bar_w = min( abs(axes.c2p(x, 0)[0] - axes.c2p(x_values[i - 1], 0)[0]), abs(axes.c2p(x, 0)[0] - axes.c2p(x_range[1], 0)[0]), ) else: bar_w = min( abs(axes.c2p(x_values[i + 1], 0)[0] - axes.c2p(x, 0)[0]), abs(axes.c2p(x, 0)[0] - axes.c2p(x_values[i - 1], 0)[0]), ) bar_h = axes.c2p(0, y)[1] - axes.c2p(0, 0)[1] bar = Rectangle(height=bar_h, width=bar_w, **kwargs).set_fill(ORCAblue, 0.7) bar.move_to(axes.c2p(x, 0), DOWN) bar.stretch(bar_width, 0) bars.add(bar) return bars # function for calculating expected value def expected_value(values, probabilities, stretch=1, shift=0): if len(values) == len(probabilities): sum = 0 for val, prob in zip(values, probabilities): sum += prob * (val * stretch + shift) return sum # function for calculating variance def variance(values, probabilities, stretch=1, shift=0): if len(values) == len(probabilities): ev = expected_value(values, probabilities, stretch, shift) sum = 0 for val, prob in zip(values, probabilities): sum += prob * (val * stretch + shift - ev) ** 2 return sum # function to draw line with ends def line_with_ends( start=LEFT, end=RIGHT, buff=0, path_arc=None, color=ORCAblue, end_length=0.3, **kwargs, ): line = Line(start=start, end=end, buff=buff, path_arc=path_arc, **kwargs) # phi = line.get_angle() # horizontal = PI end1 = Line(start=start + end_length / 2 * UP, end=start + end_length / 2 * DOWN) end2 = Line(start=end + end_length / 2 * UP, end=end + end_length / 2 * DOWN) return VGroup(line, end1, end2).set_color(color) # define probability distributions x_vals = [1, 2, 3, 4, 5, 6] y_vals = [ [0.1, 0.2, 0.1, 0.35, 0.2, 0.05], [0.275, 0.225, 0, 0, 0.225, 0.275], [0, 0.3, 0.2, 0.2, 0.3, 0], ] # draw bar charts for each distribution bars0 = bar_chart( axes_global, x_values=x_vals, y_values=y_vals[0], x_range=[0, 7], bar_width=0.5, color=ORCAblue, ) bars1 = bar_chart( axes_global, x_values=x_vals, y_values=y_vals[1], x_range=[0, 7], bar_width=0.5, color=ORCAblue, ) bars2 = bar_chart( axes_global, x_values=x_vals, y_values=y_vals[2], x_range=[0, 7], bar_width=0.5, color=ORCAblue, ) # calculate expected value and variance for each distribution ev0 = expected_value(x_vals, y_vals[0]) ev1 = expected_value(x_vals, y_vals[1]) ev2 = expected_value(x_vals, y_vals[2]) var0 = variance(x_vals, y_vals[0]) var1 = variance(x_vals, y_vals[1]) var2 = variance(x_vals, y_vals[2]) var0_adjusted = var0 / 62.5 var1_adjusted = var1 / 62.5 var2_adjusted = var2 / 62.5 class Moments1(Scene): # scene 1: expected value motivation def construct(self): # write out probability density function distribution = ( VGroup( *[ MathTex("P(X = ", x_vals[j], f") = {y_vals[0][j]:.2f}", color=ORCAblue, font_size=40) for j in range(6) ] ) .arrange(DOWN) .to_edge(LEFT) ) self.wait() self.play(Write(distribution)) self.wait() # set up and transition to next scene bars = BarChart( values=y_vals[0], bar_names=x_vals, y_range=[0, 0.5, 0.1], x_length=5, y_length=3, x_axis_config={"font_size": 40, "include_ticks": False}, ).set_color(ORCAblue) bars.y_axis.set_opacity(0) bars[1][1].set_opacity(0) self.play( AnimationGroup( *[distribution[i][1].copy().animate.move_to(bars[1][1][i]) for i in range(len(distribution))] ) ) self.play(Write(bars), run_time=2) self.wait() self.play(FadeOut(distribution)) self.wait() class Moments2(Scene): # scene 2 (non-physics version): expected value formula def construct(self): bars_prev = BarChart( values=y_vals[0], bar_names=x_vals, y_range=[0, 0.5, 0.1], x_length=5, y_length=3, x_axis_config={"font_size": 40, "include_ticks": False}, ).set_color(ORCAblue) bars_prev.y_axis.set_opacity(0) # bar chart of probability distribution bars = BarChart( values=y_vals[0], bar_names=x_vals, y_range=[0, 0.5, 0.1], x_length=5, y_length=3, x_axis_config={"font_size": 40, "include_ticks": False}, ).set_color(ORCAblue) # hide y-axis of bar chart bars.y_axis.set_opacity(0) # initial rotation of bar chart lastDegree = -9 bars.rotate(math.radians(lastDegree), about_point=DOWN * 1.3 + RIGHT * 0.3) # triangle marker for expected value ev_indicator = ( Triangle( fill_color=ORCAblue, fill_opacity=0.5, ) .set_stroke(ORCAblue, width=1) .scale(0.5) .move_to(bars.x_axis.number_to_point(0) + DOWN * 0.4) ) graph = VGroup(bars, ev_indicator) # transition from last scene's chart to current one self.add(bars_prev) self.wait() self.play(ReplacementTransform(bars_prev, bars), Create(ev_indicator)) self.wait() # indicate realizations and probabilities self.play(Indicate(bars.x_axis, color=None)) self.wait() self.play(Indicate(bars.bars, color=None)) self.wait() # reminder that the sum of all probabilities is 1 sum_of_probabilities = MathTex(r"\sum_i p_i = 1") sum_of_probabilities.set_color(ORCAblue).to_edge(UP) self.play(FadeIn(sum_of_probabilities)) self.wait() self.play(FadeOut(sum_of_probabilities)) self.wait() # add updaters to triangle to move it along x-axis x = ValueTracker(0) ev_indicator.add_updater( lambda m: m.move_to(bars.x_axis.number_to_point(x.get_value()) + DOWN * 0.4) ) ev_indicator.update() # function for rotating graph and moving triangle def animateTriangle(xVal): degrees = int(xVal - x.get_value()) * 3 - 9 - lastDegree self.lastDegree = int(xVal - x.get_value()) * 3 - 9 self.wait() self.play( bars.animate.rotate( math.radians(degrees), about_point=DOWN * 1.3 + RIGHT * 0.3 ), x.animate.set_value(xVal), ) self.wait() # animate rotation animateTriangle(6) animateTriangle(1) animateTriangle(5) animateTriangle(2) animateTriangle(4) animateTriangle(3) ev_indicator.clear_updaters() self.play(graph.animate.shift(UP * 2)) self.wait() # ev calculation ev_calculation1 = MathTex(r"x_i - A") ev_calculation2 = MathTex(r"({{x_i - A}}) {{p_i}}") ev_calculation3 = MathTex(r"{{\sum_i}} {{(x_i - A) p_i}}") ev_calculation4 = MathTex(r"{{\sum_i}} {{(x_i - A) p_i}} = 0") ev_calculation5 = MathTex(r"{{\sum_i x_i p_i}} - {{\sum_i}} {{A}} {{p_i}} = 0") ev_calculation6 = MathTex(r"{{\sum_i x_i p_i}} = {{\sum_i}} {{A}} {{p_i}}") ev_calculation7 = MathTex(r"{{\sum_i x_i p_i}} = {{A}} {{\sum_i}} {{p_i}}") ev_calculation8 = MathTex(r"{{\sum_i x_i p_i}} = {{A}} {{\cdot}} {{1}}") ev_calculation9 = MathTex(r"{{\sum_i x_i p_i}} = {{A}}") ev_calculation10 = MathTex(r"{{\textsf{E}[X]}} = {{\sum_i x_i p_i}}") ev_calculation1.set_color(ORCAblue).to_edge(UP) ev_calculation2.set_color(ORCAblue).to_edge(UP) ev_calculation3.set_color(ORCAblue).to_edge(UP) ev_calculation4.set_color(ORCAblue).to_edge(UP) ev_calculation5.set_color(ORCAblue).to_edge(UP).shift(DOWN) ev_calculation6.set_color(ORCAblue).to_edge(UP).shift(DOWN) ev_calculation7.set_color(ORCAblue).to_edge(UP).shift(DOWN) ev_calculation8.set_color(ORCAblue).to_edge(UP).shift(DOWN) ev_calculation9.set_color(ORCAblue).to_edge(UP).shift(DOWN) ev_calculation10.set_color(ORCAblue).to_edge(UP).shift(DOWN) # horizontal lines hLines = VGroup() for val, i in zip(x_vals, range(len(x_vals))): hLines += line_with_ends( start=bars.get_x_axis().number_to_point(ev0 - 0.5), end=bars.get_x_axis().number_to_point(val - 0.5), ).shift((0.5 * i + 1) * DOWN) # staple each line to its start point and change colors for line in hLines: if line[0].get_left()[0] < bars.get_x_axis().number_to_point(ev0 - 0.5)[0]: line.set_color(ORCAred) line.add_updater( lambda m: m.align_to( bars.get_x_axis().number_to_point(ev0 - 0.5), RIGHT, LEFT, ) ) else: line.set_color(ORCAgreen) line.add_updater( lambda m: m.align_to( bars.get_x_axis().number_to_point(ev0 - 0.5), LEFT, LEFT, ) ) # start calculation self.play(FadeIn(ev_calculation1), Create(hLines)) self.wait() # multiply by probability self.play( *[line.animate.stretch(y, 0) for line, y in zip(hLines, y_vals[0])], TransformMatchingTex(ev_calculation1, ev_calculation2), run_time=2, ) self.wait() # take sum self.play( hLines.animate.arrange(buff=0).shift(DOWN * 2), TransformMatchingShapes(ev_calculation2, ev_calculation3), run_time=2, ) self.wait() # set sum to zero self.play( ShrinkToCenter(hLines), TransformMatchingShapes(ev_calculation3, ev_calculation4), ) self.wait() # add label to triangle marker self.play( ev_calculation4.animate.shift(DOWN), graph.animate.shift(DOWN * 2), ) self.wait() ev_indicator_label = MathTex("A") ev_indicator_label.set_color(ORCAblue).next_to(ev_indicator, DOWN * 0.4) self.play(FadeIn(ev_indicator_label)) self.wait() # finish calculation self.play(TransformMatchingShapes(ev_calculation4, ev_calculation5), run_time=2) self.wait() self.play(TransformMatchingShapes(ev_calculation5, ev_calculation6), run_time=2) self.wait() self.play(TransformMatchingTex(ev_calculation6, ev_calculation7), run_time=2) self.wait() self.play(TransformMatchingTex(ev_calculation7, ev_calculation8), run_time=2) self.wait() self.play(TransformMatchingShapes(ev_calculation8, ev_calculation9), run_time=2) self.wait() # show definition and draw box ev_definition = MathTex(r"\textbf{\textsf{Erwartungswert}}") ev_definition.set_color(ORCAblue).to_edge(UP) ev_definition_label = MathTex(r"\textsf{E}[X]") ev_definition_label.set_color(ORCAblue).next_to(ev_indicator, DOWN * 0.4) ev_definition_box = SurroundingRectangle( VGroup(ev_definition, ev_calculation10), color=ORCAblue ) self.play( TransformMatchingTex(ev_calculation9, ev_calculation10), ReplacementTransform(ev_indicator_label, ev_definition_label), run_time=2, ) self.wait() self.play(FadeIn(ev_definition)) self.wait() self.play(Create(ev_definition_box)) self.wait() self.play(FadeOut(*self.mobjects)) self.wait() # display type of affine transformation depending on animation (applies to Moments3 and Moments7) reminder_shift = Tex(r"\textsf{Verschiebung}") reminder_shift.set_color(ORCAblue).to_edge(UL) reminder_stretch = Tex(r"\textsf{Skalierung}") reminder_stretch.set_color(ORCAblue).to_edge(UL).shift(DOWN * 0.75) class Moments3(Scene): # scene 3: expected value calculation rules def construct(self): # background grid plane = NumberPlane( x_range=[-20, 20, 1], y_range=[-3, 3, 1], background_line_style={"stroke_color": GRAY}, axis_config={"stroke_color": DARK_GRAY, "stroke_width": 3}, ).shift(DOWN + LEFT * 3.5) # stretch and shift parameter affecting ev_indicator, ev_arrow a = ValueTracker(1) b = ValueTracker(0) # stretch and shift parameter affecting bars, a_arrow, b_arrow, ab_arrow a_bar = ValueTracker(1) b_bar = ValueTracker(0) # tilt bars: rotate by r and shift down by s r = ValueTracker(0) s = ValueTracker(0) # tilt line: rotate by r and shift down by sLine sLine = ValueTracker(1) # axes for bar chart axes = ( Axes( x_range=[0, 7, 1], y_range=[0, 0.5, 1], x_length=7, y_length=3, tips=False, axis_config={"include_ticks": False, "color": ORCAblue}, x_axis_config={"stroke_width": 1}, y_axis_config={"stroke_width": 0}, ) .add_coordinates(range(10), color=ORCAblue) .shift(UP * 0.5) ) # bar chart of distribution bars = always_redraw( lambda: bar_chart( axes, x_values=np.array(x_vals) * a_bar.get_value() + b_bar.get_value(), y_values=y_vals[0], x_range=[0, 7 * a_bar.get_value() + b_bar.get_value()], bar_width=0.5 / a_bar.get_value(), color=ORCAblue, ) .rotate(math.radians(r.get_value())) .shift(DOWN * s.get_value()) ) # base line under bars line = always_redraw( lambda: Line( [-4 / a_bar.get_value() + b_bar.get_value(), 0, 0], [4 * a_bar.get_value() + b_bar.get_value(), 0, 0], color=ORCAblue, ) .rotate(math.radians(r.get_value())) .shift(DOWN * sLine.get_value()) ) # triangle marker for expected value ev_indicator = always_redraw( lambda: Triangle( fill_color=ORCAblue, fill_opacity=0.5, ) .set_stroke(ORCAblue, width=1) .scale(0.5) .move_to( axes.get_x_axis().number_to_point( expected_value(x_vals, y_vals[0], a.get_value(), b.get_value()) ) + 0.4 * DOWN ) ) # arrow marker for expected value ev_arrow = always_redraw( lambda: Arrow( start=LEFT * 3.5, end=LEFT * ( 3.5 - expected_value(x_vals, y_vals[0], a.get_value(), b.get_value()) ), color=ORCAblue, buff=0, ).shift(DOWN * 2) ) # add label to arrow tip ev_arrow_label = MathTex(r"\textsf{E}[X]", color=ORCAblue).add_updater( lambda m: m.next_to(ev_arrow, DOWN, buff=0) ) ev_arrow_label.update() # arrow marker for standard deviation sd_line = always_redraw( lambda: Line( start=ev_indicator.get_bottom(), end=ev_indicator.get_bottom() + 2.5 * DOWN, color=ORCAblue, ) ) # build up scene self.play(Write(plane)) self.play(FadeIn(axes, bars, line, ev_indicator)) self.wait() self.play(Create(sd_line)) self.play(GrowArrow(ev_arrow)) self.play(Write(ev_arrow_label)) self.wait() # arrow marker for shift b_arrow = always_redraw( lambda: Arrow( start=RIGHT, end=RIGHT * (1 + b_bar.get_value()), color=ORCAred, buff=0, ).shift(UP + LEFT * 3.5) ) # add label to arrow tip b_arrow_label = MathTex("x + b", color=ORCAred).add_updater( lambda m: m.next_to(b_arrow, UP) ) b_arrow_label.update() # animate shift and tilt of bars self.play(FadeIn(reminder_shift)) self.wait() self.add(b_arrow) self.play( b_bar.animate.set_value(1), FadeIn(b_arrow_label), ) self.play( r.animate.set_value(-12), s.animate.set_value(0.2), sLine.animate.set_value(1.2), ) self.wait() # animate shift of expected value marker to correct the tilt self.play( b.animate.set_value(1), r.animate.set_value(0), s.animate.set_value(0), sLine.animate.set_value(1.0), ev_arrow_label.animate.become( MathTex(r"\textsf{E}[X + b]", color=ORCAblue) ), ) self.wait() # CUT TO EV CALCULATION SHIFT WHEN EDITING (Moments3a) # show formula ev_formula_shift = MathTex(r"\textsf{E}[X + b] = \textsf{E}[X] + b") ev_formula_shift.set_color(ORCAblue).to_edge(UP) self.play(FadeIn(ev_formula_shift)) self.wait() # draw box ev_box_shift = SurroundingRectangle(ev_formula_shift, color=ORCAblue) self.play(Create(ev_box_shift)) self.wait() # change ev_arrow_label self.play( ev_arrow_label.animate.become(MathTex(r"\textsf{E}[X] + b", color=ORCAblue)) ) self.wait() # fade out box self.play(FadeOut(ev_formula_shift, ev_box_shift)) self.wait() # reset self.play( b.animate.set_value(0), b_bar.animate.set_value(0), ev_arrow_label.animate.become(MathTex(r"\textsf{E}[X]", color=ORCAblue)), FadeOut(b_arrow_label), FadeOut(reminder_shift), ) self.remove(b_arrow) self.wait() # arrow marker for stretch a_arrow = always_redraw( lambda: Arrow( start=RIGHT, end=RIGHT * (a_bar.get_value() + 0), color=ORCAred, buff=0, ).shift(UP + LEFT * 3.5) ) # add label to arrow tip a_arrow_label = MathTex("ax", color=ORCAred).add_updater( lambda m: m.next_to(a_arrow, UP) ) a_arrow_label.update() # animate stretch and tilt of bars self.play(FadeIn(reminder_stretch)) self.wait() self.add(a_arrow) self.play( a_bar.animate.set_value(1.5), FadeIn(a_arrow_label), ) self.play( r.animate.set_value(-12), s.animate.set_value(0.4), sLine.animate.set_value(1.4), ) self.wait() # animate stretch of expected value marker to correct the tilt self.play( a.animate.set_value(1.5), r.animate.set_value(0), s.animate.set_value(0), sLine.animate.set_value(1.0), ev_arrow_label.animate.become(MathTex(r"\textsf{E}[aX]", color=ORCAblue)), ) self.wait() # CUT TO EV CALCULATION STRETCH WHEN EDITING (Moments3b) # show formula ev_formula_stretch = MathTex(r"\textsf{E}[aX] = a\textsf{E}[X]") ev_formula_stretch.set_color(ORCAblue).to_edge(UP) self.play(FadeIn(ev_formula_stretch)) self.wait() # draw box ev_box_stretch = SurroundingRectangle(ev_formula_stretch, color=ORCAblue) self.play(Create(ev_box_stretch)) self.wait() # change ev_arrow_label self.play( ev_arrow_label.animate.become(MathTex(r"a\textsf{E}[X]", color=ORCAblue)) ) self.wait() # fade out box self.play(FadeOut(ev_formula_stretch, ev_box_stretch)) self.wait() # reset self.play( a.animate.set_value(1), a_bar.animate.set_value(1), ev_arrow_label.animate.become(MathTex(r"\textsf{E}[X]", color=ORCAblue)), FadeOut(a_arrow_label), FadeOut(reminder_stretch), ) self.remove(a_arrow) self.wait() # arrow marker for stretch and shift ab_arrow = always_redraw( lambda: Arrow( start=RIGHT, end=RIGHT * (a_bar.get_value() + b_bar.get_value()), color=ORCAred, buff=0, ).shift(UP + LEFT * 3.5) ) # add label to arrow tip ab_arrow_label = MathTex("ax + b", color=ORCAred).add_updater( lambda m: m.next_to(ab_arrow, UP) ) ab_arrow_label.update() # animate stretch and shift self.play(FadeIn(reminder_shift), FadeIn(reminder_stretch)) self.wait() self.add(ab_arrow) self.play( a.animate.set_value(1.5), a_bar.animate.set_value(1.5), b.animate.set_value(1), b_bar.animate.set_value(1), ev_arrow_label.animate.become( MathTex(r"\textsf{E}[aX + b]", color=ORCAblue) ), FadeIn(ab_arrow_label), ) self.wait() # show formula ev_formula_stretch_shift = MathTex(r"\textsf{E}[aX + b] = a\textsf{E}[X] + b") ev_formula_stretch_shift.set_color(ORCAblue).to_edge(UP) self.play(FadeIn(ev_formula_stretch_shift)) self.wait() # draw box ev_box_stretch_shift = SurroundingRectangle( ev_formula_stretch_shift, color=ORCAblue ) self.play(Create(ev_box_stretch_shift)) self.wait() # change ev_arrow_label self.play( ev_arrow_label.animate.become( MathTex(r"a\textsf{E}[X] + b", color=ORCAblue) ) ) self.wait() # fade out box self.play(FadeOut(ev_formula_stretch_shift, ev_box_stretch_shift)) self.wait() # reset self.play( a.animate.set_value(1), a_bar.animate.set_value(1), b.animate.set_value(0), b_bar.animate.set_value(0), ev_arrow_label.animate.become(MathTex(r"\textsf{E}[X]", color=ORCAblue)), FadeOut(ab_arrow_label), FadeOut(reminder_shift), FadeOut(reminder_stretch), ) self.remove(ab_arrow) self.wait() self.play(FadeOut(*self.mobjects)) self.wait() # ev calculation shift ev_calculation_shift = MathTex( r"\textsf{E}[X + b]", r"& = \sum_i (x_i + b) P(X + b = x_i + b)\\", r"& = \sum_i (x_i + b) P(X = x_i)\\", r"& = \sum_i x_i P(X = x_i) + \sum_i b P(X = x_i)\\", r"& = \textsf{E}[X] + \sum_i b P(X = x_i)\\", r"& = \textsf{E}[X] + b \sum_i P(X = x_i)\\", r"& = \textsf{E}[X] + b", color=ORCAblue, ) # ev calculation stretch ev_calculation_stretch = MathTex( r"\textsf{E}[aX]", r"& = \sum_i a x_i P(aX = ax_i)\\", r"& = \sum_i a x_i P(X = x_i)\\", r"& = a \sum_i x_i P(X = x_i)\\", r"& = a \textsf{E}[X]", color=ORCAblue, ) class Moments3a(Scene): # scene 3: expected value calculation rule shift def construct(self): self.play(FadeIn(ev_calculation_shift[:2])) self.wait() self.play( ReplacementTransform( ev_calculation_shift[1].copy(), ev_calculation_shift[2] ), run_time=2, ) self.wait() self.play( ReplacementTransform( ev_calculation_shift[2].copy(), ev_calculation_shift[3] ), run_time=2, ) self.wait() self.play( ReplacementTransform( ev_calculation_shift[3].copy(), ev_calculation_shift[4] ), run_time=2, ) self.wait() self.play( ReplacementTransform( ev_calculation_shift[4].copy(), ev_calculation_shift[5] ), run_time=2, ) self.wait() self.play( ReplacementTransform( ev_calculation_shift[5].copy(), ev_calculation_shift[6] ), run_time=2, ) self.wait() self.play(FadeOut(*self.mobjects)) self.wait() class Moments3b(Scene): # scene 3: expected value calculation rule stretch def construct(self): self.play(FadeIn(ev_calculation_stretch[:2])) self.wait() self.play( ReplacementTransform( ev_calculation_stretch[1].copy(), ev_calculation_stretch[2] ), run_time=2, ) self.wait() self.play( ReplacementTransform( ev_calculation_stretch[2].copy(), ev_calculation_stretch[3] ), run_time=2, ) self.wait() self.play( ReplacementTransform( ev_calculation_stretch[3].copy(), ev_calculation_stretch[4] ), run_time=2, ) self.wait() self.play(FadeOut(*self.mobjects)) self.wait() class Moments4(Scene): # scene 4: variance motivation def construct(self): bars0_copy = bars0.copy() self.play(FadeIn(axes_global, bars0, ev_indicator)) self.wait() self.play(ReplacementTransform(bars0, bars1)) self.wait() self.play(ReplacementTransform(bars1, bars2)) self.wait() self.play(ReplacementTransform(bars2, bars0_copy)) self.wait() class Moments5a(Scene): # scene 5 (non-physics version): variance formula part 1 def construct(self): # bend axes axes0 = axes_global.copy().apply_function( lambda p: p - (0, var0_adjusted * p[0] ** 2, 0) ) axes1 = axes_global.copy().apply_function( lambda p: p - (0, var1_adjusted * p[0] ** 2, 0) ) axes2 = axes_global.copy().apply_function( lambda p: p - (0, var2_adjusted * p[0] ** 2, 0) ) # bend bar charts bars0_bent = bars0.copy().apply_function( lambda p: p - (0, var0_adjusted * p[0] ** 2, 0) ) bars1_bent = bars1.copy().apply_function( lambda p: p - (0, var1_adjusted * p[0] ** 2, 0) ) bars2_bent = bars2.copy().apply_function( lambda p: p - (0, var2_adjusted * p[0] ** 2, 0) ) axes0_copy = axes0.copy() axes_global_copy = axes_global.copy() bars0_copy = bars0.copy() self.add(axes_global, bars0, ev_indicator) self.wait() self.play( bars0.animate.apply_function( lambda p: p - (0, var0_adjusted * p[0] ** 2, 0) ), ReplacementTransform(axes_global, axes0), ) self.wait() self.play( ReplacementTransform(bars0, bars1_bent), ReplacementTransform(axes0, axes1), ) self.wait() self.play( ReplacementTransform(bars1_bent, bars2_bent), ReplacementTransform(axes1, axes2), ) self.wait() self.play( ReplacementTransform(bars2_bent, bars0_bent), ReplacementTransform(axes2, axes0_copy), ) self.wait() self.play( ReplacementTransform(bars0_bent, bars0_copy), ReplacementTransform(axes0_copy, axes_global_copy), ) self.wait() class Moments5b(Scene): # scene 5 (non-physics version): variance formula part 2 def construct(self): graph = VGroup(axes_global, bars0, ev_indicator) sd_line = always_redraw( lambda: Line( start=ev_indicator.get_bottom(), end=ev_indicator.get_bottom() + 5 * DOWN, color=ORCAblue, ) ) self.add(graph) self.wait() self.play(Create(sd_line)) self.wait() self.play(graph.animate.shift(UP * 2)) self.wait() # horizontal lines hLines = VGroup() for val, i in zip(x_vals, range(len(x_vals))): hLines += line_with_ends( start=axes_global.get_x_axis().number_to_point(ev0), end=axes_global.get_x_axis().number_to_point(val), ).shift((0.5 * i + 1) * DOWN) # var calculation var_calculation1 = MathTex(r"{{x_i - \textsf{E}[X]}}") var_calculation2 = MathTex(r"{{|x_i - \textsf{E}[X]|}}") var_calculation3 = MathTex(r"{{|x_i - \textsf{E}[X]|}} \, {{p_i}}") var_calculation4 = MathTex( r"{{\sum_i}} {{|x_i - \textsf{E}[X]|}}{{^2}} \, {{p_i}}" ) var_calculation1.set_color(ORCAblue).to_edge(UP) var_calculation2.set_color(ORCAblue).to_edge(UP) var_calculation3.set_color(ORCAblue).to_edge(UP) var_calculation4.set_color(ORCAblue).to_edge(UP).shift(DOWN) # mean absolute deviation mad_definition = ( MathTex(r"\textbf{\textsf{mittlere absolute Abweichung}}") .set_color(ORCAblue) .to_edge(UP) ) mad_formula = ( MathTex(r"{{\sum_i}} {{|x_i - \textsf{E}[X]|}} \, {{p_i}}") .set_color(ORCAblue) .to_edge(UP) ) # variance var_definition = ( MathTex(r"\textbf{\textsf{Varianz}}").set_color(ORCAblue).to_edge(UP) ) var_formula = ( MathTex( r"{{\textsf{Var}(X)}} {{=}} {{\sum_i}} {{|x_i - \textsf{E}[X]|}}{{^2}} \, {{p_i}}", ) .set_color(ORCAblue) .to_edge(UP) .shift(DOWN) ) var_box = SurroundingRectangle( VGroup(var_definition, var_formula), color=ORCAblue ) # standard deviation sd_definition = ( MathTex(r"\textbf{\textsf{Standardabweichung}}") .set_color(ORCAblue) .to_edge(UP) ) sd_formula = ( MathTex(r"{{\sigma_X}} {{=}} \sqrt{ {{\textsf{Var}(X)}} }") .set_color(ORCAblue) .to_edge(UP) .shift(DOWN) ) sd_box = SurroundingRectangle(VGroup(sd_definition, sd_formula), color=ORCAblue) # start animation of calculation self.play(FadeIn(hLines, var_calculation1)) self.wait() # take absolute value self.play( *[ line.animate.shift(line[0].get_length() * RIGHT) for line in hLines if line[0].get_left()[0] < axes_global.get_x_axis().number_to_point(ev0)[0] ], TransformMatchingTex(var_calculation1, var_calculation2), ) self.wait() # staple each line to sd_line for line in hLines: line.add_updater( lambda m: m.align_to( axes_global.get_x_axis().number_to_point(ev0), LEFT, LEFT ) ) # multiply by probability self.play( *[line.animate.stretch(y, 0) for line, y in zip(hLines, y_vals[0])], TransformMatchingTex(var_calculation2, var_calculation3), ) self.wait() # unstaple each line and instead staple hLines to sd_line for line in hLines: line.clear_updaters() hLines.add_updater( lambda m: m.align_to([ev_indicator.get_bottom()[0], 0, 0], LEFT) ) # take sum self.play( hLines.animate.arrange(buff=0).shift(DOWN * 2), TransformMatchingTex(var_calculation3, mad_formula), ) self.wait() self.play( mad_formula.animate.shift(DOWN), graph.animate.shift(DOWN * 2), hLines.animate.shift(DOWN * 0.5), ) self.wait() # define mean absolute deviation self.play(FadeIn(mad_definition)) self.wait() self.play(FadeOut(mad_definition)) self.wait() # square absolute value self.play( *[line.animate.stretch(abs(ev0 - x), 0) for line, x in zip(hLines, x_vals)], TransformMatchingTex(mad_formula, var_calculation4), ) self.wait() # define variance and draw box self.play( TransformMatchingTex(var_calculation4, var_formula), FadeIn(var_definition) ) self.wait() self.play(Create(var_box)) self.wait() self.play(FadeOut(var_definition, var_box)) self.wait() # take square root self.play( hLines.animate.stretch(sqrt(var0) / var0, 0), TransformMatchingTex(var_formula, sd_formula), ) self.wait() # define standard deviation and draw box self.play(FadeIn(sd_definition)) self.wait() self.play(Create(sd_box)) self.wait() # arrow marker for standard deviation sd_arrow_plus = always_redraw( lambda: Arrow( start=ev_indicator.get_top(), end=ev_indicator.get_top() + abs(axes_global.c2p(sqrt(var0)) - axes_global.c2p(0)) * RIGHT, color=ORCAblue, buff=0, max_tip_length_to_length_ratio=0.2, ).shift(DOWN) ) sd_arrow_minus = always_redraw( lambda: Arrow( start=ev_indicator.get_top(), end=ev_indicator.get_top() + abs(axes_global.c2p(sqrt(var0)) - axes_global.c2p(0)) * LEFT, color=ORCAblue, buff=0, max_tip_length_to_length_ratio=0.2, ).shift(DOWN) ) # add label to arrow tip sd_label_plus = MathTex(r"+\sigma_X", color=ORCAblue).add_updater( lambda m: m.next_to(sd_arrow_plus.get_right(), DOWN) ) sd_label_plus.update() sd_label_minus = MathTex(r"-\sigma_X", color=ORCAblue).add_updater( lambda m: m.next_to(sd_arrow_minus.get_left(), DOWN).align_to(sd_label_plus, DOWN) ) sd_label_minus.update() # replace hLines with arrow markers self.play( GrowArrow(sd_arrow_minus), ReplacementTransform(hLines, sd_arrow_plus) ) self.play(Write(sd_label_plus), Write(sd_label_minus)) self.wait() self.play(FadeOut(sd_definition, sd_formula, sd_box)) self.wait() class Moments6(Scene): # scene 6 (non-physics version): variance example def construct(self): # ValueTracker for variance var = ValueTracker(var0) # arrow marker for standard deviation sd_line = always_redraw( lambda: Line( start=ev_indicator.get_bottom(), end=ev_indicator.get_bottom() + 2.5 * DOWN, color=ORCAblue, ) ) sd_arrow_plus = always_redraw( lambda: Arrow( start=ev_indicator.get_top(), end=ev_indicator.get_top() + abs(axes_global.c2p(sqrt(var.get_value())) - axes_global.c2p(0)) * RIGHT, color=ORCAblue, buff=0, max_tip_length_to_length_ratio=0.2, ).shift(DOWN) ) sd_arrow_minus = always_redraw( lambda: Arrow( start=ev_indicator.get_top(), end=ev_indicator.get_top() + abs(axes_global.c2p(sqrt(var.get_value())) - axes_global.c2p(0)) * LEFT, color=ORCAblue, buff=0, max_tip_length_to_length_ratio=0.2, ).shift(DOWN) ) # add label to arrow tip sd_label_plus = MathTex(r"+\sigma_X", color=ORCAblue).add_updater( lambda m: m.next_to(sd_arrow_plus.get_right(), DOWN) ) sd_label_plus.update() sd_label_minus = MathTex(r"-\sigma_X", color=ORCAblue).add_updater( lambda m: m.next_to(sd_arrow_minus.get_left(), DOWN).align_to(sd_label_plus, DOWN) ) sd_label_minus.update() # display variance value_var = Variable( var=var0, label=MathTex(r"\textsf{Var}(X)"), num_decimal_places=2, ) value_var.label.set_color(ORCAblue) value_var.value.set_color(ORCAblue) value_var.add_updater(lambda m: m.tracker.set_value(var.get_value())) # display standard deviation value_sd = Variable( var=sqrt(var0), label=MathTex(r"\sigma_X"), num_decimal_places=2, ) value_sd.label.set_color(ORCAblue) value_sd.value.set_color(ORCAblue) value_sd.add_updater(lambda m: m.tracker.set_value(sqrt(var.get_value()))) values = VGroup(value_var, value_sd).arrange(DOWN).to_edge(UP) bars3 = bars0.copy() # build up scene self.add( axes_global, bars0, ev_indicator, sd_line, sd_arrow_plus, sd_arrow_minus, sd_label_plus, sd_label_minus, ) self.wait() self.play(FadeIn(values)) self.wait() # transform into different distributions self.play( ReplacementTransform(bars0, bars1), var.animate.set_value(var1), ) self.wait() self.play( ReplacementTransform(bars1, bars2), var.animate.set_value(var2), ) self.wait() self.play( ReplacementTransform(bars2, bars3), var.animate.set_value(var0), ) self.wait() self.play(FadeOut(*self.mobjects)) self.wait() class Moments7(Scene): # scene 7: variance calculation rules def construct(self): # background grid plane = NumberPlane( x_range=[-20, 20, 1], y_range=[-3, 3, 1], background_line_style={"stroke_color": GRAY}, axis_config={"stroke_color": DARK_GRAY, "stroke_width": 3}, ).shift(DOWN + LEFT * 3.5) # stretch and shift parameter a = ValueTracker(1) b = ValueTracker(0) # axes for bar chart axes = always_redraw( lambda: Axes( x_range=[0, 7, 1], y_range=[0, 0.5, 1], x_length=7, y_length=3, tips=False, axis_config={"include_ticks": False, "color": ORCAblue}, x_axis_config={"stroke_width": 4}, y_axis_config={"stroke_width": 0}, ) .add_coordinates(range(10), color=ORCAblue) .shift(UP * 0.5) ) # bar chart of distribution bars = always_redraw( lambda: bar_chart( axes, x_values=np.array(x_vals) * a.get_value() + b.get_value(), y_values=y_vals[0], x_range=[0, 7 * a.get_value() + b.get_value()], bar_width=0.5 / a.get_value(), color=ORCAblue, ) ) # triangle marker for expected value ev_indicator = always_redraw( lambda: Triangle( fill_color=ORCAblue, fill_opacity=0.5, ) .set_stroke(ORCAblue, width=1) .scale(0.5) .move_to( axes.get_x_axis().number_to_point( expected_value(x_vals, y_vals[0], a.get_value(), b.get_value()) ) + 0.4 * DOWN ) ) # arrow marker for expected value ev_arrow = always_redraw( lambda: Arrow( start=LEFT * 3.5, end=LEFT * ( 3.5 - expected_value(x_vals, y_vals[0], a.get_value(), b.get_value()) ), color=ORCAblue, buff=0, ).shift(DOWN * 2) ) # add label to arrow tip ev_arrow_label = MathTex(r"\textsf{E}[X]", color=ORCAblue).add_updater( lambda m: m.next_to(ev_arrow, DOWN, buff=0) ) ev_arrow_label.update() # arrow marker for standard deviation sd_line = always_redraw( lambda: Line( start=ev_indicator.get_bottom(), end=ev_indicator.get_bottom() + 2.5 * DOWN, color=ORCAblue, ) ) sd_arrow_plus = always_redraw( lambda: Arrow( start=ev_indicator.get_top(), end=ev_indicator.get_top() + abs( axes_global.c2p( sqrt(variance(x_vals, y_vals[0], a.get_value(), b.get_value())) ) - axes_global.c2p(0) ) * RIGHT, color=ORCAblue, buff=0, max_tip_length_to_length_ratio=0.2, ).shift(DOWN * 2) ) sd_arrow_minus = always_redraw( lambda: Arrow( start=ev_indicator.get_top(), end=ev_indicator.get_top() + abs( axes_global.c2p( sqrt(variance(x_vals, y_vals[0], a.get_value(), b.get_value())) ) - axes_global.c2p(0) ) * LEFT, color=ORCAblue, buff=0, max_tip_length_to_length_ratio=0.2, ).shift(DOWN * 2) ) # add label to arrow tip sd_label_plus = MathTex(r"+\sigma_X", color=ORCAblue).add_updater( lambda m: m.next_to(sd_arrow_plus.get_right(), DOWN) ) sd_label_plus.update() sd_label_minus = MathTex(r"-\sigma_X", color=ORCAblue).add_updater( lambda m: m.next_to(sd_arrow_minus.get_left(), DOWN).align_to(sd_label_plus, DOWN) ) sd_label_minus.update() # build up scene self.play(FadeIn(plane, axes, bars, ev_indicator)) self.wait() self.play(Create(sd_line)) self.play(GrowArrow(ev_arrow)) self.play(Write(ev_arrow_label)) self.play(GrowArrow(sd_arrow_plus), GrowArrow(sd_arrow_minus)) self.play(Write(sd_label_plus), Write(sd_label_minus)) self.wait() # arrow marker for shift b_arrow = always_redraw( lambda: Arrow( start=RIGHT, end=RIGHT * (1 + b.get_value()), color=ORCAred, buff=0, ).shift(UP + LEFT * 3.5) ) # add label to arrow tip b_arrow_label = MathTex("x + b", color=ORCAred).add_updater( lambda m: m.next_to(b_arrow, UP) ) b_arrow_label.update() # animate shift self.play(FadeIn(reminder_shift)) self.wait() self.add(b_arrow) self.play( b.animate.set_value(1), ev_arrow_label.animate.become( MathTex(r"\textsf{E}[X + b]", color=ORCAblue) ), sd_label_plus.animate.become(MathTex(r"+\sigma_{X + b}", color=ORCAblue)), sd_label_minus.animate.become(MathTex(r"-\sigma_{X + b}", color=ORCAblue)), FadeIn(b_arrow_label), ) self.wait() # change ev_arrow_label self.play( ev_arrow_label.animate.become(MathTex(r"\textsf{E}[X] + b", color=ORCAblue)) ) self.wait() # CUT TO VAR CALCULATION SHIFT WHEN EDITING (Moments7a) # show formula for variance var_formula_shift = MathTex(r"\textsf{Var}(X + b) = \textsf{Var}(X)") var_formula_shift.set_color(ORCAblue).to_edge(UP) self.play(FadeIn(var_formula_shift)) self.wait() # draw box var_box_shift = SurroundingRectangle(var_formula_shift, color=ORCAblue) self.play(Create(var_box_shift)) self.wait() self.play(FadeOut(var_formula_shift, var_box_shift)) self.wait() # show formula for standard deviation sd_formula_shift = MathTex(r"\sigma_{X + b} = \sigma_X") sd_formula_shift.set_color(ORCAblue).to_edge(UP) self.play(FadeIn(sd_formula_shift)) self.wait() # draw box sd_box_shift = SurroundingRectangle(sd_formula_shift, color=ORCAblue) self.play(Create(sd_box_shift)) self.wait() # change sd_label_plus and sd_label_minus self.play( sd_label_plus.animate.become(MathTex(r"+\sigma_X", color=ORCAblue)), sd_label_minus.animate.become(MathTex(r"-\sigma_X", color=ORCAblue)), ) self.wait() # fade out box self.play(FadeOut(sd_formula_shift, sd_box_shift)) self.wait() # reset self.play( b.animate.set_value(0), ev_arrow_label.animate.become(MathTex(r"\textsf{E}[X]", color=ORCAblue)), sd_label_plus.animate.become(MathTex(r"+\sigma_X", color=ORCAblue)), sd_label_minus.animate.become(MathTex(r"-\sigma_X", color=ORCAblue)), FadeOut(b_arrow_label), FadeOut(reminder_shift), ) self.remove(b_arrow) self.wait() # arrow marker for stretch a_arrow = always_redraw( lambda: Arrow( start=RIGHT, end=RIGHT * (a.get_value() + 0), color=ORCAred, buff=0, ).shift(UP + LEFT * 3.5) ) # add label to arrow tip a_arrow_label = MathTex("ax", color=ORCAred).add_updater( lambda m: m.next_to(a_arrow, UP) ) a_arrow_label.update() # animate stretch self.play(FadeIn(reminder_stretch)) self.wait() self.add(a_arrow) self.play( a.animate.set_value(1.5), ev_arrow_label.animate.become(MathTex(r"\textsf{E}[aX]", color=ORCAblue)), sd_label_plus.animate.become(MathTex(r"+\sigma_{aX}", color=ORCAblue)), sd_label_minus.animate.become(MathTex(r"-\sigma_{aX}", color=ORCAblue)), FadeIn(a_arrow_label), ) self.wait() # change ev_arrow_label self.play( ev_arrow_label.animate.become(MathTex(r"a\textsf{E}[X]", color=ORCAblue)) ) self.wait() # CUT TO VAR CALCULATION STRETCH WHEN EDITING (Moments7b) # show formula for variance var_formula_stretch = MathTex(r"\textsf{Var}(aX) = a^2 \textsf{Var}(X)") var_formula_stretch.set_color(ORCAblue).to_edge(UP) self.play(FadeIn(var_formula_stretch)) self.wait() # draw box var_box_stretch = SurroundingRectangle(var_formula_stretch, color=ORCAblue) self.play(Create(var_box_stretch)) self.wait() self.play(FadeOut(var_formula_stretch, var_box_stretch)) self.wait() # show formula for standard deviation sd_formula_stretch = MathTex(r"\sigma_{aX} = |a| \sigma_X") sd_formula_stretch.set_color(ORCAblue).to_edge(UP) self.play(FadeIn(sd_formula_stretch)) self.wait() # draw box sd_box_stretch = SurroundingRectangle(sd_formula_stretch, color=ORCAblue) self.play(Create(sd_box_stretch)) self.wait() # change sd_label_plus and sd_label_minus self.play( sd_label_plus.animate.become(MathTex(r"+|a|\sigma_X", color=ORCAblue)), sd_label_minus.animate.become(MathTex(r"-|a|\sigma_X", color=ORCAblue)), ) self.wait() # fade out box self.play(FadeOut(sd_formula_stretch, sd_box_stretch)) self.wait() # reset self.play( a.animate.set_value(1), ev_arrow_label.animate.become(MathTex(r"\textsf{E}[X]", color=ORCAblue)), sd_label_plus.animate.become(MathTex(r"+\sigma_X", color=ORCAblue)), sd_label_minus.animate.become(MathTex(r"-\sigma_X", color=ORCAblue)), FadeOut(a_arrow_label), FadeOut(reminder_stretch), ) self.remove(a_arrow) self.wait() # arrow marker for stretch and shift ab_arrow = always_redraw( lambda: Arrow( start=RIGHT, end=RIGHT * (a.get_value() + b.get_value()), color=ORCAred, buff=0, ).shift(UP + LEFT * 3.5) ) # add label to arrow tip ab_arrow_label = MathTex("ax + b", color=ORCAred).add_updater( lambda m: m.next_to(ab_arrow, UP) ) ab_arrow_label.update() # animate stretch and shift self.play(FadeIn(reminder_shift), FadeIn(reminder_stretch)) self.wait() self.add(ab_arrow) self.play( a.animate.set_value(1.5), b.animate.set_value(1), ev_arrow_label.animate.become( MathTex(r"\textsf{E}[aX + b]", color=ORCAblue) ), sd_label_plus.animate.become(MathTex(r"+\sigma_{aX + b}", color=ORCAblue)), sd_label_minus.animate.become(MathTex(r"-\sigma_{aX + b}", color=ORCAblue)), FadeIn(ab_arrow_label), ) self.wait() # change ev_arrow_label self.play( ev_arrow_label.animate.become( MathTex(r"a\textsf{E}[X] + b", color=ORCAblue) ) ) self.wait() # show formula for variance var_formula_stretch_shift = MathTex( r"\textsf{Var}(aX + b) = a^2 \textsf{Var}(X)" ) var_formula_stretch_shift.set_color(ORCAblue).to_edge(UP) self.play(FadeIn(var_formula_stretch_shift)) self.wait() # draw box var_box_stretch_shift = SurroundingRectangle( var_formula_stretch_shift, color=ORCAblue ) self.play(Create(var_box_stretch_shift)) self.wait() self.play(FadeOut(var_formula_stretch_shift, var_box_stretch_shift)) self.wait() # NOT IN SPOKEN TEXT """ # show formula for standard deviation sd_formula_stretch_shift = MathTex(r"\sigma_{aX + b} = |a| \sigma_X") sd_formula_stretch_shift.set_color(ORCAblue).to_edge(UP) self.play(FadeIn(sd_formula_stretch_shift)) self.wait() # draw box sd_box_stretch_shift = SurroundingRectangle( sd_formula_stretch_shift, color=ORCAblue ) self.play(Create(sd_box_stretch_shift)) self.wait() """ # change sd_label_plus and sd_label_minus self.play( sd_label_plus.animate.become(MathTex(r"+|a|\sigma_X", color=ORCAblue)), sd_label_minus.animate.become(MathTex(r"-|a|\sigma_X", color=ORCAblue)), ) self.wait() """ # fade out box self.play(FadeOut(sd_formula_stretch_shift, sd_box_stretch_shift)) self.wait() """ # reset self.play( a.animate.set_value(1), b.animate.set_value(0), ev_arrow_label.animate.become(MathTex(r"\textsf{E}[X]", color=ORCAblue)), sd_label_plus.animate.become(MathTex(r"+\sigma_X", color=ORCAblue)), sd_label_minus.animate.become(MathTex(r"-\sigma_X", color=ORCAblue)), FadeOut(ab_arrow_label), FadeOut(reminder_shift), FadeOut(reminder_stretch), ) self.remove(ab_arrow) self.wait() self.play(FadeOut(*self.mobjects)) self.wait() # var calculation shift var_calculation_shift = MathTex( r"\textsf{Var}(X + b)", r"& = \sum_i |x_i + b - \textsf{E}[X + b]|^2 P(X + b = x_i + b)\\", r"& = \sum_i |x_i + b - \textsf{E}[X + b]|^2 P(X = x_i)\\", r"& = \sum_i |x_i + b - \textsf{E}[X] - b|^2 P(X = x_i)\\", r"& = \sum_i |x_i - \textsf{E}[X]|^2 P(X = x_i)\\", r"& = \textsf{Var}(X)", color=ORCAblue, ) # var calculation stretch var_calculation_stretch = MathTex( r"\textsf{Var}(aX)", r"& = \sum_i |ax_i - \textsf{E}[aX]|^2 P(aX = ax_i)\\", r"& = \sum_i |ax_i - \textsf{E}[aX]|^2 P(X = x_i)\\", r"& = \sum_i |ax_i - a\textsf{E}[X]|^2 P(X = x_i)\\", r"& = \sum_i a^2 |x_i - \textsf{E}[X]|^2 P(X = x_i)\\", r"& = a^2 \sum_i |x_i - \textsf{E}[X]|^2 P(X = x_i)\\", r"& = a^2 \textsf{Var}(X)", color=ORCAblue, ) class Moments7a(Scene): # scene 7: variance calculation rule shift def construct(self): self.play(FadeIn(var_calculation_shift[:2])) self.wait() self.play( ReplacementTransform( var_calculation_shift[1].copy(), var_calculation_shift[2] ), run_time=2, ) self.wait() self.play( ReplacementTransform( var_calculation_shift[2].copy(), var_calculation_shift[3] ), run_time=2, ) self.wait() self.play( ReplacementTransform( var_calculation_shift[3].copy(), var_calculation_shift[4] ), run_time=2, ) self.wait() self.play( ReplacementTransform( var_calculation_shift[4].copy(), var_calculation_shift[5] ), run_time=2, ) self.wait() self.play(FadeOut(*self.mobjects)) self.wait() class Moments7b(Scene): # scene 7: variance calculation rule stretch def construct(self): self.play(FadeIn(var_calculation_stretch[:2])) self.wait() self.play( ReplacementTransform( var_calculation_stretch[1].copy(), var_calculation_stretch[2] ), run_time=2, ) self.wait() self.play( ReplacementTransform( var_calculation_stretch[2].copy(), var_calculation_stretch[3] ), run_time=2, ) self.wait() self.play( ReplacementTransform( var_calculation_stretch[3].copy(), var_calculation_stretch[4] ), run_time=2, ) self.wait() self.play( ReplacementTransform( var_calculation_stretch[4].copy(), var_calculation_stretch[5] ), run_time=2, ) self.wait() self.play( ReplacementTransform( var_calculation_stretch[5].copy(), var_calculation_stretch[6] ), run_time=2, ) self.wait() self.play(FadeOut(*self.mobjects)) self.wait()