3b1b-manim/active_projects/eop/independence.py

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)