Files
manim/eop/independence.py
2017-07-20 13:37:12 -07:00

1452 lines
45 KiB
Python

from helpers import *
from mobject.tex_mobject import TexMobject
from 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.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 import Scene
from scene.reconfigurable_scene import ReconfigurableScene
from scene.zoomed_scene import *
from camera import Camera
from mobject.svg_mobject import *
from mobject.tex_mobject import *
#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.highlight(WHITE)
quiz = VGroup(rect, content)
quiz.scale(0.7)
return quiz
def get_slot_group(bool_list, buff = MED_LARGE_BUFF, include_qs = True):
lines = VGroup(*[
Line(ORIGIN, MED_LARGE_BUFF*RIGHT)
for x in range(3)
])
lines.arrange_submobjects(RIGHT, buff = buff)
if include_qs:
labels = VGroup(*[
TextMobject("Q%d"%d) for d in range(1, 4)
])
else:
labels = VGroup(*[VectorizedPoint() for d in range(3)])
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 = SPACE_HEIGHT
base = 2.3
for i, line in enumerate(lines):
if i < len(bool_list):
if bool_list[i]:
mob = TexMobject("\\checkmark")
mob.highlight(GREEN)
slot_group.shift(total_height*DOWN / (base**(i+1)))
else:
mob = TexMobject("\\times")
mob.highlight(RED)
slot_group.shift(total_height*UP / (base**(i+1)))
else:
mob = VectorizedPoint()
mob.next_to(line, UP, SMALL_BUFF)
slot_group.content.add(mob)
return slot_group
#########
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.dither()
#####
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.highlight(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.dither()
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.dither()
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.dither()
self.play(Blink(morty))
self.dither()
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.dither()
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.highlight_by_tex("k", BLUE)
formula.highlight_by_tex("p", YELLOW)
choose_part = formula.get_part_by_tex("choose")
choose_part.highlight(WHITE)
choose_part[-2].highlight(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(*chart.family_members_with_points()),
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.highlight(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.dither()
def write_independence_assumption(self):
assumption = TextMobject("Independence assumption")
assumption.scale(1.2)
assumption.next_to(self.formula, DOWN, MED_LARGE_BUFF, LEFT)
assumption.highlight(GREEN_C)
self.play(Write(assumption, run_time = 2))
self.dither()
self.assumption = assumption
def cross_out_assumption(self):
cross = Cross(self.assumption)
cross.highlight(GREY)
self.bar_chart.save_state()
self.play(ShowCreation(cross))
self.play(self.bar_chart.fade, 0.7)
self.dither(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.highlight(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.dither(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.dither()
self.quiz = quiz
def ask_about_probabilities(self):
probabilities, abbreviated_probabilities = [
VGroup(*[
TexMobject(
"P(", s_tex, "=", str(score), ")", rhs
).highlight_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.dither()
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.dither()
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.highlight_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.dither()
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.dither()
for new_group_group in slot_group_groups[1:]:
self.play(Transform(group_group, new_group_group))
self.dither(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.dither()
#######
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.highlight_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.dither()
self.play(
ReplacementTransform(
score_group.copy(), score_group.organized
),
LaggedStart(FadeIn, score, run_time = 1)
)
self.play(score_group.restore)
self.dither()
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.highlight, YELLOW)
self.dither(4)
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)
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.highlight_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.highlight(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.dither(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.dither()
self.dither()
self.play(
Write(question),
self.randy.change, "confused"
)
self.dither(3)
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.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)])
self.add(full_quizzes)
self.dither()
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.dither()
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].highlight(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.dither()
self.splits = VGroup(top_split, bottom_split)
self.q1_split_labels = labels
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].highlight(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.highlight, YELLOW),
rate_func = there_and_back,
lag_ratio = 0.2,
))
self.dither()
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].highlight(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].highlight(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.dither(2)
self.play(Indicate(left_label_equation[0]))
self.dither()
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].highlight(color)
left_split.target.shift(LEFT)
movers = VGroup(*left_split[-self.n_movers:])
movers.generate_target()
for quiz in movers.target:
quiz[1].highlight(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].highlight(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.dither()
self.play(
MoveToTarget(
movers,
submobject_mode = "lagged_start",
run_time = 3,
),
Transform(equation, alt_equation)
)
self.dither()
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.highlight_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.dither()
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].highlight(RED)
for quiz in bottom_movers.target:
quiz[0].highlight(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.dither()
self.play(both_movers.restore)
self.dither()
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].highlight(GREEN)
for quiz in bottom_part:
quiz[-1].highlight(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.dither()
self.play(LaggedStart(
ApplyMethod, all_right,
lambda m : (m.highlight, YELLOW),
rate_func = there_and_back,
lag_ratio = 0.2,
run_time = 2
))
self.dither(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 AccurateProductRule(SampleSpaceScene, ThreeDScene):
def construct(self):
self.setup_terms()
self.add_sample_space()
self.dither()
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.dither()
self.play(Write(self.terms[1].value))
self.dither()
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.dither()
self.play(Write(self.terms[2].value))
self.dither()
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.dither()
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.dither()
self.play(ReplacementTransform(
label.copy(), self.terms[3]
))
self.dither()
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.dither()
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.dither()
self.play(ShowCreation(rect, run_time = 2))
self.play(FadeOut(rect))
self.dither()
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.dither()
class ShowIndependenceSymbolically(Scene):
def construct(self):
filler_tex = "Filler"
rhs = TexMobject("=", "0.8")
rhs.highlight_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.highlight(RED)
words = TextMobject("Doesn't matter")
words.highlight(RED)
words.next_to(arrow, UP)
self.add(rhs, lhs, arrow, words)
self.dither()
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.dither()