mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
3597 lines
109 KiB
Python
3597 lines
109 KiB
Python
from constants import *
|
|
|
|
from mobject.tex_mobject import TexMobject
|
|
from mobject.mobject import Mobject
|
|
from mobject.image_mobject import ImageMobject
|
|
from mobject.vectorized_mobject import *
|
|
|
|
from animation.animation import Animation
|
|
from animation.transform import *
|
|
from animation.simple_animations import *
|
|
from animation.composition import *
|
|
from animation.playground import *
|
|
from topics.geometry import *
|
|
from topics.characters import *
|
|
from topics.functions import *
|
|
from topics.fractals import *
|
|
from topics.number_line import *
|
|
from topics.combinatorics import *
|
|
from topics.numerals import *
|
|
from topics.three_dimensions import *
|
|
from topics.objects import *
|
|
from topics.complex_numbers import *
|
|
from topics.common_scenes import *
|
|
from topics.probability import *
|
|
from scene.scene import Scene
|
|
from scene.reconfigurable_scene import ReconfigurableScene
|
|
from scene.zoomed_scene import *
|
|
from camera.camera import Camera
|
|
from mobject.svg_mobject import *
|
|
from mobject.tex_mobject import *
|
|
|
|
from scene.scene import ProgressDisplay
|
|
import scipy
|
|
|
|
#revert_to_original_skipping_status
|
|
|
|
def get_binomial_distribution(n, p):
|
|
return lambda k : choose(n, k)*(p**(k))*((1-p)**(n-k))
|
|
|
|
def get_quiz(*questions):
|
|
q_mobs = VGroup(*map(TextMobject, [
|
|
"%d. %s"%(i+1, question)
|
|
for i, question in enumerate(questions)
|
|
]))
|
|
q_mobs.arrange_submobjects(
|
|
DOWN,
|
|
buff = MED_LARGE_BUFF,
|
|
aligned_edge = LEFT,
|
|
)
|
|
content = VGroup(
|
|
TextMobject("Quiz").scale(1.5),
|
|
Line(q_mobs.get_left(), q_mobs.get_right()),
|
|
q_mobs
|
|
)
|
|
content.arrange_submobjects(DOWN, buff = MED_SMALL_BUFF)
|
|
rect = SurroundingRectangle(content, buff = MED_LARGE_BUFF)
|
|
rect.shift(MED_SMALL_BUFF*DOWN)
|
|
rect.set_color(WHITE)
|
|
quiz = VGroup(rect, content)
|
|
quiz.questions = q_mobs
|
|
quiz.scale(0.7)
|
|
return quiz
|
|
|
|
def get_slot_group(
|
|
bool_list,
|
|
buff = MED_LARGE_BUFF,
|
|
include_qs = True,
|
|
min_bool_list_len = 3,
|
|
):
|
|
if len(bool_list) < min_bool_list_len:
|
|
bool_list += [None]*(min_bool_list_len - len(bool_list))
|
|
n = len(bool_list)
|
|
|
|
lines = VGroup(*[
|
|
Line(ORIGIN, MED_LARGE_BUFF*RIGHT)
|
|
for x in range(n)
|
|
])
|
|
lines.arrange_submobjects(RIGHT, buff = buff)
|
|
if include_qs:
|
|
labels = VGroup(*[
|
|
TextMobject("Q%d"%d) for d in range(1, n+1)
|
|
])
|
|
else:
|
|
labels = VGroup(*[VectorizedPoint() for d in range(n)])
|
|
for label, line in zip(labels, lines):
|
|
label.scale(0.7)
|
|
label.next_to(line, DOWN, SMALL_BUFF)
|
|
slot_group = VGroup()
|
|
slot_group.lines = lines
|
|
slot_group.labels = labels
|
|
slot_group.content = VGroup()
|
|
slot_group.digest_mobject_attrs()
|
|
slot_group.to_edge(RIGHT)
|
|
slot_group.bool_list = bool_list
|
|
|
|
total_height = FRAME_Y_RADIUS
|
|
base = 2.3
|
|
|
|
for i, line in enumerate(lines):
|
|
if i >= len(bool_list) or bool_list[i] is None:
|
|
mob = VectorizedPoint()
|
|
elif bool_list[i]:
|
|
mob = TexMobject("\\checkmark")
|
|
mob.set_color(GREEN)
|
|
slot_group.shift(total_height*DOWN / (base**(i+1)))
|
|
else:
|
|
mob = TexMobject("\\times")
|
|
mob.set_color(RED)
|
|
slot_group.shift(total_height*UP / (base**(i+1)))
|
|
mob.next_to(line, UP, SMALL_BUFF)
|
|
slot_group.content.add(mob)
|
|
return slot_group
|
|
|
|
def get_probability_of_slot_group(bool_list, conditioned_list = None):
|
|
filler_tex = "Fi"*max(len(bool_list), 3)
|
|
if conditioned_list is None:
|
|
result = TexMobject("P(", filler_tex, ")")
|
|
else:
|
|
result = TexMobject("P(", filler_tex, "|", filler_tex, ")")
|
|
fillers = result.get_parts_by_tex(filler_tex)
|
|
for filler, bl in zip(fillers, [bool_list, conditioned_list]):
|
|
slot_group = get_slot_group(
|
|
bl, buff = SMALL_BUFF, include_qs = False,
|
|
)
|
|
slot_group.replace(filler, dim_to_match = 0)
|
|
slot_group.shift(0.5*SMALL_BUFF*DOWN)
|
|
index = result.index_of_part(filler)
|
|
result.submobjects[index] = slot_group
|
|
return result
|
|
|
|
|
|
#########
|
|
|
|
class IndependenceOpeningQuote(OpeningQuote):
|
|
CONFIG = {
|
|
"quote" : [
|
|
"Far better an ", "approximate",
|
|
" answer to the ", " right question",
|
|
", which is often vague, than an ", "exact",
|
|
" answer to the ", "wrong question", "."
|
|
],
|
|
"highlighted_quote_terms" : {
|
|
"approximate" : GREEN,
|
|
"right" : GREEN,
|
|
"exact" : RED,
|
|
"wrong" : RED,
|
|
},
|
|
"author" : "John Tukey",
|
|
"quote_arg_separator" : "",
|
|
}
|
|
|
|
class DangerInProbability(Scene):
|
|
def construct(self):
|
|
warning = self.get_warning_sign()
|
|
probability = TextMobject("Probability")
|
|
probability.scale(2)
|
|
|
|
self.play(Write(warning, run_time = 1))
|
|
self.play(
|
|
warning.next_to, probability, UP, LARGE_BUFF,
|
|
LaggedStart(FadeIn, probability)
|
|
)
|
|
self.wait()
|
|
|
|
|
|
#####
|
|
|
|
def get_warning_sign(self):
|
|
triangle = RegularPolygon(n = 3, start_angle = np.pi/2)
|
|
triangle.set_stroke(RED, 12)
|
|
triangle.scale_to_fit_height(2)
|
|
bang = TextMobject("!")
|
|
bang.scale_to_fit_height(0.6*triangle.get_height())
|
|
bang.move_to(interpolate(
|
|
triangle.get_bottom(),
|
|
triangle.get_top(),
|
|
0.4,
|
|
))
|
|
triangle.add(bang)
|
|
return triangle
|
|
|
|
class MeaningOfIndependence(SampleSpaceScene):
|
|
CONFIG = {
|
|
"sample_space_config" : {
|
|
"height" : 4,
|
|
"width" : 4,
|
|
}
|
|
}
|
|
def construct(self):
|
|
self.add_labeled_space()
|
|
self.align_conditionals()
|
|
self.relabel()
|
|
self.assume_independence()
|
|
self.no_independence()
|
|
|
|
def add_labeled_space(self):
|
|
self.add_sample_space(**self.sample_space_config)
|
|
self.sample_space.shift(2*LEFT)
|
|
self.sample_space.divide_horizontally(0.3)
|
|
self.sample_space[0].divide_vertically(
|
|
0.9, colors = [BLUE_D, GREEN_C]
|
|
)
|
|
self.sample_space[1].divide_vertically(
|
|
0.5, colors = [BLUE_E, GREEN_E]
|
|
)
|
|
side_braces_and_labels = self.sample_space.get_side_braces_and_labels(
|
|
["P(A)", "P(\\overline A)"]
|
|
)
|
|
top_braces_and_labels, bottom_braces_and_labels = [
|
|
part.get_subdivision_braces_and_labels(
|
|
part.vertical_parts,
|
|
labels = ["P(B | %s)"%s, "P(\\overline B | %s)"%s],
|
|
direction = vect
|
|
)
|
|
for part, s, vect in zip(
|
|
self.sample_space.horizontal_parts,
|
|
["A", "\\overline A"],
|
|
[UP, DOWN],
|
|
)
|
|
]
|
|
braces_and_labels_groups = VGroup(
|
|
side_braces_and_labels,
|
|
top_braces_and_labels,
|
|
bottom_braces_and_labels,
|
|
)
|
|
|
|
self.add(self.sample_space)
|
|
self.play(Write(braces_and_labels_groups, run_time = 4))
|
|
|
|
def align_conditionals(self):
|
|
line = Line(*[
|
|
interpolate(
|
|
self.sample_space.get_corner(vect+LEFT),
|
|
self.sample_space.get_corner(vect+RIGHT),
|
|
0.7
|
|
)
|
|
for vect in UP, DOWN
|
|
])
|
|
line.set_stroke(RED, 8)
|
|
word = TextMobject("Independence")
|
|
word.scale(1.5)
|
|
word.next_to(self.sample_space, RIGHT, buff = LARGE_BUFF)
|
|
word.set_color(RED)
|
|
|
|
self.play(*it.chain(
|
|
self.get_top_conditional_change_anims(0.7),
|
|
self.get_bottom_conditional_change_anims(0.7)
|
|
))
|
|
self.play(
|
|
ShowCreation(line),
|
|
Write(word, run_time = 1)
|
|
)
|
|
self.wait()
|
|
|
|
self.independence_word = word
|
|
self.independence_line = line
|
|
|
|
def relabel(self):
|
|
old_labels = self.sample_space[0].vertical_parts.labels
|
|
ignored_braces, new_top_labels = self.sample_space[0].get_top_braces_and_labels(
|
|
["P(B)", "P(\\overline B)"]
|
|
)
|
|
equation = TexMobject(
|
|
"P(B | A) = P(B)"
|
|
)
|
|
equation.scale(1.5)
|
|
equation.move_to(self.independence_word)
|
|
|
|
self.play(
|
|
Transform(old_labels, new_top_labels),
|
|
FadeOut(self.sample_space[1].vertical_parts.labels),
|
|
FadeOut(self.sample_space[1].vertical_parts.braces),
|
|
)
|
|
self.play(
|
|
self.independence_word.next_to, equation, UP, MED_LARGE_BUFF,
|
|
Write(equation)
|
|
)
|
|
self.wait()
|
|
|
|
self.equation = equation
|
|
|
|
def assume_independence(self):
|
|
everything = VGroup(*self.get_top_level_mobjects())
|
|
morty = Mortimer()
|
|
morty.scale(0.7)
|
|
morty.to_corner(DOWN+RIGHT)
|
|
bubble = ThoughtBubble(direction = RIGHT)
|
|
bubble.pin_to(morty)
|
|
bubble.set_fill(opacity = 0)
|
|
|
|
self.play(
|
|
FadeIn(morty),
|
|
everything.scale, 0.5,
|
|
everything.move_to, bubble.get_bubble_center(),
|
|
)
|
|
self.play(
|
|
morty.change, "hooray", everything,
|
|
ShowCreation(bubble)
|
|
)
|
|
self.wait()
|
|
self.play(Blink(morty))
|
|
self.wait()
|
|
|
|
self.morty = morty
|
|
|
|
def no_independence(self):
|
|
for part in self.sample_space.horizontal_parts:
|
|
part.vertical_parts.labels = None
|
|
self.play(*it.chain(
|
|
self.get_top_conditional_change_anims(0.9),
|
|
self.get_bottom_conditional_change_anims(0.5),
|
|
[
|
|
self.independence_word.fade, 0.7,
|
|
self.equation.fade, 0.7,
|
|
self.morty.change, "confused", self.sample_space,
|
|
FadeOut(self.independence_line)
|
|
]
|
|
))
|
|
self.wait()
|
|
|
|
class IntroduceBinomial(Scene):
|
|
CONFIG = {
|
|
"n" : 8,
|
|
"p" : 0.7,
|
|
}
|
|
def construct(self):
|
|
self.add_title()
|
|
self.add_bar_chart()
|
|
self.add_p_slider()
|
|
self.write_independence_assumption()
|
|
self.play_with_p_value(0.2, 0.5)
|
|
self.cross_out_assumption()
|
|
self.play_with_p_value(0.8, 0.4)
|
|
self.shift_weight_to_tails()
|
|
|
|
|
|
def add_title(self):
|
|
title = TextMobject("Binomial distribution")
|
|
title.scale(1.3)
|
|
title.to_edge(RIGHT)
|
|
title.shift(2*UP)
|
|
|
|
formula = TexMobject(
|
|
"P(X=", "k", ")=",
|
|
"{n \\choose k}",
|
|
"p", "^k",
|
|
"(1-", "p", ")", "^{n-", "k}",
|
|
arg_separator = ""
|
|
)
|
|
formula.set_color_by_tex("k", BLUE)
|
|
formula.set_color_by_tex("p", YELLOW)
|
|
choose_part = formula.get_part_by_tex("choose")
|
|
choose_part.set_color(WHITE)
|
|
choose_part[-2].set_color(BLUE)
|
|
formula.next_to(title, DOWN, MED_LARGE_BUFF)
|
|
|
|
self.formula = formula
|
|
self.title = title
|
|
self.add(title, formula)
|
|
|
|
def add_bar_chart(self):
|
|
n, p = self.n, self.p
|
|
dist = get_binomial_distribution(n, p)
|
|
chart = BarChart(
|
|
[dist(k) for k in range(n+1)],
|
|
bar_names = range(n+1),
|
|
)
|
|
chart.to_edge(LEFT)
|
|
self.bar_chart = chart
|
|
|
|
self.play(LaggedStart(
|
|
FadeIn, VGroup(*it.chain(*chart)),
|
|
run_time = 2
|
|
))
|
|
|
|
def add_p_slider(self):
|
|
interval = UnitInterval(color = LIGHT_GREY)
|
|
interval.scale_to_fit_width(4)
|
|
interval.next_to(
|
|
VGroup(self.bar_chart.x_axis, self.bar_chart.y_axis),
|
|
UP, MED_LARGE_BUFF
|
|
)
|
|
interval.add_numbers(0, 1)
|
|
triangle = RegularPolygon(
|
|
n=3, start_angle = -np.pi/2,
|
|
stroke_width = 0,
|
|
fill_color = YELLOW,
|
|
fill_opacity = 1,
|
|
)
|
|
triangle.scale_to_fit_height(0.25)
|
|
triangle.move_to(interval.number_to_point(self.p), DOWN)
|
|
label = TexMobject("p")
|
|
label.next_to(triangle, UP, SMALL_BUFF)
|
|
label.set_color(triangle.get_color())
|
|
|
|
self.p_slider = VGroup(interval, triangle, label)
|
|
self.play(Write(self.p_slider, run_time = 1))
|
|
|
|
def play_with_p_value(self, *values):
|
|
for value in values:
|
|
self.change_p(value)
|
|
self.wait()
|
|
|
|
def write_independence_assumption(self):
|
|
assumption = TextMobject("Independence assumption")
|
|
assumption.scale(1.2)
|
|
assumption.next_to(self.formula, DOWN, MED_LARGE_BUFF, LEFT)
|
|
assumption.set_color(GREEN_C)
|
|
|
|
self.play(Write(assumption, run_time = 2))
|
|
self.wait()
|
|
|
|
self.assumption = assumption
|
|
|
|
def cross_out_assumption(self):
|
|
cross = Cross(self.assumption)
|
|
cross.set_color(GREY)
|
|
self.bar_chart.save_state()
|
|
|
|
self.play(ShowCreation(cross))
|
|
self.play(self.bar_chart.fade, 0.7)
|
|
self.wait(2)
|
|
self.play(self.bar_chart.restore)
|
|
|
|
def shift_weight_to_tails(self):
|
|
chart = self.bar_chart
|
|
chart_copy = chart.copy()
|
|
dist = get_binomial_distribution(self.n, self.p)
|
|
values = np.array(map(dist, range(self.n+1)))
|
|
values += 0.1
|
|
values /= sum(values)
|
|
|
|
old_bars = chart.bars
|
|
old_bars.generate_target()
|
|
new_bars = chart_copy.bars
|
|
for bars, vect in (old_bars.target, LEFT), (new_bars, RIGHT):
|
|
for bar in bars:
|
|
corner = bar.get_corner(DOWN+vect)
|
|
bar.stretch(0.5, 0)
|
|
bar.move_to(corner, DOWN+vect)
|
|
old_bars.target.set_color(RED)
|
|
old_bars.target.fade()
|
|
|
|
self.play(
|
|
MoveToTarget(old_bars),
|
|
ReplacementTransform(
|
|
old_bars.copy().set_fill(opacity = 0),
|
|
new_bars
|
|
)
|
|
)
|
|
self.play(
|
|
chart_copy.change_bar_values, values
|
|
)
|
|
self.wait(2)
|
|
|
|
|
|
|
|
#####
|
|
|
|
def change_p(self, p):
|
|
interval, triangle, p_label = self.p_slider
|
|
alt_dist = get_binomial_distribution(self.n, p)
|
|
self.play(
|
|
ApplyMethod(
|
|
self.bar_chart.change_bar_values,
|
|
[alt_dist(k) for k in range(self.n+1)],
|
|
),
|
|
triangle.move_to, interval.number_to_point(p), DOWN,
|
|
MaintainPositionRelativeTo(p_label, triangle)
|
|
)
|
|
self.p = p
|
|
|
|
class IntroduceQuiz(PiCreatureScene):
|
|
def construct(self):
|
|
self.add_quiz()
|
|
self.ask_about_probabilities()
|
|
self.show_distribution()
|
|
self.show_single_question_probability()
|
|
|
|
def add_quiz(self):
|
|
quiz = self.get_example_quiz()
|
|
quiz.next_to(self.randy, UP+RIGHT)
|
|
|
|
self.play(
|
|
Write(quiz),
|
|
self.randy.change, "pondering", quiz
|
|
)
|
|
self.wait()
|
|
|
|
self.quiz = quiz
|
|
|
|
def ask_about_probabilities(self):
|
|
probabilities, abbreviated_probabilities = [
|
|
VGroup(*[
|
|
TexMobject(
|
|
"P(", s_tex, "=", str(score), ")", rhs
|
|
).set_color_by_tex_to_color_map({
|
|
str(score) : YELLOW,
|
|
"text" : GREEN,
|
|
})
|
|
for score in range(4)
|
|
])
|
|
for s_tex, rhs in [
|
|
("\\text{Score}", "= \\, ???"),
|
|
("\\text{S}", "")
|
|
]
|
|
]
|
|
for group in probabilities, abbreviated_probabilities:
|
|
group.arrange_submobjects(
|
|
DOWN,
|
|
buff = MED_LARGE_BUFF,
|
|
aligned_edge = LEFT
|
|
)
|
|
group.to_corner(UP+LEFT)
|
|
|
|
self.play(
|
|
LaggedStart(FadeIn, probabilities, run_time = 3),
|
|
self.quiz.scale_to_fit_height, 0.7*self.randy.get_height(),
|
|
self.quiz.next_to, self.randy, RIGHT,
|
|
self.randy.change, "confused", probabilities
|
|
)
|
|
self.wait()
|
|
|
|
self.probabilities = probabilities
|
|
self.abbreviated_probabilities = abbreviated_probabilities
|
|
|
|
def show_distribution(self):
|
|
dist = get_binomial_distribution(3, 0.7)
|
|
values = map(dist, range(4))
|
|
chart = BarChart(
|
|
values,
|
|
width = 7,
|
|
bar_names = range(4)
|
|
)
|
|
chart.to_edge(RIGHT)
|
|
for short_p, bar in zip(self.abbreviated_probabilities, chart.bars):
|
|
short_p.scale_to_fit_width(1.75*bar.get_width())
|
|
short_p.next_to(bar, UP)
|
|
|
|
self.play(
|
|
LaggedStart(Write, VGroup(
|
|
*filter(lambda m : m is not chart.bars, chart)
|
|
)),
|
|
)
|
|
self.play(*[
|
|
ReplacementTransform(
|
|
bar.copy().stretch_to_fit_height(0).move_to(bar.get_bottom()),
|
|
bar
|
|
)
|
|
for bar in chart.bars
|
|
])
|
|
self.play(*[
|
|
ReplacementTransform(p.copy(), short_p)
|
|
for p, short_p in zip(
|
|
self.probabilities,
|
|
self.abbreviated_probabilities,
|
|
)
|
|
])
|
|
self.wait()
|
|
|
|
self.bar_chart = chart
|
|
|
|
def show_single_question_probability(self):
|
|
prob = TexMobject(
|
|
"P(", "\\text{Can answer a given question}", ")",
|
|
"= 0.8"
|
|
)
|
|
prob.to_corner(UP+RIGHT)
|
|
prob.set_color_by_tex("text", GREEN)
|
|
rect = SurroundingRectangle(prob, buff = MED_SMALL_BUFF)
|
|
|
|
self.play(
|
|
Write(prob),
|
|
self.randy.change, "happy", prob
|
|
)
|
|
self.play(ShowCreation(rect))
|
|
self.wait()
|
|
|
|
self.single_question_probability = VGroup(
|
|
prob, rect
|
|
)
|
|
|
|
|
|
######
|
|
|
|
def create_pi_creature(self):
|
|
randy = Randolph()
|
|
randy.scale(0.7)
|
|
randy.to_corner(DOWN+LEFT)
|
|
self.randy = randy
|
|
return randy
|
|
|
|
def get_example_quiz(self):
|
|
return get_quiz(
|
|
"Define ``Brachistochrone'' ",
|
|
"Define ``Tautochrone'' ",
|
|
"Define ``Cycloid'' ",
|
|
)
|
|
|
|
class BreakDownQuestionPatterns(IntroduceQuiz):
|
|
def construct(self):
|
|
self.add_parts_from_last_scene()
|
|
self.break_down_possibilities()
|
|
self.count_patterns()
|
|
|
|
def add_parts_from_last_scene(self):
|
|
self.force_skipping()
|
|
IntroduceQuiz.construct(self)
|
|
self.revert_to_original_skipping_status()
|
|
|
|
chart_group = VGroup(
|
|
self.bar_chart,
|
|
self.abbreviated_probabilities
|
|
)
|
|
self.play(
|
|
self.single_question_probability.scale, 0.8,
|
|
self.single_question_probability.to_corner, UP+LEFT,
|
|
chart_group.scale, 0.7, chart_group.get_top(),
|
|
chart_group.to_edge, LEFT,
|
|
FadeOut(self.probabilities)
|
|
)
|
|
|
|
def break_down_possibilities(self):
|
|
slot_group_groups = VGroup(*[VGroup() for x in range(4)])
|
|
bool_lists = [[]]
|
|
while bool_lists:
|
|
bool_list = bool_lists.pop()
|
|
slot_group = self.get_slot_group(bool_list)
|
|
slot_group_groups[len(bool_list)].add(slot_group)
|
|
if len(bool_list) < 3:
|
|
bool_lists += [
|
|
list(bool_list) + [True],
|
|
list(bool_list) + [False],
|
|
]
|
|
|
|
group_group = slot_group_groups[0]
|
|
self.revert_to_original_skipping_status()
|
|
self.play(Write(group_group, run_time = 1))
|
|
self.wait()
|
|
for new_group_group in slot_group_groups[1:]:
|
|
self.play(Transform(group_group, new_group_group))
|
|
self.wait(2)
|
|
|
|
self.slot_groups = slot_group_groups[-1]
|
|
|
|
def count_patterns(self):
|
|
brace = Brace(self.slot_groups, LEFT)
|
|
count = TexMobject("2^3 = 8")
|
|
count.next_to(brace, LEFT)
|
|
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
Write(count)
|
|
)
|
|
self.wait()
|
|
|
|
#######
|
|
|
|
def get_slot_group(self, bool_list):
|
|
return get_slot_group(bool_list, include_qs = len(bool_list) < 3)
|
|
|
|
class AssociatePatternsWithScores(BreakDownQuestionPatterns):
|
|
CONFIG = {
|
|
"score_group_scale_val" : 0.8,
|
|
}
|
|
def construct(self):
|
|
self.add_slot_groups()
|
|
self.show_score_groups()
|
|
self.think_about_binomial_patterns()
|
|
|
|
def add_slot_groups(self):
|
|
self.slot_groups = VGroup(*map(
|
|
self.get_slot_group,
|
|
it.product(*[[True, False]]*3)
|
|
))
|
|
self.add(self.slot_groups)
|
|
self.remove(self.randy)
|
|
|
|
def show_score_groups(self):
|
|
score_groups = [VGroup() for x in range(4)]
|
|
scores = VGroup()
|
|
full_score_groups = VGroup()
|
|
|
|
for slot_group in self.slot_groups:
|
|
score_groups[sum(slot_group.bool_list)].add(slot_group)
|
|
for i, score_group in enumerate(score_groups):
|
|
score = TextMobject("Score", "=", str(i))
|
|
score.set_color_by_tex("Score", GREEN)
|
|
scores.add(score)
|
|
score_group.organized = score_group.deepcopy()
|
|
score_group.organized.arrange_submobjects(UP, buff = SMALL_BUFF)
|
|
score_group.organized.scale(self.score_group_scale_val)
|
|
brace = Brace(score_group.organized, LEFT)
|
|
score.next_to(brace, LEFT)
|
|
score.add(brace)
|
|
full_score_groups.add(VGroup(score, score_group.organized))
|
|
full_score_groups.arrange_submobjects(
|
|
DOWN, buff = MED_LARGE_BUFF,
|
|
aligned_edge = RIGHT
|
|
)
|
|
full_score_groups.to_edge(LEFT)
|
|
|
|
for score, score_group in zip(scores, score_groups):
|
|
score_group.save_state()
|
|
self.play(score_group.next_to, score_group, LEFT, MED_LARGE_BUFF)
|
|
self.wait()
|
|
self.play(
|
|
ReplacementTransform(
|
|
score_group.copy(), score_group.organized
|
|
),
|
|
LaggedStart(FadeIn, score, run_time = 1)
|
|
)
|
|
self.play(score_group.restore)
|
|
self.wait()
|
|
|
|
def think_about_binomial_patterns(self):
|
|
triangle = PascalsTriangle(
|
|
nrows = 5,
|
|
height = 3,
|
|
width = 3,
|
|
)
|
|
triangle.to_edge(UP+RIGHT)
|
|
row = VGroup(*[
|
|
triangle.coords_to_mobs[3][k]
|
|
for k in range(4)
|
|
])
|
|
self.randy.center().to_edge(DOWN)
|
|
bubble = ThoughtBubble()
|
|
bubble.add_content(triangle)
|
|
bubble.resize_to_content()
|
|
triangle.shift(SMALL_BUFF*(3*UP + RIGHT))
|
|
bubble.add(triangle)
|
|
bubble.next_to(self.randy, UP+RIGHT, SMALL_BUFF)
|
|
bubble.remove(triangle)
|
|
|
|
self.play(
|
|
FadeOut(self.slot_groups),
|
|
FadeIn(self.randy),
|
|
FadeIn(bubble)
|
|
)
|
|
self.play(
|
|
self.randy.change, "pondering",
|
|
LaggedStart(FadeIn, triangle, run_time = 4),
|
|
)
|
|
self.play(row.set_color, YELLOW)
|
|
self.wait(4)
|
|
|
|
class BeforeCounting(TeacherStudentsScene):
|
|
def construct(self):
|
|
triangle = PascalsTriangle(nrows = 7)
|
|
triangle.scale_to_fit_height(4)
|
|
triangle.next_to(self.teacher, UP+LEFT)
|
|
|
|
prob = get_probability_of_slot_group([True, True, False])
|
|
prob.to_edge(UP)
|
|
brace = Brace(prob, DOWN)
|
|
q_marks = brace.get_text("???")
|
|
|
|
self.teacher.change_mode("raise_right_hand")
|
|
self.add(triangle)
|
|
self.change_student_modes(*["hooray"]*3)
|
|
self.play(
|
|
triangle.scale, 0.5,
|
|
triangle.to_corner, UP+RIGHT,
|
|
self.teacher.change_mode, "sassy"
|
|
)
|
|
self.change_student_modes(*["confused"]*3)
|
|
self.play(Write(prob))
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
LaggedStart(FadeIn, q_marks)
|
|
)
|
|
self.wait(2)
|
|
|
|
class TemptingButWrongCalculation(BreakDownQuestionPatterns):
|
|
def construct(self):
|
|
self.add_title()
|
|
self.write_simple_product()
|
|
|
|
def add_title(self):
|
|
title = TextMobject("Tempting$\\dots$")
|
|
title.scale(1.5)
|
|
title.to_edge(UP)
|
|
self.add(title)
|
|
self.title = title
|
|
|
|
def write_simple_product(self):
|
|
lhs = TexMobject("P\\big(", "Filler Blah", "\\big)", "= ")
|
|
lhs.next_to(ORIGIN, UP+LEFT)
|
|
p_of = lhs.get_part_by_tex("P\\big(")
|
|
filler = lhs.get_part_by_tex("Filler")
|
|
rp = lhs.get_part_by_tex("\\big)")
|
|
slot_group = self.get_slot_group([True, True, False])
|
|
slot_group.replace(filler, dim_to_match = 0)
|
|
lhs.submobjects.remove(filler)
|
|
|
|
rhs = VGroup(*[
|
|
TexMobject("P(", "\\checkmark" if b else "\\times", ")")
|
|
for b in slot_group.bool_list
|
|
])
|
|
rhs.arrange_submobjects(RIGHT, SMALL_BUFF)
|
|
rhs.next_to(lhs, RIGHT, SMALL_BUFF)
|
|
for part, b in zip(rhs, slot_group.bool_list):
|
|
part.set_color_by_tex_to_color_map({
|
|
"checkmark" : GREEN,
|
|
"times" : RED,
|
|
})
|
|
brace = Brace(part, UP)
|
|
if b:
|
|
value = TexMobject("(0.8)")
|
|
else:
|
|
value = TexMobject("(0.2)")
|
|
value.set_color(part[1].get_color())
|
|
value.next_to(brace, UP)
|
|
part.brace = brace
|
|
part.value = value
|
|
|
|
question = TextMobject("What about correlations?")
|
|
question.next_to(rhs, DOWN, LARGE_BUFF)
|
|
|
|
self.play(
|
|
Write(lhs),
|
|
ShowCreation(slot_group.lines),
|
|
LaggedStart(FadeIn, slot_group.content, run_time = 3),
|
|
self.randy.change, "pondering"
|
|
)
|
|
self.wait(2)
|
|
for part, mob in zip(rhs, slot_group.content):
|
|
self.play(*[
|
|
ReplacementTransform(
|
|
mob.copy(), subpart,
|
|
path_arc = np.pi/6
|
|
)
|
|
for subpart, mob in zip(part, [
|
|
p_of, mob, rp
|
|
])
|
|
])
|
|
self.play(GrowFromCenter(part.brace))
|
|
self.play(FadeIn(part.value))
|
|
self.wait()
|
|
self.wait()
|
|
self.play(
|
|
Write(question),
|
|
self.randy.change, "confused"
|
|
)
|
|
self.wait(3)
|
|
|
|
self.question = question
|
|
self.rhs = rhs
|
|
|
|
class ThousandPossibleQuizzes(Scene):
|
|
CONFIG = {
|
|
"n_quiz_rows" : 25,
|
|
"n_quiz_cols" : 40,
|
|
"n_movers" : 100,
|
|
# "n_quiz_rows" : 5,
|
|
# "n_quiz_cols" : 8,
|
|
# "n_movers" : 4,
|
|
"quizzes_height" : 4,
|
|
}
|
|
def construct(self):
|
|
self.draw_all_quizzes()
|
|
self.show_division_by_first_question()
|
|
self.ask_about_second_question()
|
|
self.show_uncorrelated_division_by_second()
|
|
self.increase_second_correct_slice()
|
|
self.second_division_among_first_wrong()
|
|
self.show_that_second_is_still_80()
|
|
self.emphasize_disproportionate_divide()
|
|
self.show_third_question_results()
|
|
|
|
def draw_all_quizzes(self):
|
|
quizzes = self.get_thousand_quizzes()
|
|
title = TextMobject("$1{,}000$ possible quizzes")
|
|
title.scale(1.5)
|
|
title.next_to(quizzes, UP)
|
|
full_quizzes = VGroup(
|
|
get_quiz(
|
|
"Define ``Brachistochrone''",
|
|
"Define ``Tautochrone''",
|
|
"Define ``Cycloid''",
|
|
),
|
|
get_quiz(
|
|
"Define $\\dfrac{df}{dx}$",
|
|
"Define $\\displaystyle \\lim_{h \\to 0} f(h)$",
|
|
"Prove $\\dfrac{d(x^2)}{dx} = 2x$ ",
|
|
),
|
|
get_quiz(
|
|
"Find all primes $p$ \\\\ where $p+2$ is prime.",
|
|
"Find all primes $p$ \\\\ where $2^{p}-1$ is prime.",
|
|
"Solve $\\zeta(s) = 0$",
|
|
),
|
|
)
|
|
full_quizzes.arrange_submobjects(RIGHT)
|
|
target_quizzes = VGroup(*quizzes[:len(full_quizzes)])
|
|
|
|
for quiz in full_quizzes:
|
|
self.play(FadeIn(quiz, run_time = 3, submobject_mode = "lagged_start"))
|
|
self.play(
|
|
Transform(full_quizzes, target_quizzes),
|
|
FadeIn(title)
|
|
)
|
|
self.play(
|
|
LaggedStart(
|
|
FadeIn, quizzes,
|
|
run_time = 3,
|
|
lag_ratio = 0.2,
|
|
),
|
|
Animation(full_quizzes, remover = True)
|
|
)
|
|
self.wait()
|
|
|
|
self.quizzes = quizzes
|
|
self.title = title
|
|
|
|
def show_division_by_first_question(self):
|
|
n = int(0.8*len(self.quizzes))
|
|
top_split = VGroup(*self.quizzes[:n])
|
|
bottom_split = VGroup(*self.quizzes[n:])
|
|
for split, color, vect in (top_split, GREEN, UP), (bottom_split, RED, DOWN):
|
|
split.sort_submobjects(lambda p : p[0])
|
|
split.generate_target()
|
|
split.target.shift(MED_LARGE_BUFF*vect)
|
|
for quiz in split.target:
|
|
quiz[0].set_color(color)
|
|
|
|
labels = VGroup()
|
|
for num, b, split in (800, True, top_split), (200, False, bottom_split):
|
|
label = VGroup(
|
|
TexMobject(str(num)),
|
|
get_slot_group([b], buff = SMALL_BUFF, include_qs = False)
|
|
)
|
|
label.arrange_submobjects(DOWN)
|
|
label.next_to(split.target, LEFT, buff = LARGE_BUFF)
|
|
labels.add(label)
|
|
|
|
self.play(
|
|
FadeOut(self.title),
|
|
MoveToTarget(top_split),
|
|
MoveToTarget(bottom_split),
|
|
)
|
|
for label in labels:
|
|
self.play(FadeIn(label))
|
|
self.wait()
|
|
|
|
self.splits = VGroup(top_split, bottom_split)
|
|
self.q1_split_labels = labels
|
|
|
|
def ask_about_second_question(self):
|
|
top_split = self.splits[0]
|
|
sg1, sg2 = slot_groups = VGroup(*[
|
|
get_slot_group(
|
|
[True, b],
|
|
include_qs = False,
|
|
buff = SMALL_BUFF
|
|
)
|
|
for b in True, False
|
|
])
|
|
question = VGroup(
|
|
TextMobject("Where are"), sg1,
|
|
TextMobject("and"), sg2, TextMobject("?"),
|
|
)
|
|
question.arrange_submobjects(RIGHT, aligned_edge = DOWN)
|
|
question[-1].next_to(question[-2], RIGHT, SMALL_BUFF)
|
|
question.next_to(top_split, UP, MED_LARGE_BUFF)
|
|
slot_groups.shift(SMALL_BUFF*DOWN)
|
|
little_rects = VGroup(*[
|
|
SurroundingRectangle(
|
|
VGroup(sg.lines[1], sg.content[1])
|
|
)
|
|
for sg in slot_groups
|
|
])
|
|
big_rect = SurroundingRectangle(top_split)
|
|
|
|
self.play(Write(question))
|
|
self.play(ShowCreation(little_rects))
|
|
self.wait()
|
|
self.play(FadeOut(little_rects))
|
|
self.play(ShowCreation(big_rect))
|
|
self.play(
|
|
FadeOut(big_rect),
|
|
FadeOut(question),
|
|
)
|
|
self.wait()
|
|
|
|
def show_uncorrelated_division_by_second(self):
|
|
top_split = self.splits[0]
|
|
top_label = self.q1_split_labels[0]
|
|
n = int(0.8*len(top_split))
|
|
left_split = VGroup(*top_split[:n])
|
|
right_split = VGroup(*top_split[n:])
|
|
for split, color in (left_split, GREEN_E), (right_split, RED_E):
|
|
split.generate_target()
|
|
for quiz in split.target:
|
|
quiz[1].set_color(color)
|
|
left_split.target.shift(LEFT)
|
|
|
|
left_label = VGroup(
|
|
TexMobject("(0.8)", "800 =", "640"),
|
|
get_slot_group([True, True], buff = SMALL_BUFF, include_qs = False)
|
|
)
|
|
left_label.arrange_submobjects(RIGHT, buff = MED_LARGE_BUFF)
|
|
left_label.next_to(left_split.target, UP)
|
|
|
|
self.play(
|
|
MoveToTarget(left_split),
|
|
MaintainPositionRelativeTo(top_label, left_split),
|
|
MoveToTarget(right_split),
|
|
)
|
|
self.play(FadeIn(left_label))
|
|
self.play(LaggedStart(
|
|
ApplyMethod, left_split,
|
|
lambda m : (m.set_color, YELLOW),
|
|
rate_func = there_and_back,
|
|
lag_ratio = 0.2,
|
|
))
|
|
self.wait()
|
|
|
|
self.top_left_label = left_label
|
|
self.top_splits = VGroup(left_split, right_split)
|
|
|
|
def increase_second_correct_slice(self):
|
|
left_split, right_split = self.top_splits
|
|
left_label = self.top_left_label
|
|
left_label_equation = left_label[0]
|
|
movers = VGroup(*right_split[:self.n_movers])
|
|
movers.generate_target()
|
|
for quiz in movers.target:
|
|
quiz[1].set_color(left_split[0][1].get_color())
|
|
movers.target.shift(LEFT)
|
|
|
|
new_equation = TexMobject("(0.925)", "800 =", "740")
|
|
for i in 0, 2:
|
|
new_equation[i].set_color(YELLOW)
|
|
new_equation.move_to(left_label_equation)
|
|
|
|
self.play(
|
|
MoveToTarget(
|
|
movers,
|
|
submobject_mode = "lagged_start",
|
|
lag_factor = 4,
|
|
run_time = 3,
|
|
),
|
|
Transform(left_label_equation, new_equation)
|
|
)
|
|
self.wait(2)
|
|
self.play(Indicate(left_label_equation[0]))
|
|
self.wait()
|
|
|
|
left_split.add(*movers)
|
|
right_split.remove(*movers)
|
|
self.top_left_split = left_split
|
|
self.top_right_split = right_split
|
|
self.top_movers = movers
|
|
self.top_equation = left_label_equation
|
|
|
|
def second_division_among_first_wrong(self):
|
|
top_label, bottom_label = self.q1_split_labels
|
|
top_split, bottom_split = self.splits
|
|
top_left_label = self.top_left_label
|
|
top_group = VGroup(top_split, top_left_label, top_label)
|
|
|
|
n = int(0.8*len(bottom_split))
|
|
left_split = VGroup(*bottom_split[:n])
|
|
right_split = VGroup(*bottom_split[n:])
|
|
for split, color in (left_split, GREEN_E), (right_split, RED_E):
|
|
split.generate_target()
|
|
for quiz in split.target:
|
|
quiz[1].set_color(color)
|
|
left_split.target.shift(LEFT)
|
|
|
|
movers = VGroup(*left_split[-self.n_movers:])
|
|
movers.generate_target()
|
|
for quiz in movers.target:
|
|
quiz[1].set_color(right_split.target[0][1].get_color())
|
|
|
|
equation = TexMobject("(0.8)", "200 = ", "160")
|
|
slot_group = get_slot_group([False, True], buff = SMALL_BUFF, include_qs = False)
|
|
label = VGroup(equation, slot_group)
|
|
label.arrange_submobjects(DOWN, buff = SMALL_BUFF)
|
|
label.next_to(left_split.target, UP, SMALL_BUFF, LEFT)
|
|
alt_equation = TexMobject("(0.3)", "200 = ", "60")
|
|
for i in 0, 2:
|
|
alt_equation[i].set_color(YELLOW)
|
|
alt_equation.move_to(equation)
|
|
|
|
self.play(top_group.to_edge, UP, SMALL_BUFF)
|
|
self.play(
|
|
bottom_label.shift, LEFT,
|
|
*map(MoveToTarget, [left_split, right_split])
|
|
)
|
|
self.play(FadeIn(label))
|
|
self.wait()
|
|
self.play(
|
|
MoveToTarget(
|
|
movers,
|
|
submobject_mode = "lagged_start",
|
|
run_time = 3,
|
|
),
|
|
Transform(equation, alt_equation)
|
|
)
|
|
self.wait()
|
|
|
|
left_split.remove(*movers)
|
|
right_split.add(*movers)
|
|
self.bottom_left_split = left_split
|
|
self.bottom_right_split = right_split
|
|
self.bottom_movers = movers
|
|
self.bottom_equation = equation
|
|
self.bottom_left_label = label
|
|
|
|
def show_that_second_is_still_80(self):
|
|
second_right = VGroup(
|
|
self.bottom_left_split, self.top_left_split
|
|
)
|
|
second_wrong = VGroup(
|
|
self.bottom_right_split, self.top_right_split
|
|
)
|
|
rects = VGroup(*[
|
|
SurroundingRectangle(mob, buff = SMALL_BUFF)
|
|
for mob in second_right
|
|
])
|
|
|
|
num1 = self.top_equation[-1].copy()
|
|
num2 = self.bottom_equation[-1].copy()
|
|
|
|
equation = TexMobject("740", "+", "60", "=", "800")
|
|
for tex in "740", "60":
|
|
equation.set_color_by_tex(tex, YELLOW)
|
|
slot_group = get_slot_group([True, True])
|
|
slot_group.content[0].set_fill(BLACK, 0)
|
|
label = VGroup(equation, slot_group)
|
|
label.arrange_submobjects(DOWN)
|
|
label.next_to(self.quizzes, LEFT, LARGE_BUFF)
|
|
|
|
self.play(
|
|
FadeOut(self.q1_split_labels),
|
|
ShowCreation(rects)
|
|
)
|
|
self.play(
|
|
FadeIn(slot_group),
|
|
Transform(
|
|
num1, equation[0],
|
|
rate_func = squish_rate_func(smooth, 0, 0.7),
|
|
),
|
|
Transform(
|
|
num2, equation[2],
|
|
rate_func = squish_rate_func(smooth, 0.3, 1),
|
|
),
|
|
run_time = 2
|
|
)
|
|
self.play(
|
|
Write(equation),
|
|
*map(Animation, [num1, num2])
|
|
)
|
|
self.remove(num1, num2)
|
|
self.wait()
|
|
self.play(FadeOut(rects))
|
|
|
|
def emphasize_disproportionate_divide(self):
|
|
top_movers = self.top_movers
|
|
bottom_movers = self.bottom_movers
|
|
both_movers = VGroup(top_movers, bottom_movers)
|
|
both_movers.save_state()
|
|
|
|
top_movers.target = bottom_movers.copy().shift(LEFT)
|
|
bottom_movers.target = top_movers.copy().shift(RIGHT)
|
|
for quiz in top_movers.target:
|
|
quiz[0].set_color(RED)
|
|
for quiz in bottom_movers.target:
|
|
quiz[0].set_color(GREEN)
|
|
|
|
line = Line(UP, DOWN, color = YELLOW)
|
|
line.scale_to_fit_height(self.quizzes.get_height())
|
|
line.next_to(bottom_movers.target, LEFT, MED_LARGE_BUFF, UP)
|
|
|
|
self.revert_to_original_skipping_status()
|
|
self.play(*map(MoveToTarget, both_movers))
|
|
self.play(ShowCreation(line))
|
|
self.play(FadeOut(line))
|
|
self.wait()
|
|
self.play(both_movers.restore)
|
|
self.wait()
|
|
|
|
def show_third_question_results(self):
|
|
all_splits = VGroup(
|
|
self.top_left_split, self.top_right_split,
|
|
self.bottom_left_split, self.bottom_right_split
|
|
)
|
|
proportions = [0.9, 0.8, 0.8, 0.4]
|
|
for split, prop in zip(all_splits, proportions):
|
|
n = int(prop*len(split))
|
|
split.sort_submobjects(lambda p : -p[1])
|
|
split.generate_target()
|
|
top_part = VGroup(*split.target[:n])
|
|
top_part.shift(MED_SMALL_BUFF*UP)
|
|
bottom_part = VGroup(*split.target[n:])
|
|
bottom_part.shift(MED_SMALL_BUFF*DOWN)
|
|
for quiz in top_part:
|
|
quiz[-1].set_color(GREEN)
|
|
for quiz in bottom_part:
|
|
quiz[-1].set_color(RED)
|
|
|
|
split = self.top_left_split
|
|
n_all_right = int(proportions[0]*len(split))
|
|
all_right = VGroup(*split[:n_all_right])
|
|
|
|
self.play(
|
|
FadeOut(self.top_left_label),
|
|
FadeOut(self.bottom_left_label),
|
|
)
|
|
for split in all_splits:
|
|
self.play(MoveToTarget(split))
|
|
self.wait()
|
|
self.play(LaggedStart(
|
|
ApplyMethod, all_right,
|
|
lambda m : (m.set_color, YELLOW),
|
|
rate_func = there_and_back,
|
|
lag_ratio = 0.2,
|
|
run_time = 2
|
|
))
|
|
self.wait(2)
|
|
|
|
#####
|
|
|
|
def get_thousand_quizzes(self):
|
|
rows = VGroup()
|
|
for x in xrange(self.n_quiz_rows):
|
|
quiz = VGroup(*[
|
|
Rectangle(
|
|
height = SMALL_BUFF,
|
|
width = 0.5*SMALL_BUFF
|
|
)
|
|
for x in range(3)
|
|
])
|
|
quiz.arrange_submobjects(RIGHT, buff = 0)
|
|
quiz.set_stroke(width = 0)
|
|
quiz.set_fill(LIGHT_GREY, 1)
|
|
row = VGroup(*[quiz.copy() for y in range(self.n_quiz_cols)])
|
|
row.arrange_submobjects(RIGHT, buff = SMALL_BUFF)
|
|
rows.add(row)
|
|
|
|
rows.arrange_submobjects(DOWN, buff = SMALL_BUFF)
|
|
quizzes = VGroup(*it.chain(*rows))
|
|
quizzes.scale_to_fit_height(self.quizzes_height)
|
|
quizzes.to_edge(RIGHT)
|
|
quizzes.shift(MED_LARGE_BUFF*DOWN)
|
|
return quizzes
|
|
|
|
class ExampleConditional(Scene):
|
|
def construct(self):
|
|
prob = get_probability_of_slot_group(
|
|
[True, True], [True]
|
|
)
|
|
rhs = TexMobject("=", "0.925", ">", "0.8")
|
|
rhs.set_color_by_tex("0.925", YELLOW)
|
|
rhs.next_to(prob, RIGHT)
|
|
expression = VGroup(prob, rhs)
|
|
expression.scale_to_fit_width(FRAME_WIDTH - 1)
|
|
expression.center().to_edge(DOWN)
|
|
|
|
self.play(Write(expression))
|
|
self.wait()
|
|
|
|
class HarderQuizzes(Scene):
|
|
def construct(self):
|
|
quizzes = VGroup(
|
|
get_quiz(
|
|
"Find all primes $p$ \\\\ where $p+2$ is prime.",
|
|
"Find all primes $p$ \\\\ where $2^{p}-1$ is prime.",
|
|
"Solve $\\zeta(s) = 0$",
|
|
),
|
|
get_quiz(
|
|
"Find $S$ such that \\\\ $\\#\\mathds{N} < \\#S < \\#\\mathcal{P}(\\mathds{N})$",
|
|
"Describe ``forcing''",
|
|
"Prove from ZFC that $S \\notin S$.",
|
|
),
|
|
)
|
|
quizzes.arrange_submobjects(RIGHT)
|
|
quizzes.to_edge(DOWN)
|
|
crosses = VGroup(*[
|
|
Cross(quiz.questions[0])
|
|
for quiz in quizzes
|
|
])
|
|
|
|
for quiz in quizzes:
|
|
self.play(FadeIn(quiz))
|
|
self.wait()
|
|
for cross in crosses:
|
|
self.play(ShowCreation(cross))
|
|
self.wait()
|
|
|
|
class WritePSecond(Scene):
|
|
def construct(self):
|
|
prob = get_probability_of_slot_group([None, True, None])
|
|
rhs = TexMobject("= 0.8")
|
|
rhs.next_to(prob, RIGHT)
|
|
prob.add(rhs)
|
|
prob.scale_to_fit_width(FRAME_WIDTH - 1)
|
|
prob.center().to_edge(DOWN)
|
|
self.play(Write(prob))
|
|
|
|
class SubmitToTemptation(TemptingButWrongCalculation):
|
|
def construct(self):
|
|
self.force_skipping()
|
|
TemptingButWrongCalculation.construct(self)
|
|
self.revert_to_original_skipping_status()
|
|
|
|
title = self.title
|
|
question = self.question
|
|
title.generate_target()
|
|
title.target.scale_in_place(1./1.5)
|
|
new_words = TextMobject("and", "okay", "assuming independence.")
|
|
new_words.set_color_by_tex("okay", GREEN)
|
|
new_words.next_to(title.target, RIGHT)
|
|
VGroup(title.target, new_words).center().to_edge(UP)
|
|
|
|
self.play(
|
|
MoveToTarget(title),
|
|
FadeOut(question)
|
|
)
|
|
self.play(
|
|
Write(new_words, run_time = 2),
|
|
self.randy.change, "hooray"
|
|
)
|
|
for part in self.rhs:
|
|
self.play(Indicate(part.value))
|
|
self.wait()
|
|
|
|
class AccurateProductRule(SampleSpaceScene, ThreeDScene):
|
|
def construct(self):
|
|
self.setup_terms()
|
|
self.add_sample_space()
|
|
self.wait()
|
|
self.show_first_division()
|
|
self.show_second_division()
|
|
self.move_to_third_dimension()
|
|
self.show_final_probability()
|
|
self.show_confusion()
|
|
|
|
def setup_terms(self):
|
|
filler_tex = "Filler"
|
|
lhs = TexMobject("P(", filler_tex, ")", "=")
|
|
p1 = TexMobject("P(", filler_tex, ")")
|
|
p2 = TexMobject("P(", filler_tex, "|", filler_tex, ")")
|
|
p3 = TexMobject("P(", filler_tex, "|", filler_tex, ")")
|
|
terms = VGroup(lhs, p1, p2, p3)
|
|
terms.arrange_submobjects(RIGHT, buff = SMALL_BUFF)
|
|
terms.to_edge(UP, buff = LARGE_BUFF)
|
|
|
|
kwargs = {"buff" : SMALL_BUFF, "include_qs" : False}
|
|
slot_group_lists = [
|
|
[get_slot_group([True, True, False], **kwargs)],
|
|
[get_slot_group([True], **kwargs)],
|
|
[
|
|
get_slot_group([True, True], **kwargs),
|
|
get_slot_group([True], **kwargs),
|
|
],
|
|
[
|
|
get_slot_group([True, True, False], **kwargs),
|
|
get_slot_group([True, True], **kwargs),
|
|
],
|
|
]
|
|
for term, slot_group_list in zip(terms, slot_group_lists):
|
|
parts = term.get_parts_by_tex(filler_tex)
|
|
for part, slot_group in zip(parts, slot_group_list):
|
|
slot_group.replace(part, dim_to_match = 0)
|
|
term.submobjects[term.index_of_part(part)] = slot_group
|
|
# terms[2][1].content[0].set_fill(BLACK, 0)
|
|
# VGroup(*terms[3][1].content[:2]).set_fill(BLACK, 0)
|
|
|
|
value_texs = ["0.8", ">0.8", "<0.2"]
|
|
for term, tex in zip(terms[1:], value_texs):
|
|
term.value = TexMobject(tex)
|
|
term.value.next_to(term, UP)
|
|
|
|
self.terms = terms
|
|
self.add(terms[0])
|
|
|
|
def add_sample_space(self):
|
|
SampleSpaceScene.add_sample_space(self, height = 4, width = 5)
|
|
self.sample_space.to_edge(DOWN)
|
|
|
|
def show_first_division(self):
|
|
space = self.sample_space
|
|
space.divide_horizontally(
|
|
[0.8], colors = [GREEN_E, RED_E]
|
|
)
|
|
space.horizontal_parts.fade(0.1)
|
|
top_label = self.terms[1].copy()
|
|
bottom_label = top_label.copy()
|
|
slot_group = get_slot_group([False], buff = SMALL_BUFF, include_qs = False)
|
|
slot_group.replace(bottom_label[1])
|
|
Transform(bottom_label[1], slot_group).update(1)
|
|
braces_and_labels = space.get_side_braces_and_labels(
|
|
[top_label, bottom_label]
|
|
)
|
|
|
|
self.play(
|
|
FadeIn(space.horizontal_parts),
|
|
FadeIn(braces_and_labels)
|
|
)
|
|
self.play(ReplacementTransform(
|
|
top_label.copy(), self.terms[1]
|
|
))
|
|
self.wait()
|
|
self.play(Write(self.terms[1].value))
|
|
self.wait()
|
|
|
|
space.add(braces_and_labels)
|
|
self.top_part = space.horizontal_parts[0]
|
|
|
|
def show_second_division(self):
|
|
space = self.sample_space
|
|
top_part = self.top_part
|
|
green_red_mix = average_color(GREEN_E, RED_E)
|
|
top_part.divide_vertically(
|
|
[0.9], colors = [GREEN_E, green_red_mix]
|
|
)
|
|
label = self.terms[2].deepcopy()
|
|
braces_and_labels = top_part.get_top_braces_and_labels(
|
|
labels = [label]
|
|
)
|
|
|
|
self.play(
|
|
FadeIn(top_part.vertical_parts),
|
|
FadeIn(braces_and_labels)
|
|
)
|
|
self.play(ReplacementTransform(
|
|
label.copy(), self.terms[2]
|
|
))
|
|
self.wait()
|
|
self.play(Write(self.terms[2].value))
|
|
self.wait()
|
|
|
|
space.add(braces_and_labels)
|
|
self.top_left_part = top_part.vertical_parts[0]
|
|
|
|
def move_to_third_dimension(self):
|
|
space = self.sample_space
|
|
part = self.top_left_part
|
|
cubes = VGroup(
|
|
Cube(fill_color = RED_E),
|
|
Cube(fill_color = GREEN_E),
|
|
)
|
|
cubes.set_fill(opacity = 0)
|
|
cubes.stretch_to_fit_width(part.get_width())
|
|
cubes.stretch_to_fit_height(part.get_height())
|
|
cubes[1].move_to(part, IN)
|
|
cubes[0].stretch(0.2, 2)
|
|
cubes[0].move_to(cubes[1].get_edge_center(OUT), IN)
|
|
space.add(cubes)
|
|
|
|
self.play(
|
|
space.rotate, 0.9*np.pi/2, LEFT,
|
|
space.rotate, np.pi/12, UP,
|
|
space.to_corner, DOWN+RIGHT, LARGE_BUFF
|
|
)
|
|
space.remove(cubes)
|
|
self.play(
|
|
cubes[0].set_fill, None, 1,
|
|
cubes[0].set_stroke, WHITE, 1,
|
|
cubes[1].set_fill, None, 0.5,
|
|
cubes[1].set_stroke, WHITE, 1,
|
|
)
|
|
self.wait()
|
|
|
|
self.cubes = cubes
|
|
|
|
def show_final_probability(self):
|
|
cube = self.cubes[0]
|
|
face = cube[2]
|
|
points = face.get_anchors()
|
|
line = Line(points[2], points[3])
|
|
line.set_stroke(YELLOW, 8)
|
|
brace = Brace(line, LEFT)
|
|
label = self.terms[3].copy()
|
|
label.next_to(brace, LEFT)
|
|
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
FadeIn(label),
|
|
)
|
|
self.wait()
|
|
self.play(ReplacementTransform(
|
|
label.copy(), self.terms[3]
|
|
))
|
|
self.wait()
|
|
|
|
def show_confusion(self):
|
|
randy = Randolph()
|
|
randy.to_corner(DOWN+LEFT)
|
|
|
|
self.play(FadeIn(randy))
|
|
self.play(randy.change, "confused", self.terms)
|
|
self.play(randy.look_at, self.cubes)
|
|
self.play(Blink(randy))
|
|
self.play(randy.look_at, self.terms)
|
|
self.wait()
|
|
|
|
class ShowAllEightConditionals(Scene):
|
|
def construct(self):
|
|
self.show_all_conditionals()
|
|
self.suggest_independence()
|
|
|
|
def show_all_conditionals(self):
|
|
equations = VGroup()
|
|
filler_tex = "Filler"
|
|
for bool_list in it.product(*[[True, False]]*3):
|
|
equation = TexMobject(
|
|
"P(", filler_tex, ")", "=",
|
|
"P(", filler_tex, ")",
|
|
"P(", filler_tex, "|", filler_tex, ")",
|
|
"P(", filler_tex, "|", filler_tex, ")",
|
|
)
|
|
sub_bool_lists = [
|
|
bool_list[:n] for n in 3, 1, 2, 1, 3, 2
|
|
]
|
|
parts = equation.get_parts_by_tex(filler_tex)
|
|
for part, sub_list in zip(parts, sub_bool_lists):
|
|
slot_group = get_slot_group(
|
|
sub_list,
|
|
buff = SMALL_BUFF,
|
|
include_qs = False
|
|
)
|
|
slot_group.replace(part, dim_to_match = 0)
|
|
index = equation.index_of_part(part)
|
|
equation.submobjects[index] = slot_group
|
|
equations.add(equation)
|
|
equations.arrange_submobjects(DOWN)
|
|
|
|
rect = SurroundingRectangle(
|
|
VGroup(*equations[0][7:]+equations[-1][7:]),
|
|
buff = SMALL_BUFF
|
|
)
|
|
rect.shift(0.5*SMALL_BUFF*RIGHT)
|
|
|
|
self.play(LaggedStart(
|
|
FadeIn, equations,
|
|
run_time = 5,
|
|
lag_ratio = 0.3
|
|
))
|
|
self.wait()
|
|
self.play(ShowCreation(rect, run_time = 2))
|
|
self.play(FadeOut(rect))
|
|
self.wait()
|
|
|
|
def suggest_independence(self):
|
|
full_screen_rect = FullScreenFadeRectangle()
|
|
randy = Randolph()
|
|
randy.to_corner(DOWN+LEFT)
|
|
|
|
|
|
self.play(
|
|
FadeIn(full_screen_rect),
|
|
FadeIn(randy)
|
|
)
|
|
self.play(PiCreatureSays(
|
|
randy, "Let's just assume \\\\ independence.",
|
|
target_mode = "shruggie"
|
|
))
|
|
self.play(Blink(randy))
|
|
self.wait()
|
|
|
|
class ShowIndependenceSymbolically(Scene):
|
|
def construct(self):
|
|
filler_tex = "Filler"
|
|
rhs = TexMobject("=", "0.8")
|
|
rhs.set_color_by_tex("0.8", YELLOW)
|
|
rhs.next_to(ORIGIN, RIGHT, LARGE_BUFF)
|
|
lhs = TexMobject("P(", filler_tex, "|", filler_tex, ")")
|
|
lhs.next_to(rhs, LEFT)
|
|
VGroup(lhs, rhs).scale(1.5)
|
|
for part in lhs.get_parts_by_tex(filler_tex):
|
|
slot_group = get_slot_group(
|
|
[True, True, True],
|
|
buff = SMALL_BUFF,
|
|
include_qs = False,
|
|
)
|
|
slot_group.replace(part, dim_to_match = 0)
|
|
lhs.submobjects[lhs.index_of_part(part)] = slot_group
|
|
VGroup(*lhs[1].content[:2]).set_fill(BLACK, 0)
|
|
condition = lhs[3]
|
|
condition.content[2].set_fill(BLACK, 0)
|
|
bool_lists = [
|
|
[False], [True, False], [False, True], [True],
|
|
]
|
|
arrow = Arrow(UP, DOWN)
|
|
arrow.next_to(condition, UP)
|
|
arrow.set_color(RED)
|
|
words = TextMobject("Doesn't matter")
|
|
words.set_color(RED)
|
|
words.next_to(arrow, UP)
|
|
|
|
self.add(rhs, lhs, arrow, words)
|
|
self.wait()
|
|
for bool_list in bool_lists:
|
|
slot_group = get_slot_group(bool_list, SMALL_BUFF, False)
|
|
slot_group.replace(condition)
|
|
slot_group.move_to(condition, DOWN)
|
|
self.play(Transform(condition, slot_group))
|
|
self.wait()
|
|
|
|
class ComputeProbabilityOfOneWrong(Scene):
|
|
CONFIG = {
|
|
"score" : 2,
|
|
"final_result_rhs_tex" : [
|
|
"3", "(0.8)", "^2", "(0.2)", "=", "0.384",
|
|
],
|
|
"default_bool" : True,
|
|
"default_p" : "0.8",
|
|
"default_q" : "0.2",
|
|
}
|
|
def construct(self):
|
|
self.show_all_three_patterns()
|
|
self.show_final_result()
|
|
|
|
def show_all_three_patterns(self):
|
|
probabilities = VGroup()
|
|
point_8s = VGroup()
|
|
point_2s = VGroup()
|
|
for i in reversed(range(3)):
|
|
bool_list = [self.default_bool]*3
|
|
bool_list[i] = not self.default_bool
|
|
probs = ["(%s)"%self.default_p]*3
|
|
probs[i] = "(%s)"%self.default_q
|
|
lhs = get_probability_of_slot_group(bool_list)
|
|
rhs = TexMobject("=", *probs)
|
|
rhs.set_color_by_tex("0.8", GREEN)
|
|
rhs.set_color_by_tex("0.2", RED)
|
|
point_8s.add(*rhs.get_parts_by_tex("0.8"))
|
|
point_2s.add(*rhs.get_parts_by_tex("0.2"))
|
|
rhs.next_to(lhs, RIGHT)
|
|
probabilities.add(VGroup(lhs, rhs))
|
|
probabilities.arrange_submobjects(DOWN, buff = LARGE_BUFF)
|
|
probabilities.center()
|
|
|
|
self.play(Write(probabilities[0]))
|
|
self.wait(2)
|
|
for i in range(2):
|
|
self.play(ReplacementTransform(
|
|
probabilities[i].copy(),
|
|
probabilities[i+1]
|
|
))
|
|
self.wait()
|
|
for group in point_8s, point_2s:
|
|
self.play(LaggedStart(
|
|
Indicate, group,
|
|
rate_func = there_and_back,
|
|
lag_ratio = 0.7
|
|
))
|
|
self.wait()
|
|
|
|
def show_final_result(self):
|
|
result = TexMobject(
|
|
"P(", "\\text{Score} = %s"%self.score, ")", "=",
|
|
*self.final_result_rhs_tex
|
|
)
|
|
result.set_color_by_tex_to_color_map({
|
|
"0.8" : GREEN,
|
|
"0.2" : RED,
|
|
"Score" : YELLOW,
|
|
})
|
|
result[-1].set_color(YELLOW)
|
|
result.set_color_by_tex("0.8", GREEN)
|
|
result.set_color_by_tex("0.2", RED)
|
|
result.to_edge(UP)
|
|
|
|
self.play(Write(result))
|
|
self.wait()
|
|
|
|
class ComputeProbabilityOfOneRight(ComputeProbabilityOfOneWrong):
|
|
CONFIG = {
|
|
"score" : 1,
|
|
"final_result_rhs_tex" : [
|
|
"3", "(0.8)", "(0.2)", "^2", "=", "0.096",
|
|
],
|
|
"default_bool" : False,
|
|
"default_p" : "0.2",
|
|
"default_q" : "0.8",
|
|
}
|
|
|
|
class ShowFullDistribution(Scene):
|
|
def construct(self):
|
|
self.add_scores_one_and_two()
|
|
self.add_scores_zero_and_three()
|
|
self.show_bar_chart()
|
|
self.compare_to_binomial_pattern()
|
|
self.show_alternate_values_of_p()
|
|
|
|
def add_scores_one_and_two(self):
|
|
scores = VGroup(
|
|
TexMobject(
|
|
"P(", "\\text{Score} = 0", ")",
|
|
"=", "(0.2)", "^3",
|
|
"=", "0.008",
|
|
),
|
|
TexMobject(
|
|
"P(", "\\text{Score} = 1", ")",
|
|
"=", "3", "(0.8)", "(0.2)", "^2",
|
|
"=", "0.096",
|
|
),
|
|
TexMobject(
|
|
"P(", "\\text{Score} = 2", ")",
|
|
"=", "3", "(0.8)", "^2", "(0.2)",
|
|
"=", "0.384",
|
|
),
|
|
TexMobject(
|
|
"P(", "\\text{Score} = 3", ")",
|
|
"=", "(0.8)", "^3",
|
|
"=", "0.512",
|
|
),
|
|
)
|
|
scores.arrange_submobjects(
|
|
DOWN,
|
|
buff = MED_LARGE_BUFF,
|
|
aligned_edge = LEFT
|
|
)
|
|
scores.shift(MED_LARGE_BUFF*UP)
|
|
scores.to_edge(LEFT)
|
|
for score in scores:
|
|
score.set_color_by_tex_to_color_map({
|
|
"0.8" : GREEN,
|
|
"0.2" : RED,
|
|
})
|
|
score[-1].set_color(YELLOW)
|
|
|
|
self.add(*scores[1:3])
|
|
self.scores = scores
|
|
|
|
def add_scores_zero_and_three(self):
|
|
self.p_slot_groups = VGroup()
|
|
|
|
self.wait()
|
|
self.add_edge_score(0, UP, False)
|
|
self.add_edge_score(3, DOWN, True)
|
|
|
|
def add_edge_score(self, index, vect, q_bool):
|
|
score = self.scores[index]
|
|
prob = VGroup(*score[:3])
|
|
brace = Brace(prob, vect)
|
|
p_slot_group = get_probability_of_slot_group([q_bool]*3)
|
|
p_slot_group.next_to(brace, vect)
|
|
group = VGroup(*it.chain(p_slot_group, brace, score))
|
|
|
|
self.play(LaggedStart(
|
|
FadeIn, group,
|
|
run_time = 2,
|
|
lag_ratio = 0.7,
|
|
))
|
|
self.wait(2)
|
|
self.p_slot_groups.add(brace, p_slot_group)
|
|
|
|
def show_bar_chart(self):
|
|
p_terms = VGroup()
|
|
to_fade = VGroup(self.p_slot_groups)
|
|
value_mobs = VGroup()
|
|
for score in self.scores:
|
|
p_terms.add(VGroup(*score[:3]))
|
|
to_fade.add(VGroup(*score[3:-1]))
|
|
value_mobs.add(score[-1])
|
|
dist = get_binomial_distribution(3, 0.8)
|
|
values = map(dist, range(4))
|
|
chart = BarChart(
|
|
values, bar_names = range(4),
|
|
)
|
|
chart.shift(DOWN)
|
|
|
|
new_p_terms = VGroup(*[
|
|
TexMobject("P(", "S=%d"%k, ")")
|
|
for k in range(4)
|
|
])
|
|
for term, bar in zip(new_p_terms, chart.bars):
|
|
term[1].set_color(YELLOW)
|
|
term.scale_to_fit_width(1.5*bar.get_width())
|
|
term.next_to(bar, UP)
|
|
|
|
self.play(
|
|
ReplacementTransform(
|
|
value_mobs, chart.bars,
|
|
submobject_mode = "lagged_start",
|
|
run_time = 2
|
|
)
|
|
)
|
|
self.play(
|
|
LaggedStart(FadeIn, VGroup(*it.chain(*[
|
|
submob
|
|
for submob in chart
|
|
if submob is not chart.bars
|
|
]))),
|
|
Transform(p_terms, new_p_terms),
|
|
FadeOut(to_fade),
|
|
)
|
|
self.wait(2)
|
|
|
|
chart.bar_top_labels = p_terms
|
|
chart.add(p_terms)
|
|
self.bar_chart = chart
|
|
|
|
def compare_to_binomial_pattern(self):
|
|
dist = get_binomial_distribution(3, 0.5)
|
|
values = map(dist, range(4))
|
|
alt_chart = BarChart(values)
|
|
alt_chart.move_to(self.bar_chart)
|
|
bars = alt_chart.bars
|
|
bars.set_fill(GREY, opacity = 0.5)
|
|
vect = 4*UP
|
|
bars.shift(vect)
|
|
nums = VGroup(*map(TexMobject, map(str, [1, 3, 3, 1])))
|
|
for num, bar in zip(nums, bars):
|
|
num.next_to(bar, UP)
|
|
bars_copy = bars.copy()
|
|
|
|
self.play(
|
|
LaggedStart(FadeIn, bars),
|
|
LaggedStart(FadeIn, nums),
|
|
)
|
|
self.wait(2)
|
|
self.play(bars_copy.shift, -vect)
|
|
self.play(ReplacementTransform(
|
|
bars_copy, self.bar_chart.bars
|
|
))
|
|
self.wait(2)
|
|
self.play(
|
|
VGroup(self.bar_chart, bars, nums).to_edge, LEFT
|
|
)
|
|
|
|
self.alt_bars = bars
|
|
self.alt_bars_labels = nums
|
|
|
|
def show_alternate_values_of_p(self):
|
|
new_prob = TexMobject(
|
|
"P(", "\\text{Correct}", ")", "=", "0.8"
|
|
)
|
|
new_prob.set_color_by_tex("Correct", GREEN)
|
|
new_prob.shift(FRAME_X_RADIUS*RIGHT/2)
|
|
new_prob.to_edge(UP)
|
|
|
|
alt_ps = 0.5, 0.65, 0.25
|
|
alt_rhss = VGroup()
|
|
alt_charts = VGroup()
|
|
for p in alt_ps:
|
|
rhs = TexMobject(str(p))
|
|
rhs.set_color(YELLOW)
|
|
rhs.move_to(new_prob[-1])
|
|
alt_rhss.add(rhs)
|
|
|
|
dist = get_binomial_distribution(3, p)
|
|
values = map(dist, range(4))
|
|
chart = self.bar_chart.copy()
|
|
chart.change_bar_values(values)
|
|
for label, bar in zip(chart.bar_top_labels, chart.bars):
|
|
label.next_to(bar, UP)
|
|
alt_charts.add(chart)
|
|
|
|
self.play(FadeIn(new_prob))
|
|
self.play(Transform(new_prob[-1], alt_rhss[0]))
|
|
point_5_probs = self.show_point_5_probs(new_prob)
|
|
self.wait()
|
|
self.play(Transform(self.bar_chart, alt_charts[0]))
|
|
self.wait()
|
|
self.play(FadeOut(point_5_probs))
|
|
for rhs, chart in zip(alt_rhss, alt_charts)[1:]:
|
|
self.play(Transform(new_prob[-1], rhs))
|
|
self.play(Transform(self.bar_chart, chart))
|
|
self.wait(2)
|
|
|
|
def show_point_5_probs(self, mob):
|
|
probs = VGroup()
|
|
last = mob
|
|
for k in range(4):
|
|
buff = MED_LARGE_BUFF
|
|
for indices in it.combinations(range(3), k):
|
|
bool_list = np.array([False]*3)
|
|
bool_list[list(indices)] = True
|
|
prob = get_probability_of_slot_group(bool_list)
|
|
rhs = TexMobject("= (0.5)^3")
|
|
rhs.next_to(prob, RIGHT)
|
|
prob.add(rhs)
|
|
prob.scale(0.9)
|
|
prob.next_to(last, DOWN, buff)
|
|
probs.add(prob)
|
|
last = prob
|
|
buff = SMALL_BUFF
|
|
|
|
self.play(LaggedStart(FadeIn, probs))
|
|
self.wait()
|
|
return probs
|
|
|
|
class ProbablyWrong(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.teacher_says(
|
|
"Probably wrong!",
|
|
run_time = 1,
|
|
)
|
|
self.change_student_modes(
|
|
*["angry"]*3,
|
|
run_time = 1
|
|
)
|
|
self.wait()
|
|
|
|
class ShowTrueDistribution(PiCreatureScene):
|
|
def construct(self):
|
|
self.force_skipping()
|
|
|
|
self.remove(self.randy)
|
|
self.add_title()
|
|
self.show_distributions()
|
|
self.show_emotion()
|
|
self.imagine_score_0()
|
|
|
|
self.revert_to_original_skipping_status()
|
|
self.get_angry()
|
|
|
|
def add_title(self):
|
|
title = TexMobject("P(", "\\text{Correct}", ")", "=", "0.65")
|
|
title.to_edge(UP)
|
|
title.set_color_by_tex("Correct", GREEN)
|
|
|
|
self.add(title)
|
|
self.title = title
|
|
|
|
def show_distributions(self):
|
|
dist = get_binomial_distribution(3, 0.65)
|
|
values = np.array(map(dist, range(4)))
|
|
alt_values = values + [0.2, 0, 0, 0.2]
|
|
alt_values /= sum(alt_values)
|
|
chart = BarChart(values, bar_names = range(4))
|
|
bars = chart.bars
|
|
old_bars = bars.copy()
|
|
arrows = VGroup()
|
|
for bar, old_bar in zip(bars, old_bars):
|
|
for mob, vect in (bar, RIGHT), (old_bar, LEFT):
|
|
mob.generate_target()
|
|
mob.target.do_about_point(
|
|
mob.get_corner(DOWN+vect),
|
|
mob.target.stretch, 0.5, 0
|
|
)
|
|
old_bar.target.set_color(average_color(RED_E, BLACK))
|
|
old_bar.target.set_stroke(width = 0)
|
|
arrow = Arrow(ORIGIN, UP, buff = 0, color = GREEN)
|
|
arrow.move_to(bar.get_bottom())
|
|
arrow.shift(3*UP)
|
|
arrows.add(arrow)
|
|
for arrow in arrows[1:3]:
|
|
arrow.rotate_in_place(np.pi)
|
|
arrow.set_color(RED)
|
|
arrows.set_color_by_gradient(BLUE, YELLOW)
|
|
|
|
self.add(chart)
|
|
self.play(*map(MoveToTarget, it.chain(bars, old_bars)))
|
|
self.play(
|
|
chart.change_bar_values, alt_values,
|
|
*map(ShowCreation, arrows)
|
|
)
|
|
self.wait(2)
|
|
|
|
self.bar_chart = chart
|
|
self.old_bars = old_bars
|
|
|
|
def show_emotion(self):
|
|
randy = self.randy
|
|
|
|
self.play(FadeIn(randy))
|
|
self.play(randy.change, "sad")
|
|
self.play(Blink(randy))
|
|
|
|
def imagine_score_0(self):
|
|
prob_rect = SurroundingRectangle(self.title[-1])
|
|
bar_rect = SurroundingRectangle(VGroup(
|
|
self.bar_chart.bars[0], self.old_bars[0],
|
|
self.bar_chart.bar_labels[0],
|
|
))
|
|
|
|
self.play(ShowCreation(prob_rect))
|
|
self.wait()
|
|
self.play(ReplacementTransform(
|
|
prob_rect, bar_rect
|
|
))
|
|
self.wait()
|
|
self.play(FadeOut(bar_rect))
|
|
|
|
def get_angry(self):
|
|
randy = self.randy
|
|
|
|
self.play(randy.change, "angry")
|
|
self.wait(2)
|
|
self.play(PiCreatureSays(
|
|
randy, "It's not representative!",
|
|
target_mode = "pleading",
|
|
bubble_kwargs = {"fill_opacity" : 1}
|
|
))
|
|
self.wait(2)
|
|
|
|
#####
|
|
|
|
def create_pi_creature(self):
|
|
self.randy = Randolph()
|
|
self.randy.to_corner(DOWN+LEFT)
|
|
return self.randy
|
|
|
|
class TeacherAssessingLiklihoodOfZero(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.add_title()
|
|
self.fade_other_students()
|
|
self.show_independence_probability()
|
|
self.teacher_reacts()
|
|
|
|
def add_title(self):
|
|
title = TexMobject("P(", "\\text{Correct}", ")", "=", "0.65")
|
|
title.to_edge(UP)
|
|
title.set_color_by_tex("Correct", GREEN)
|
|
q_mark = TexMobject("?")
|
|
q_mark.next_to(title[-2], UP, SMALL_BUFF)
|
|
title.add(q_mark)
|
|
|
|
self.add(title)
|
|
self.title = title
|
|
|
|
def fade_other_students(self):
|
|
for student in self.students[0::2]:
|
|
student.fade(0.7)
|
|
self.pi_creatures.remove(student)
|
|
|
|
def show_independence_probability(self):
|
|
prob = get_probability_of_slot_group(3*[False])
|
|
rhs = TexMobject("=", "(0.35)", "^3", "\\approx 4.3\\%")
|
|
rhs.set_color_by_tex("0.35", RED)
|
|
rhs.next_to(prob, RIGHT)
|
|
prob.add(rhs)
|
|
prob.next_to(self.teacher, UP+LEFT)
|
|
words = TextMobject("Assuming independence")
|
|
words.next_to(prob, UP)
|
|
|
|
self.play(
|
|
self.teacher.change, "raise_right_hand",
|
|
FadeIn(words),
|
|
Write(prob)
|
|
)
|
|
self.wait()
|
|
|
|
self.ind_group = VGroup(prob, words)
|
|
|
|
def teacher_reacts(self):
|
|
ind_group = self.ind_group
|
|
box = SurroundingRectangle(ind_group)
|
|
box.set_stroke(WHITE, 0)
|
|
ind_group.add(box)
|
|
ind_group.generate_target()
|
|
ind_group.target.scale(0.7)
|
|
ind_group.target.to_corner(UP+RIGHT, MED_SMALL_BUFF)
|
|
ind_group.target[-1].set_stroke(WHITE, 2)
|
|
|
|
randy = self.students[1]
|
|
|
|
self.teacher_says(
|
|
"Highly unlikely",
|
|
target_mode = "sassy",
|
|
added_anims = [MoveToTarget(ind_group)],
|
|
run_time = 2,
|
|
)
|
|
self.play(randy.change, "sad")
|
|
self.wait(2)
|
|
self.play(
|
|
RemovePiCreatureBubble(
|
|
self.teacher, target_mode = "guilty",
|
|
),
|
|
PiCreatureSays(randy, "Wait!", target_mode = "surprised"),
|
|
run_time = 1
|
|
)
|
|
self.wait(1)
|
|
|
|
class CorrelationsWith35Percent(ThousandPossibleQuizzes):
|
|
def construct(self):
|
|
self.add_top_calculation()
|
|
self.show_first_split()
|
|
self.show_second_split()
|
|
self.show_third_split()
|
|
self.comment_on_final_size()
|
|
|
|
def add_top_calculation(self):
|
|
equation = VGroup(
|
|
get_probability_of_slot_group(3*[False]),
|
|
TexMobject("="),
|
|
get_probability_of_slot_group([False]),
|
|
get_probability_of_slot_group(2*[False], [False]),
|
|
get_probability_of_slot_group(3*[False], 2*[False]),
|
|
)
|
|
equation.arrange_submobjects(RIGHT, buff = SMALL_BUFF)
|
|
equation.to_edge(UP)
|
|
|
|
self.add(equation)
|
|
self.equation = equation
|
|
|
|
def show_first_split(self):
|
|
quizzes = self.get_thousand_quizzes()
|
|
n = int(0.65*len(quizzes))
|
|
top_part = VGroup(*quizzes[:n])
|
|
bottom_part = VGroup(*quizzes[n:])
|
|
parts = [top_part, bottom_part]
|
|
for part, color in zip(parts, [GREEN, RED]):
|
|
part.generate_target()
|
|
for quiz in part.target:
|
|
quiz[0].set_color(color)
|
|
top_part.target.shift(UP)
|
|
brace = Brace(bottom_part, LEFT)
|
|
prop = TexMobject("0.35")
|
|
prop.next_to(brace, LEFT)
|
|
|
|
term = self.equation[2]
|
|
term_brace = Brace(term, DOWN)
|
|
|
|
self.add(quizzes)
|
|
self.wait()
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
FadeIn(prop),
|
|
*map(MoveToTarget, parts)
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
top_part.fade, 0.8,
|
|
Transform(brace, term_brace),
|
|
prop.next_to, term_brace, DOWN,
|
|
)
|
|
self.wait()
|
|
|
|
self.quizzes = bottom_part
|
|
self.quizzes.sort_submobjects(lambda p : p[0])
|
|
|
|
def show_second_split(self):
|
|
n = int(0.45*len(self.quizzes))
|
|
left_part = VGroup(*self.quizzes[:n])
|
|
right_part = VGroup(*self.quizzes[n:])
|
|
parts = [left_part, right_part]
|
|
for part, color in zip(parts, [GREEN, RED_E]):
|
|
part.generate_target()
|
|
for quiz in part.target:
|
|
quiz[1].set_color(color)
|
|
left_part.target.shift(LEFT)
|
|
brace = Brace(right_part, UP)
|
|
prop = TexMobject(">0.35")
|
|
prop.next_to(brace, UP)
|
|
|
|
term = self.equation[3]
|
|
term_brace = Brace(term, DOWN)
|
|
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
FadeIn(prop),
|
|
*map(MoveToTarget, parts)
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
Transform(brace, term_brace),
|
|
prop.next_to, term_brace, DOWN
|
|
)
|
|
self.play(left_part.fade, 0.8)
|
|
|
|
self.quizzes = right_part
|
|
self.quizzes.sort_submobjects(lambda p : -p[1])
|
|
|
|
def show_third_split(self):
|
|
quizzes = self.quizzes
|
|
n = int(0.22*len(quizzes))
|
|
top_part = VGroup(*quizzes[:n])
|
|
bottom_part = VGroup(*quizzes[n:])
|
|
parts = [top_part, bottom_part]
|
|
for part, color in zip(parts, [GREEN, RED_B]):
|
|
part.generate_target()
|
|
for quiz in part.target:
|
|
quiz[2].set_color(color)
|
|
top_part.target.shift(0.5*UP)
|
|
brace = Brace(bottom_part, LEFT)
|
|
prop = TexMobject("\\gg 0.35")
|
|
prop.next_to(brace, LEFT)
|
|
|
|
term = self.equation[4]
|
|
term_brace = Brace(term, DOWN)
|
|
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
FadeIn(prop),
|
|
*map(MoveToTarget, parts)
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
Transform(brace, term_brace),
|
|
prop.next_to, term_brace, DOWN,
|
|
)
|
|
self.play(top_part.fade, 0.8)
|
|
self.wait()
|
|
|
|
self.quizzes = bottom_part
|
|
|
|
def comment_on_final_size(self):
|
|
rect = SurroundingRectangle(self.quizzes)
|
|
words = TextMobject(
|
|
"Much more than ", "$(0.35)^3 \\approx 4.3\\%$"
|
|
)
|
|
words.next_to(rect, LEFT)
|
|
|
|
self.play(
|
|
ShowCreation(rect),
|
|
FadeIn(words)
|
|
)
|
|
self.wait()
|
|
|
|
class WeighingIndependenceAssumption(PiCreatureScene):
|
|
def construct(self):
|
|
randy = self.randy
|
|
|
|
title = TextMobject("Independence")
|
|
title.scale(1.5)
|
|
title.to_edge(UP)
|
|
self.add(title)
|
|
formula = TexMobject(
|
|
"P(", "A", "B", ")", "="
|
|
"P(", "A", ")", "P(", "B", ")"
|
|
)
|
|
formula.set_color_by_tex("A", BLUE)
|
|
formula.set_color_by_tex("B", GREEN)
|
|
|
|
clean = TextMobject("Clean")
|
|
clean.next_to(formula, UP)
|
|
VGroup(clean, formula).next_to(randy, UP+LEFT)
|
|
clean.save_state()
|
|
clean.shift(2*(DOWN+RIGHT))
|
|
clean.set_fill(opacity = 0)
|
|
|
|
self.play(
|
|
randy.change, "raise_left_hand", clean,
|
|
clean.restore
|
|
)
|
|
self.play(Write(formula))
|
|
self.play(
|
|
randy.change, "raise_right_hand",
|
|
randy.look, UP+RIGHT,
|
|
)
|
|
self.wait(2)
|
|
|
|
####
|
|
|
|
def create_pi_creature(self):
|
|
self.randy = Randolph().to_edge(DOWN)
|
|
return self.randy
|
|
|
|
class NameBinomial(Scene):
|
|
CONFIG = {
|
|
"flip_indices" : [0, 2, 4, 5, 6, 7],
|
|
}
|
|
def construct(self):
|
|
self.name_distribution()
|
|
self.add_quiz_questions()
|
|
self.change_to_gender()
|
|
self.change_bar_chart_for_gender_example()
|
|
self.point_out_example_input()
|
|
self.write_probability_of_girl()
|
|
self.think_through_probabilities()
|
|
|
|
def name_distribution(self):
|
|
ns = [3, 10]
|
|
p = 0.65
|
|
charts = VGroup()
|
|
for n in ns:
|
|
dist = get_binomial_distribution(n, p)
|
|
values = map(dist, range(n+1))
|
|
chart = BarChart(values, bar_names = range(n+1))
|
|
chart.to_edge(LEFT)
|
|
charts.add(chart)
|
|
|
|
probability = TexMobject(
|
|
"P(", "\\checkmark", ")", "=", str(p)
|
|
)
|
|
probability.set_color_by_tex("checkmark", GREEN)
|
|
probability.move_to(charts, UP)
|
|
|
|
title = TextMobject("``Binomial distribution''")
|
|
title.next_to(charts, UP)
|
|
title.to_edge(UP)
|
|
formula = TexMobject(
|
|
"P(X=", "k", ")=",
|
|
"{n \\choose k}",
|
|
"p", "^k",
|
|
"(1-", "p", ")", "^{n-", "k}",
|
|
arg_separator = ""
|
|
)
|
|
formula.set_color_by_tex("p", YELLOW)
|
|
formula.set_color_by_tex("k", GREEN)
|
|
choose_part = formula.get_part_by_tex("choose")
|
|
choose_part.set_color(WHITE)
|
|
choose_part[-2].set_color(GREEN)
|
|
formula.to_corner(UP+RIGHT)
|
|
|
|
self.add(charts[0], probability)
|
|
self.wait()
|
|
self.play(Write(title))
|
|
self.wait()
|
|
self.play(ReplacementTransform(*charts))
|
|
self.play(Write(formula))
|
|
self.wait()
|
|
self.play(
|
|
formula.scale, 0.7,
|
|
formula.next_to, charts, DOWN,
|
|
)
|
|
|
|
self.chart = charts[1]
|
|
self.probability = probability
|
|
self.title = title
|
|
self.formula = formula
|
|
|
|
def add_quiz_questions(self):
|
|
n = 10
|
|
checkmarks = VGroup(*[
|
|
TexMobject("\\checkmark").set_color(GREEN)
|
|
for x in range(n)
|
|
])
|
|
checkmarks.arrange_submobjects(DOWN, buff = 0.3)
|
|
crosses = VGroup()
|
|
arrows = VGroup()
|
|
for checkmark in checkmarks:
|
|
cross = TexMobject("\\times")
|
|
cross.set_color(RED)
|
|
cross.next_to(checkmark, RIGHT, LARGE_BUFF)
|
|
crosses.add(cross)
|
|
arrow = Arrow(
|
|
checkmark, cross,
|
|
tip_length = 0.15,
|
|
color = WHITE
|
|
)
|
|
arrows.add(arrow)
|
|
full_group = VGroup(checkmarks, crosses, arrows)
|
|
full_group.center().to_corner(UP + RIGHT, buff = MED_LARGE_BUFF)
|
|
flip_indices = self.flip_indices
|
|
flipped_arrows, faded_crosses, full_checks = [
|
|
VGroup(*[group[i] for i in flip_indices])
|
|
for group in arrows, crosses, checkmarks
|
|
]
|
|
faded_checkmarks = VGroup(*filter(
|
|
lambda m : m not in full_checks,
|
|
checkmarks
|
|
))
|
|
|
|
self.play(*[
|
|
LaggedStart(
|
|
Write, mob,
|
|
run_time = 3,
|
|
lag_ratio = 0.3
|
|
)
|
|
for mob in full_group
|
|
])
|
|
self.wait()
|
|
self.play(
|
|
LaggedStart(
|
|
Rotate, flipped_arrows,
|
|
angle = np.pi,
|
|
in_place = True,
|
|
run_time = 2,
|
|
lag_ratio = 0.5
|
|
),
|
|
faded_crosses.set_fill, None, 0.5,
|
|
faded_checkmarks.set_fill, None, 0.5,
|
|
)
|
|
self.wait()
|
|
|
|
self.checkmarks = checkmarks
|
|
self.crosses = crosses
|
|
self.arrows = arrows
|
|
|
|
def change_to_gender(self):
|
|
flip_indices = self.flip_indices
|
|
male = self.get_male()
|
|
female = self.get_female()
|
|
|
|
girls, boys = [
|
|
VGroup(*[
|
|
template.copy().move_to(mob)
|
|
for mob in group
|
|
])
|
|
for template, group in [
|
|
(female, self.checkmarks), (male, self.crosses)
|
|
]
|
|
]
|
|
for i in range(len(boys)):
|
|
mob = boys[i] if i in flip_indices else girls[i]
|
|
mob.set_fill(opacity = 0.5)
|
|
|
|
brace = Brace(girls, LEFT)
|
|
words = brace.get_text("$n$ children")
|
|
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
FadeIn(words)
|
|
)
|
|
for m1, m2 in (self.crosses, boys), (self.checkmarks, girls):
|
|
self.play(ReplacementTransform(
|
|
m1, m2,
|
|
submobject_mode = "lagged_start",
|
|
run_time = 3
|
|
))
|
|
self.wait()
|
|
|
|
self.boys = boys
|
|
self.girls = girls
|
|
self.children_brace = brace
|
|
self.n_children_words = words
|
|
|
|
def change_bar_chart_for_gender_example(self):
|
|
checkmark = self.probability.get_part_by_tex("checkmark")
|
|
p_mob = self.probability[-1]
|
|
|
|
female = self.get_female()
|
|
female.move_to(checkmark)
|
|
new_p_mob = TexMobject("0.49")
|
|
new_p_mob.move_to(p_mob, LEFT)
|
|
|
|
dist = get_binomial_distribution(10, 0.49)
|
|
values = map(dist, range(11))
|
|
|
|
self.play(
|
|
Transform(checkmark, female),
|
|
Transform(p_mob, new_p_mob),
|
|
)
|
|
self.play(self.chart.change_bar_values, values)
|
|
self.wait()
|
|
|
|
def point_out_example_input(self):
|
|
boy_girl_groups = VGroup(*[
|
|
VGroup(boy, girl)
|
|
for boy, girl in zip(self.boys, self.girls)
|
|
])
|
|
girl_rects = VGroup(*[
|
|
SurroundingRectangle(
|
|
boy_girl_groups[i],
|
|
color = MAROON_B,
|
|
buff = SMALL_BUFF,
|
|
)
|
|
for i in sorted(self.flip_indices)
|
|
])
|
|
|
|
chart = self.chart
|
|
n_girls = len(girl_rects)
|
|
chart_rect = SurroundingRectangle(
|
|
VGroup(chart.bars[n_girls], chart.bar_labels[n_girls]),
|
|
buff = SMALL_BUFF
|
|
)
|
|
|
|
prob = TexMobject(
|
|
"P(", "\\# \\text{Girls}", "=", "6", ")"
|
|
)
|
|
prob.set_color_by_tex("Girls", MAROON_B)
|
|
arrow = Arrow(UP, ORIGIN, tip_length = 0.15)
|
|
arrow.set_color(MAROON_B)
|
|
arrow.next_to(prob, DOWN)
|
|
prob.add(arrow)
|
|
prob.next_to(chart_rect, UP)
|
|
girls = VGroup(*[self.girls[i] for i in self.flip_indices])
|
|
|
|
|
|
self.play(ShowCreation(chart_rect))
|
|
self.play(LaggedStart(
|
|
ShowCreation, girl_rects,
|
|
run_time = 2,
|
|
lag_ratio = 0.5,
|
|
))
|
|
self.wait()
|
|
|
|
self.play(Write(prob))
|
|
self.play(LaggedStart(
|
|
Indicate, girls,
|
|
run_time = 3,
|
|
lag_ratio = 0.3,
|
|
rate_func = there_and_back
|
|
))
|
|
self.play(FadeOut(prob))
|
|
self.wait()
|
|
|
|
self.chart_rect = chart_rect
|
|
self.girl_rects = girl_rects
|
|
|
|
def write_probability_of_girl(self):
|
|
probability = self.probability
|
|
probability_copies = VGroup(*[
|
|
probability.copy().scale(0.7).next_to(
|
|
girl, LEFT, MED_LARGE_BUFF
|
|
)
|
|
for girl in self.girls
|
|
])
|
|
|
|
self.play(FocusOn(probability))
|
|
self.play(Indicate(probability[-1]))
|
|
self.wait()
|
|
self.play(
|
|
ReplacementTransform(
|
|
VGroup(probability.copy()), probability_copies
|
|
),
|
|
FadeOut(self.children_brace),
|
|
FadeOut(self.n_children_words),
|
|
)
|
|
self.wait()
|
|
|
|
self.probability_copies = probability_copies
|
|
|
|
def think_through_probabilities(self):
|
|
randy = Randolph().scale(0.5)
|
|
randy.next_to(self.probability_copies, LEFT, LARGE_BUFF)
|
|
|
|
self.play(FadeIn(randy))
|
|
self.play(randy.change, "pondering")
|
|
self.play(Blink(randy))
|
|
self.wait()
|
|
|
|
##
|
|
|
|
def get_male(self):
|
|
return TexMobject("\\male").scale(1.3).set_color(BLUE)
|
|
|
|
def get_female(self):
|
|
return TexMobject("\\female").scale(1.3).set_color(MAROON_B)
|
|
|
|
class CycleThroughPatterns(NameBinomial):
|
|
CONFIG = {
|
|
"n_patterns_shown" : 100,
|
|
"pattern_scale_value" : 2.7,
|
|
"n" : 10,
|
|
"k" : 6,
|
|
}
|
|
def construct(self):
|
|
n = self.n
|
|
k = self.k
|
|
question = TextMobject(
|
|
"How many patterns have \\\\ %d "%k,
|
|
"$\\female$",
|
|
" and %d "%(n-k),
|
|
"$\\male$",
|
|
"?",
|
|
arg_separator = ""
|
|
)
|
|
question.set_color_by_tex("male", BLUE)
|
|
question.set_color_by_tex("female", MAROON_B)
|
|
question.scale_to_fit_width(FRAME_WIDTH - 1)
|
|
question.to_edge(UP, buff = LARGE_BUFF)
|
|
self.add(question)
|
|
|
|
all_combinations = list(it.combinations(range(n), k))
|
|
shown_combinations = all_combinations[:self.n_patterns_shown]
|
|
patterns = VGroup(*[
|
|
self.get_pattern(indicies)
|
|
for indicies in shown_combinations
|
|
])
|
|
patterns.to_edge(DOWN, buff = LARGE_BUFF)
|
|
pattern = patterns[0]
|
|
self.add(pattern)
|
|
for new_pattern in ProgressDisplay(patterns[1:]):
|
|
self.play(*[
|
|
Transform(
|
|
getattr(pattern, attr),
|
|
getattr(new_pattern, attr),
|
|
path_arc = np.pi
|
|
)
|
|
for attr in "boys", "girls"
|
|
])
|
|
|
|
####
|
|
|
|
def get_pattern(self, indices):
|
|
pattern = VGroup()
|
|
pattern.boys = VGroup()
|
|
pattern.girls = VGroup()
|
|
for i in range(self.n):
|
|
if i in indices:
|
|
mob = self.get_female()
|
|
pattern.girls.add(mob)
|
|
else:
|
|
mob = self.get_male()
|
|
pattern.boys.add(mob)
|
|
mob.shift(i*MED_LARGE_BUFF*RIGHT)
|
|
pattern.add(mob)
|
|
pattern.scale(self.pattern_scale_value)
|
|
pattern.to_edge(LEFT)
|
|
return pattern
|
|
|
|
class Compute6of10GirlsProbability(CycleThroughPatterns):
|
|
def construct(self):
|
|
self.show_combinations()
|
|
self.write_n_choose_k()
|
|
|
|
def show_combinations(self):
|
|
pattern_rect = ScreenRectangle(height = 4)
|
|
pattern_rect.center()
|
|
pattern_rect.to_edge(UP, buff = MED_SMALL_BUFF)
|
|
|
|
self.add(pattern_rect)
|
|
self.wait(5)
|
|
|
|
self.pattern_rect = pattern_rect
|
|
|
|
def write_n_choose_k(self):
|
|
brace = Brace(self.pattern_rect, DOWN)
|
|
ten_choose_six = brace.get_tex("{10 \\choose 6}")
|
|
see_chapter_one = TextMobject("(See chapter 1)")
|
|
see_chapter_one.next_to(ten_choose_six, DOWN)
|
|
see_chapter_one.set_color(GREEN)
|
|
computation = TexMobject(
|
|
"=\\frac{%s}{%s}"%(
|
|
"\\cdot ".join(map(str, range(10, 4, -1))),
|
|
"\\cdot ".join(map(str, range(1, 7))),
|
|
)
|
|
)
|
|
computation.move_to(ten_choose_six, UP)
|
|
rhs = TexMobject("=", "210")
|
|
rhs.next_to(computation, RIGHT)
|
|
|
|
self.play(
|
|
FadeIn(see_chapter_one),
|
|
GrowFromCenter(brace)
|
|
)
|
|
self.play(Write(ten_choose_six))
|
|
self.wait(2)
|
|
self.play(
|
|
ten_choose_six.next_to, computation.copy(), LEFT,
|
|
Write(VGroup(computation, rhs))
|
|
)
|
|
self.wait()
|
|
|
|
self.ten_choose_six = ten_choose_six
|
|
self.rhs = rhs
|
|
|
|
class ProbabilityOfAGivenBoyGirlPattern(CycleThroughPatterns):
|
|
def construct(self):
|
|
self.write_total_count()
|
|
self.write_example_probability()
|
|
self.write_total_probability()
|
|
|
|
def write_total_count(self):
|
|
count = TextMobject(
|
|
"${10 \\choose 6}$", " $= 210$",
|
|
"total patterns."
|
|
)
|
|
count.to_edge(UP)
|
|
self.add(count)
|
|
|
|
self.count = count
|
|
|
|
def write_example_probability(self):
|
|
prob = TexMobject("P\\big(", "O "*15, "\\big)", "=")
|
|
indices = [1, 2, 4, 6, 8, 9]
|
|
pattern = self.get_pattern(indices)
|
|
pattern.replace(prob[1], dim_to_match = 0)
|
|
prob.submobjects[1] = pattern
|
|
prob.next_to(self.count, DOWN, LARGE_BUFF)
|
|
|
|
gp = TexMobject("P(\\female)")
|
|
gp[2].set_color(MAROON_B)
|
|
bp = TexMobject("P(\\male)")
|
|
bp[2].set_color(BLUE)
|
|
gp_num = TexMobject("(0.49)").set_color(MAROON_B)
|
|
bp_num = TexMobject("(0.51)").set_color(BLUE)
|
|
gp_nums = VGroup()
|
|
bp_nums = VGroup()
|
|
factored = VGroup()
|
|
factored_in_nums = VGroup()
|
|
for i in range(10):
|
|
if i in indices:
|
|
num_mob = gp_num.copy()
|
|
gp_nums.add(num_mob)
|
|
p_mob = gp.copy()
|
|
else:
|
|
num_mob = bp_num.copy()
|
|
bp_nums.add(num_mob)
|
|
p_mob = bp.copy()
|
|
factored_in_nums.add(num_mob)
|
|
factored.add(p_mob)
|
|
for group in factored, factored_in_nums:
|
|
group.arrange_submobjects(RIGHT, buff = SMALL_BUFF)
|
|
group.next_to(prob, DOWN, MED_LARGE_BUFF)
|
|
gp_nums.save_state()
|
|
bp_nums.save_state()
|
|
|
|
final_probability = TexMobject(
|
|
"(0.49)^6", "(0.51)^4"
|
|
)
|
|
final_probability.set_color_by_tex("0.49", MAROON_B)
|
|
final_probability.set_color_by_tex("0.51", BLUE)
|
|
final_probability.next_to(factored_in_nums, DOWN, LARGE_BUFF)
|
|
|
|
self.play(FadeIn(prob))
|
|
self.wait()
|
|
self.play(ReplacementTransform(
|
|
pattern.copy(), factored,
|
|
run_time = 1.5,
|
|
))
|
|
self.wait(2)
|
|
self.play(ReplacementTransform(
|
|
factored, factored_in_nums,
|
|
run_time = 2,
|
|
submobject_mode = "lagged_start"
|
|
))
|
|
self.wait(2)
|
|
for group, tex in (gp_nums, "0.49"), (bp_nums, "0.51"):
|
|
part = final_probability.get_part_by_tex(tex)
|
|
self.play(group.shift, MED_LARGE_BUFF*DOWN)
|
|
self.play(
|
|
ReplacementTransform(
|
|
group.copy(), VGroup(VGroup(*part[:-1]))
|
|
),
|
|
Write(part[-1])
|
|
)
|
|
self.wait()
|
|
self.play(group.restore)
|
|
self.wait()
|
|
|
|
self.final_probability = final_probability
|
|
|
|
def write_total_probability(self):
|
|
ten_choose_six = self.count[0].copy()
|
|
ten_choose_six.generate_target()
|
|
ten_choose_six.target.move_to(self.final_probability)
|
|
p_tex = TexMobject("P(", "\\text{6 Girls}", ")", "=")
|
|
p_tex.set_color_by_tex("Girls", MAROON_B)
|
|
p_tex.next_to(ten_choose_six.target, LEFT)
|
|
|
|
self.play(
|
|
Write(p_tex, run_time = 2),
|
|
self.final_probability.next_to,
|
|
ten_choose_six.target, RIGHT
|
|
)
|
|
self.play(MoveToTarget(ten_choose_six))
|
|
self.wait()
|
|
|
|
class CycleThroughPatternsForThree(CycleThroughPatterns):
|
|
CONFIG = {
|
|
"k" : 3,
|
|
"n_patterns_shown" : 20,
|
|
}
|
|
|
|
class GeneralBinomialDistributionValues(Scene):
|
|
CONFIG = {
|
|
"n" : 10,
|
|
"alt_n" : 8,
|
|
"p" : 0.49,
|
|
}
|
|
def construct(self):
|
|
self.add_chart()
|
|
self.show_a_few_values()
|
|
self.compare_to_pascal_row()
|
|
self.mention_center_concentration()
|
|
self.generalize()
|
|
self.play_with_p_value()
|
|
|
|
def add_chart(self):
|
|
dist = get_binomial_distribution(self.n, self.p)
|
|
values = map(dist, range(self.n+1))
|
|
chart = BarChart(
|
|
values,
|
|
bar_names = range(self.n+1)
|
|
)
|
|
chart.to_edge(LEFT)
|
|
|
|
full_probability = self.get_probability_expression(
|
|
"10", "k", "(0.49)", "(0.51)"
|
|
)
|
|
full_probability.next_to(chart, UP, aligned_edge = LEFT)
|
|
|
|
self.add(chart)
|
|
|
|
self.chart = chart
|
|
self.full_probability = full_probability
|
|
|
|
def show_a_few_values(self):
|
|
chart = self.chart
|
|
probabilities = VGroup()
|
|
for i, bar in enumerate(chart.bars):
|
|
prob = self.get_probability_expression(
|
|
"10", str(i), "(0.49)", "(0.51)",
|
|
full = False
|
|
)
|
|
arrow = Arrow(
|
|
UP, DOWN,
|
|
color = WHITE,
|
|
tip_length = 0.15
|
|
)
|
|
arrow.next_to(bar, UP, SMALL_BUFF)
|
|
prob.next_to(arrow, UP, SMALL_BUFF)
|
|
##
|
|
prob.shift(LEFT)
|
|
prob.shift_onto_screen()
|
|
prob.shift(RIGHT)
|
|
##
|
|
prob.add(arrow)
|
|
probabilities.add(prob)
|
|
shown_prob = probabilities[6].copy()
|
|
|
|
self.play(FadeIn(shown_prob))
|
|
self.wait()
|
|
self.play(LaggedStart(
|
|
FadeIn, self.full_probability,
|
|
run_time = 4,
|
|
lag_ratio = 0.5,
|
|
))
|
|
self.wait()
|
|
last_k = 6
|
|
for k in 3, 8, 5, 9, 6:
|
|
self.play(Transform(
|
|
shown_prob, probabilities[k],
|
|
path_arc = -np.pi/6 if k > last_k else np.pi/6
|
|
))
|
|
self.wait(2)
|
|
last_k = k
|
|
|
|
self.shown_prob = shown_prob
|
|
|
|
def compare_to_pascal_row(self):
|
|
triangle = PascalsTriangle(nrows = 11)
|
|
triangle.scale_to_fit_width(6)
|
|
triangle.to_corner(UP+RIGHT)
|
|
last_row = VGroup(*[
|
|
triangle.coords_to_mobs[10][k]
|
|
for k in range(11)
|
|
])
|
|
ten_choose_ks = VGroup()
|
|
for k, mob in enumerate(last_row):
|
|
ten_choose_k = TexMobject("10 \\choose %s"%k)
|
|
ten_choose_k.scale(0.5)
|
|
ten_choose_k.stretch(0.8, 0)
|
|
ten_choose_k.next_to(mob, DOWN)
|
|
ten_choose_ks.add(ten_choose_k)
|
|
ten_choose_ks.set_color_by_gradient(BLUE, YELLOW)
|
|
|
|
self.play(
|
|
LaggedStart(FadeIn, triangle),
|
|
FadeOut(self.shown_prob)
|
|
)
|
|
self.play(
|
|
last_row.set_color_by_gradient, BLUE, YELLOW,
|
|
Write(ten_choose_ks, run_time = 2)
|
|
)
|
|
self.wait()
|
|
self.play(ApplyWave(self.chart.bars, direction = UP))
|
|
self.play(FocusOn(last_row))
|
|
self.play(LaggedStart(
|
|
ApplyMethod, last_row,
|
|
lambda m : (m.scale_in_place, 1.2),
|
|
rate_func = there_and_back,
|
|
))
|
|
self.wait()
|
|
|
|
self.pascals_triangle = triangle
|
|
self.ten_choose_ks = ten_choose_ks
|
|
|
|
def mention_center_concentration(self):
|
|
bars = self.chart.bars
|
|
bars.generate_target()
|
|
bars.save_state()
|
|
bars.target.arrange_submobjects(UP, buff = 0)
|
|
bars.target.stretch_to_fit_height(self.chart.height)
|
|
bars.target.move_to(
|
|
self.chart.x_axis.point_from_proportion(0.05),
|
|
DOWN
|
|
)
|
|
brace = Brace(VGroup(*bars.target[4:7]), RIGHT)
|
|
words = brace.get_text("Most probability \\\\ in middle values")
|
|
|
|
self.play(MoveToTarget(bars))
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
FadeIn(words)
|
|
)
|
|
self.wait(2)
|
|
self.play(
|
|
bars.restore,
|
|
*map(FadeOut, [
|
|
brace, words,
|
|
self.pascals_triangle,
|
|
self.ten_choose_ks
|
|
])
|
|
)
|
|
|
|
def generalize(self):
|
|
alt_n = self.alt_n
|
|
dist = get_binomial_distribution(alt_n, self.p)
|
|
values = map(dist, range(alt_n + 1))
|
|
alt_chart = BarChart(
|
|
values, bar_names = range(alt_n + 1)
|
|
)
|
|
alt_chart.move_to(self.chart)
|
|
|
|
alt_probs = [
|
|
self.get_probability_expression("n", "k", "(0.49)", "(0.51)"),
|
|
self.get_probability_expression("n", "k", "p", "(1-p)"),
|
|
]
|
|
for prob in alt_probs:
|
|
prob.move_to(self.full_probability)
|
|
|
|
self.play(FocusOn(
|
|
self.full_probability.get_part_by_tex("choose")
|
|
))
|
|
self.play(
|
|
ReplacementTransform(self.chart, alt_chart),
|
|
Transform(self.full_probability, alt_probs[0])
|
|
)
|
|
self.chart = alt_chart
|
|
self.wait(2)
|
|
self.play(Transform(self.full_probability, alt_probs[1]))
|
|
self.wait()
|
|
|
|
def play_with_p_value(self):
|
|
p = self.p
|
|
interval = UnitInterval(color = WHITE)
|
|
interval.scale_to_fit_width(5)
|
|
interval.next_to(self.full_probability, DOWN, LARGE_BUFF)
|
|
interval.add_numbers(0, 0.5, 1)
|
|
triangle = RegularPolygon(
|
|
n=3, start_angle = -np.pi/2,
|
|
fill_color = MAROON_B,
|
|
fill_opacity = 1,
|
|
stroke_width = 0,
|
|
)
|
|
triangle.scale_to_fit_height(0.25)
|
|
triangle.move_to(interval.number_to_point(p), DOWN)
|
|
p_mob = TexMobject("p")
|
|
p_mob.set_color(MAROON_B)
|
|
p_mob.next_to(triangle, UP, SMALL_BUFF)
|
|
triangle.add(p_mob)
|
|
|
|
new_p_values = [0.8, 0.4, 0.2, 0.9, 0.97, 0.6]
|
|
|
|
self.play(
|
|
ShowCreation(interval),
|
|
Write(triangle, run_time = 1)
|
|
)
|
|
self.wait()
|
|
for new_p in new_p_values:
|
|
p = new_p
|
|
dist = get_binomial_distribution(self.alt_n, p)
|
|
values = map(dist, range(self.alt_n + 1))
|
|
self.play(
|
|
self.chart.change_bar_values, values,
|
|
triangle.move_to, interval.number_to_point(p), DOWN
|
|
)
|
|
self.wait()
|
|
|
|
#######
|
|
|
|
def get_probability_expression(
|
|
self, n = "n", k = "k", p = "p", q = "(1-p)",
|
|
full = True
|
|
):
|
|
args = []
|
|
if full:
|
|
args += ["P(", "\\# \\text{Girls}", "=", k, ")", "="]
|
|
args += [
|
|
"{%s \\choose %s}"%(n, k),
|
|
p, "^%s"%k,
|
|
q, "^{%s"%n, "-", "%s}"%k,
|
|
]
|
|
result = TexMobject(*args, arg_separator = "")
|
|
color_map = {
|
|
"Girls" : MAROON_B,
|
|
n : WHITE,
|
|
k : YELLOW,
|
|
p : MAROON_B,
|
|
q : BLUE,
|
|
}
|
|
result.set_color_by_tex_to_color_map(color_map)
|
|
choose_part = result.get_part_by_tex("choose")
|
|
choose_part.set_color(WHITE)
|
|
VGroup(*choose_part[1:1+len(n)]).set_color(color_map[n])
|
|
VGroup(*choose_part[-1-len(k):-1]).set_color(color_map[k])
|
|
return result
|
|
|
|
class PointOutSimplicityOfFormula(TeacherStudentsScene, GeneralBinomialDistributionValues):
|
|
def construct(self):
|
|
prob = self.get_probability_expression(full = False)
|
|
corner = self.teacher.get_corner(UP+LEFT)
|
|
prob.next_to(corner, UP, MED_LARGE_BUFF)
|
|
prob.save_state()
|
|
prob.move_to(corner)
|
|
prob.set_fill(opacity = 0)
|
|
|
|
self.play(
|
|
prob.restore,
|
|
self.teacher.change_mode, "raise_right_hand"
|
|
)
|
|
self.change_student_modes(
|
|
*["pondering"]*3,
|
|
look_at_arg = prob
|
|
)
|
|
self.wait()
|
|
self.student_says(
|
|
"Simpler than I feared",
|
|
target_mode = "hooray",
|
|
student_index = 0,
|
|
added_anims = [prob.to_corner, UP+RIGHT]
|
|
)
|
|
self.wait()
|
|
self.teacher_says("Due to \\\\ independence")
|
|
self.wait(2)
|
|
|
|
class CorrectForDependence(NameBinomial):
|
|
CONFIG = {
|
|
"flip_indices" : [3, 6, 8],
|
|
}
|
|
def setup(self):
|
|
self.force_skipping()
|
|
self.name_distribution()
|
|
self.add_quiz_questions()
|
|
self.revert_to_original_skipping_status()
|
|
|
|
def construct(self):
|
|
self.mention_dependence()
|
|
self.show_tendency_to_align()
|
|
self.adjust_chart()
|
|
|
|
def mention_dependence(self):
|
|
brace = Brace(self.checkmarks, LEFT)
|
|
words = brace.get_text("What if there's \\\\ correlation?")
|
|
formula = self.formula
|
|
cross = Cross(formula)
|
|
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
Write(words)
|
|
)
|
|
self.wait()
|
|
self.play(ShowCreation(cross))
|
|
self.wait()
|
|
|
|
def show_tendency_to_align(self):
|
|
checkmarks = self.checkmarks
|
|
arrows = self.arrows
|
|
crosses = self.crosses
|
|
groups = [
|
|
VGroup(*trip)
|
|
for trip in zip(checkmarks, arrows, crosses)
|
|
]
|
|
top_rect = SurroundingRectangle(groups[0])
|
|
top_rect.set_color(GREEN)
|
|
indices_to_follow = [1, 4, 5, 7]
|
|
|
|
self.play(ShowCreation(top_rect))
|
|
self.play(*self.get_arrow_flip_anims([0]))
|
|
self.wait()
|
|
self.play(*self.get_arrow_flip_anims(indices_to_follow))
|
|
self.play(FocusOn(self.chart.bars))
|
|
|
|
def adjust_chart(self):
|
|
chart = self.chart
|
|
bars = chart.bars
|
|
old_bars = bars.copy()
|
|
old_bars.generate_target()
|
|
bars.generate_target()
|
|
for group, vect in (old_bars, LEFT), (bars, RIGHT):
|
|
for bar in group.target:
|
|
side = bar.get_edge_center(vect)
|
|
bar.stretch(0.5, 0)
|
|
bar.move_to(side, vect)
|
|
for bar in old_bars.target:
|
|
bar.set_color(average_color(RED_E, BLACK))
|
|
|
|
dist = get_binomial_distribution(10, 0.65)
|
|
values = np.array(map(dist, range(11)))
|
|
alt_values = values + 0.1
|
|
alt_values[0] -= 0.06
|
|
alt_values[1] -= 0.03
|
|
alt_values /= sum(alt_values)
|
|
arrows = VGroup()
|
|
arrow_template = Arrow(
|
|
0.5*UP, ORIGIN, buff = 0,
|
|
tip_length = 0.15,
|
|
color = WHITE
|
|
)
|
|
for value, alt_value, bar in zip(values, alt_values, bars):
|
|
arrow = arrow_template.copy()
|
|
if value < alt_value:
|
|
arrow.rotate(np.pi, about_point = ORIGIN)
|
|
arrow.next_to(bar, UP)
|
|
arrows.add(arrow)
|
|
|
|
self.play(
|
|
MoveToTarget(old_bars),
|
|
MoveToTarget(bars),
|
|
)
|
|
self.wait()
|
|
self.play(*map(ShowCreation, arrows))
|
|
self.play(chart.change_bar_values, alt_values)
|
|
|
|
######
|
|
|
|
def get_arrow_flip_anims(self, indices):
|
|
checkmarks, arrows, crosses = movers = [
|
|
VGroup(*[
|
|
group[i]
|
|
for i in range(len(group))
|
|
if i in indices
|
|
])
|
|
for group in self.checkmarks, self.arrows, self.crosses
|
|
]
|
|
for arrow in arrows:
|
|
arrow.target = arrow.deepcopy()
|
|
arrow.target.rotate_in_place(np.pi)
|
|
for group in checkmarks, crosses:
|
|
for mob, arrow in zip(group, arrows):
|
|
mob.generate_target()
|
|
c = mob.get_center()
|
|
start, end = arrow.target.get_start_and_end()
|
|
to_end = np.linalg.norm(c - end)
|
|
to_start = np.linalg.norm(c - start)
|
|
if to_end < to_start:
|
|
mob.target.set_fill(opacity = 1)
|
|
else:
|
|
mob.target.set_fill(opacity = 0.5)
|
|
for checkmark in checkmarks:
|
|
checkmark.target.scale_in_place(1.2)
|
|
|
|
kwargs = {"path_arc" : np.pi}
|
|
if len(indices) > 1:
|
|
kwargs.update({"run_time" : 2})
|
|
return [
|
|
LaggedStart(
|
|
MoveToTarget, mover,
|
|
**kwargs
|
|
)
|
|
for mover in movers
|
|
]
|
|
|
|
class ButWhatsTheAnswer(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.student_says(
|
|
"But what's the \\\\ actual answer?",
|
|
target_mode = "confused"
|
|
)
|
|
self.change_student_modes(*["confused"]*3)
|
|
self.wait()
|
|
self.play(self.teacher.change, "pondering")
|
|
self.wait(3)
|
|
|
|
class PermuteQuizQuestions(Scene):
|
|
def construct(self):
|
|
quiz = get_quiz(
|
|
"Define ``Brachistochrone''",
|
|
"Define ``Tautochrone''",
|
|
"Define ``Cycloid''",
|
|
)
|
|
questions = [
|
|
VGroup(*q[2:])
|
|
for q in quiz.questions
|
|
]
|
|
colors = [BLUE, GREEN, RED]
|
|
for color, question in zip(colors, questions):
|
|
question.set_color(color)
|
|
quiz.scale(2)
|
|
|
|
self.add(quiz)
|
|
self.wait()
|
|
for m1, m2 in it.combinations(questions, 2):
|
|
self.play(
|
|
m1.move_to, m2, LEFT,
|
|
m2.move_to, m1, LEFT,
|
|
path_arc = np.pi
|
|
)
|
|
self.wait()
|
|
|
|
class AssumeOrderDoesntMatter(Scene):
|
|
def construct(self):
|
|
self.force_skipping()
|
|
|
|
self.add_title()
|
|
self.show_equality()
|
|
self.mention_correlation()
|
|
|
|
self.revert_to_original_skipping_status()
|
|
self.coming_soon()
|
|
|
|
def add_title(self):
|
|
title = TextMobject(
|
|
"Softer simplifying assumption: " +\
|
|
"Order doesn't matter"
|
|
)
|
|
title.to_edge(UP)
|
|
|
|
self.add(title)
|
|
self.title = title
|
|
|
|
def show_equality(self):
|
|
n = 3
|
|
prob_groups = VGroup(*[
|
|
VGroup(*map(
|
|
get_probability_of_slot_group,
|
|
filter(
|
|
lambda t : sum(t) == k,
|
|
it.product(*[[True, False]]*n)
|
|
)
|
|
))
|
|
for k in range(n+1)
|
|
])
|
|
for prob_group in prob_groups:
|
|
for prob in prob_group[:-1]:
|
|
equals = TexMobject("=")
|
|
equals.next_to(prob, RIGHT)
|
|
prob.add(equals)
|
|
prob_group.arrange_submobjects(RIGHT)
|
|
max_width = FRAME_WIDTH - 1
|
|
if prob_group.get_width() > max_width:
|
|
prob_group.scale_to_fit_width(max_width)
|
|
prob_groups.arrange_submobjects(DOWN, buff = 0.7)
|
|
prob_groups.next_to(self.title, DOWN, MED_LARGE_BUFF)
|
|
|
|
self.play(FadeIn(
|
|
prob_groups[1],
|
|
run_time = 2,
|
|
submobject_mode = "lagged_start"
|
|
))
|
|
self.wait(2)
|
|
self.play(FadeIn(
|
|
VGroup(prob_groups[0], *prob_groups[2:]),
|
|
run_time = 3,
|
|
submobject_mode = "lagged_start"
|
|
))
|
|
self.wait()
|
|
|
|
self.prob_groups = prob_groups
|
|
|
|
def mention_correlation(self):
|
|
assumption_group = VGroup(*self.get_top_level_mobjects())
|
|
question = TextMobject(
|
|
"But what is ", "``correlation''", "?",
|
|
arg_separator = ""
|
|
)
|
|
question.set_color(BLUE)
|
|
question.to_edge(UP)
|
|
bottom = question.get_bottom()
|
|
|
|
self.play(
|
|
Write(question),
|
|
assumption_group.next_to, bottom, DOWN, LARGE_BUFF
|
|
)
|
|
self.wait()
|
|
|
|
self.assumption_group = assumption_group
|
|
self.question = question
|
|
|
|
def coming_soon(self):
|
|
self.play(
|
|
LaggedStart(
|
|
ApplyMethod, self.assumption_group,
|
|
lambda m : (m.shift, FRAME_HEIGHT*DOWN),
|
|
remover = True,
|
|
),
|
|
ApplyMethod(
|
|
self.question.center,
|
|
rate_func = squish_rate_func(smooth, 0.5, 1),
|
|
run_time = 2
|
|
)
|
|
)
|
|
|
|
part = self.question.get_part_by_tex("correlation")
|
|
brace = Brace(part, UP)
|
|
words = brace.get_text("Coming soon!")
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
part.set_color, YELLOW
|
|
)
|
|
self.play(Write(words))
|
|
self.wait()
|
|
|
|
|
|
|
|
class FormulaCanBeRediscovered(PointOutSimplicityOfFormula):
|
|
def construct(self):
|
|
prob = self.get_probability_expression(full = False)
|
|
corner = self.teacher.get_corner(UP+LEFT)
|
|
prob.next_to(corner, UP, MED_LARGE_BUFF)
|
|
brace = Brace(prob, UP)
|
|
rediscover = brace.get_text("Rediscover")
|
|
|
|
self.play(
|
|
Write(prob),
|
|
self.teacher.change, "hesitant", prob
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
Write(rediscover, run_time = 1)
|
|
)
|
|
self.change_student_modes(*["happy"]*3)
|
|
self.wait(2)
|
|
|
|
class CompareTwoSituations(PiCreatureScene):
|
|
def construct(self):
|
|
randy = self.randy
|
|
top_left, top_right = screens = [
|
|
ScreenRectangle(height = 3).to_corner(vect)
|
|
for vect in UP+LEFT, UP+RIGHT
|
|
]
|
|
arrow = DoubleArrow(*screens, buff = SMALL_BUFF)
|
|
arrow.set_color(BLUE)
|
|
|
|
for screen, s in zip(screens, ["left", "right"]):
|
|
self.play(
|
|
randy.change, "raise_%s_hand"%s, screen,
|
|
ShowCreation(screen)
|
|
)
|
|
self.wait(3)
|
|
self.play(
|
|
randy.change, "pondering", arrow,
|
|
ShowCreation(arrow)
|
|
)
|
|
self.wait(2)
|
|
|
|
####
|
|
|
|
def create_pi_creature(self):
|
|
self.randy = Randolph().to_edge(DOWN)
|
|
return self.randy
|
|
|
|
class SkepticalOfDistributions(TeacherStudentsScene):
|
|
CONFIG = {
|
|
"chart_height" : 3,
|
|
}
|
|
def construct(self):
|
|
self.show_binomial()
|
|
self.show_alternate_distributions()
|
|
self.emphasize_underweighted_tails()
|
|
|
|
def show_binomial(self):
|
|
binomial = self.get_binomial()
|
|
binomial.next_to(self.teacher.get_corner(UP+LEFT), UP)
|
|
title = TextMobject("Probable scores")
|
|
title.scale(0.85)
|
|
title.next_to(binomial.bars, UP, 1.5*LARGE_BUFF)
|
|
|
|
self.play(
|
|
Write(title, run_time = 1),
|
|
FadeIn(binomial, run_time = 1, submobject_mode = "lagged_start"),
|
|
self.teacher.change, "raise_right_hand"
|
|
)
|
|
for values in binomial.values_list:
|
|
self.play(binomial.change_bar_values, values)
|
|
self.wait()
|
|
self.student_says(
|
|
"Is that valid?", target_mode = "sassy",
|
|
student_index = 0,
|
|
run_time = 1
|
|
)
|
|
self.play(self.teacher.change, "guilty")
|
|
self.wait()
|
|
|
|
binomial.add(title)
|
|
self.binomial = binomial
|
|
|
|
def show_alternate_distributions(self):
|
|
poisson = self.get_poisson()
|
|
VGroup(poisson, poisson.title).next_to(
|
|
self.students, UP, LARGE_BUFF
|
|
).shift(RIGHT)
|
|
gaussian = self.get_gaussian()
|
|
VGroup(gaussian, gaussian.title).next_to(
|
|
poisson, RIGHT, LARGE_BUFF
|
|
)
|
|
|
|
|
|
self.play(
|
|
FadeIn(poisson, submobject_mode = "lagged_start"),
|
|
RemovePiCreatureBubble(self.students[0]),
|
|
self.teacher.change, "raise_right_hand",
|
|
self.binomial.scale, 0.5,
|
|
self.binomial.to_corner, UP+LEFT,
|
|
)
|
|
self.play(Write(poisson.title, run_time = 1))
|
|
self.play(FadeIn(gaussian, submobject_mode = "lagged_start"))
|
|
self.play(Write(gaussian.title, run_time = 1))
|
|
self.wait(2)
|
|
self.change_student_modes(
|
|
*["sassy"]*3,
|
|
added_anims = [self.teacher.change, "plain"]
|
|
)
|
|
self.wait(2)
|
|
|
|
self.poisson = poisson
|
|
self.gaussian = gaussian
|
|
|
|
def emphasize_underweighted_tails(self):
|
|
poisson_arrows = VGroup()
|
|
arrow_template = Arrow(
|
|
ORIGIN, UP, color = GREEN,
|
|
tip_length = 0.15
|
|
)
|
|
for bar in self.poisson.bars[-4:]:
|
|
arrow = arrow_template.copy()
|
|
arrow.next_to(bar, UP, SMALL_BUFF)
|
|
poisson_arrows.add(arrow)
|
|
|
|
gaussian_arrows = VGroup()
|
|
for prop in 0.2, 0.8:
|
|
point = self.gaussian[0][0].point_from_proportion(prop)
|
|
arrow = arrow_template.copy()
|
|
arrow.next_to(point, UP, SMALL_BUFF)
|
|
gaussian_arrows.add(arrow)
|
|
|
|
for arrows in poisson_arrows, gaussian_arrows:
|
|
self.play(
|
|
ShowCreation(
|
|
arrows,
|
|
submobject_mode = "lagged_start",
|
|
run_time = 2
|
|
),
|
|
*[
|
|
ApplyMethod(pi.change, "thinking", arrows)
|
|
for pi in self.pi_creatures
|
|
]
|
|
)
|
|
self.wait()
|
|
self.wait(2)
|
|
|
|
####
|
|
|
|
def get_binomial(self):
|
|
k_range = range(11)
|
|
dists = [
|
|
get_binomial_distribution(10, p)
|
|
for p in 0.2, 0.8, 0.5
|
|
]
|
|
values_list = [
|
|
map(dist, k_range)
|
|
for dist in dists
|
|
]
|
|
chart = BarChart(
|
|
values = values_list[-1],
|
|
bar_names = k_range
|
|
)
|
|
chart.scale_to_fit_height(self.chart_height)
|
|
chart.values_list = values_list
|
|
return chart
|
|
|
|
def get_poisson(self):
|
|
k_range = range(11)
|
|
L = 2
|
|
values = [
|
|
np.exp(-L) * (L**k) / (scipy.special.gamma(k+1))
|
|
for k in k_range
|
|
]
|
|
chart = BarChart(
|
|
values = values,
|
|
bar_names = k_range,
|
|
bar_colors = [RED, YELLOW]
|
|
)
|
|
chart.scale_to_fit_height(self.chart_height)
|
|
title = TextMobject(
|
|
"Poisson distribution \\\\",
|
|
"$e^{-\\lambda}\\frac{\\lambda^k}{k!}$"
|
|
)
|
|
title.scale(0.75)
|
|
title.move_to(chart, UP)
|
|
title.shift(MED_SMALL_BUFF*RIGHT)
|
|
title[0].shift(SMALL_BUFF*UP)
|
|
chart.title = title
|
|
|
|
return chart
|
|
|
|
def get_gaussian(self):
|
|
axes = VGroup(self.binomial.x_axis, self.binomial.y_axis).copy()
|
|
graph = FunctionGraph(
|
|
lambda x : 5*np.exp(-x**2),
|
|
mark_paths_closed = True,
|
|
fill_color = BLUE_E,
|
|
fill_opacity = 1,
|
|
stroke_color = BLUE,
|
|
)
|
|
graph.scale_to_fit_width(axes.get_width())
|
|
graph.move_to(axes[0], DOWN)
|
|
|
|
title = TextMobject(
|
|
"Gaussian distribution \\\\ ",
|
|
"$\\frac{1}{\\sqrt{2\\pi \\sigma^2}} e^{-\\frac{(x-\\mu)^2}{2\\sigma^2}}$"
|
|
)
|
|
title.scale(0.75)
|
|
title.move_to(axes, UP)
|
|
title.shift(MED_SMALL_BUFF*RIGHT)
|
|
title[0].shift(SMALL_BUFF*UP)
|
|
result = VGroup(axes, graph)
|
|
result.title = title
|
|
|
|
return result
|
|
|
|
class IndependencePatreonThanks(PatreonThanks):
|
|
CONFIG = {
|
|
"specific_patrons" : [
|
|
"Ali Yahya",
|
|
"Desmos",
|
|
"Burt Humburg",
|
|
"CrypticSwarm",
|
|
"Juan Benet",
|
|
"Mayank M. Mehrotra",
|
|
"Lukas Biewald",
|
|
"Samantha D. Suplee",
|
|
"James Park",
|
|
"Erik Sundell",
|
|
"Yana Chernobilsky",
|
|
"Kaustuv DeBiswas",
|
|
"Kathryn Schmiedicke",
|
|
"Karan Bhargava",
|
|
"Yu Jun",
|
|
"Dave Nicponski",
|
|
"Damion Kistler",
|
|
"Markus Persson",
|
|
"Yoni Nazarathy",
|
|
"Corey Ogburn",
|
|
"Ed Kellett",
|
|
"Joseph John Cox",
|
|
"Dan Buchoff",
|
|
"Luc Ritchie",
|
|
"Tianyu Ge",
|
|
"Ted Suzman",
|
|
"Amir Fayazi",
|
|
"Linh Tran",
|
|
"Andrew Busey",
|
|
"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(DangerInProbability):
|
|
def construct(self):
|
|
n, p = 15, 0.5
|
|
dist = get_binomial_distribution(n, p)
|
|
values = np.array(map(dist, range(n+1)))
|
|
values *= 2
|
|
chart = BarChart(
|
|
values = values,
|
|
label_y_axis = False,
|
|
width = FRAME_WIDTH - 3,
|
|
height = 1.5*FRAME_Y_RADIUS
|
|
)
|
|
chart.to_edge(DOWN)
|
|
self.add(chart)
|
|
|
|
|
|
warning = self.get_warning_sign()
|
|
warning.scale_to_fit_height(2)
|
|
warning.to_edge(UP)
|
|
self.add(warning)
|
|
|
|
|
|
words = TextMobject("Independence")
|
|
words.scale(2.5)
|
|
words.next_to(warning, DOWN)
|
|
self.add(words)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|