3b1b-manim/from_3b1b/active/bayes/beta1.py
2020-05-30 16:49:57 -07:00

3819 lines
112 KiB
Python

from manimlib.imports import *
from from_3b1b.active.bayes.beta_helpers import *
import scipy.stats
OUTPUT_DIRECTORY = "bayes/beta1"
# Scenes
class BarChartTest(Scene):
def construct(self):
bar_chart = BarChart()
bar_chart.to_edge(DOWN)
self.add(bar_chart)
class Thumbnail1(Scene):
def construct(self):
p1 = "$96\\%$"
p2 = "$93\\%$"
n1 = "50"
n2 = "200"
t2c = {
p1: BLUE,
p2: YELLOW,
n1: BLUE_C,
n2: YELLOW,
}
kw = {"tex_to_color_map": t2c}
text = VGroup(
TextMobject(f"{p1} with {n1} reviews", **kw),
TextMobject("vs.", **kw),
TextMobject(f"{p2} with {n2} reviews", **kw),
)
fix_percent(text[0].get_part_by_tex(p1)[-1])
fix_percent(text[2].get_part_by_tex(p2)[-1])
text.scale(2)
text.arrange(DOWN, buff=LARGE_BUFF)
text.set_width(FRAME_WIDTH - 1)
self.add(text)
class AltThumbnail1(Scene):
def construct(self):
N = 20
n_trials = 10000
p = 0.7
outcomes = (np.random.random((N, n_trials)) < p).sum(0)
counts = []
for k in range(N + 1):
counts.append((outcomes == k).sum())
hist = Histogram(
counts,
y_max=0.3,
y_tick_freq=0.05,
y_axis_numbers_to_show=[10, 20, 30],
x_label_freq=10,
)
hist.set_width(FRAME_WIDTH - 1)
hist.bars.set_submobject_colors_by_gradient(YELLOW, YELLOW, GREEN, BLUE)
hist.bars.set_stroke(WHITE, 2)
title = TextMobject("Binomial distribution")
title.set_width(12)
title.to_corner(UR, buff=0.8)
title.add_background_rectangle()
self.add(hist)
self.add(title)
class Thumbnail2(Scene):
def construct(self):
axes = self.get_axes()
graph = get_beta_graph(axes, 2, 2)
# sub_graph = axes.get_graph(
# lambda x: (1 - x) * graph.underlying_function(x)
# )
# sub_graph.add_line_to(axes.c2p(1, 0))
# sub_graph.add_line_to(axes.c2p(0, 0))
# sub_graph.set_stroke(YELLOW, 4)
# sub_graph.set_fill(YELLOW_D, 1)
new_graph = get_beta_graph(axes, 9, 2)
new_graph.set_stroke(GREEN, 4)
new_graph.set_fill(GREEN, 0.5)
self.add(axes)
self.add(graph)
self.add(new_graph)
arrow = Arrow(
axes.input_to_graph_point(0.5, graph),
axes.input_to_graph_point(0.8, new_graph),
path_arc=-90 * DEGREES,
buff=0.3
)
self.add(arrow)
formula = TexMobject(
"P(H|D) = {P(H)P(D|H) \\over P(D)}",
tex_to_color_map={
"H": YELLOW,
"D": GREEN,
}
)
formula.next_to(axes.c2p(0, 3), RIGHT, LARGE_BUFF)
formula.set_height(1.5)
formula.to_edge(LEFT)
formula.to_edge(UP, LARGE_BUFF)
formula.add_to_back(BackgroundRectangle(formula[:4], buff=0.25))
self.add(formula)
def get_axes(self, y_max=3, y_height=4.5, y_unit=0.5):
axes = get_beta_dist_axes(y_max=y_max, y_unit=y_unit)
axes.y_axis.set_height(y_height, about_point=axes.c2p(0, 0))
axes.to_edge(DOWN)
axes.scale(0.9)
return axes
class Thumbnail3(Thumbnail2):
def construct(self):
axes = self.get_axes(y_max=4, y_height=6)
axes.set_height(7)
graph = get_beta_graph(axes, 9, 2)
self.add(axes)
self.add(graph)
label = TexMobject(
"\\text{Beta}(10, 3)",
tex_to_color_map={
"10": GREEN,
"3": RED,
}
)
label = get_beta_label(9, 2)
label.set_height(1.25)
label.next_to(axes.c2p(0, 3), RIGHT, LARGE_BUFF)
self.add(label)
class HighlightReviewParts(Scene):
CONFIG = {
"reverse_order": False,
}
def construct(self):
# Setup up rectangles
rects = VGroup(*[Rectangle() for x in range(3)])
rects.set_stroke(width=0)
rects.set_fill(GREY, 0.5)
rects.set_height(1.35, stretch=True)
rects.set_width(9.75, stretch=True)
rects[0].move_to([-0.2, 0.5, 0])
rects[1].next_to(rects[0], DOWN, buff=0)
rects[2].next_to(rects[1], DOWN, buff=0)
rects[2].set_height(1, stretch=True, about_edge=UP)
inv_rects = VGroup()
for rect in rects:
fsr = FullScreenFadeRectangle()
fsr.append_points(rect.points[::-1])
inv_rects.add(fsr)
inv_rects.set_fill(BLACK, 0.85)
# Set up labels
ratings = [100, 96, 93]
n_reviews = [10, 50, 200]
colors = [PINK, BLUE, YELLOW]
review_labels = VGroup()
for rect, rating, nr, color in zip(rects, ratings, n_reviews, colors):
label = TexMobject(
f"{nr}", "\\text{ reviews }",
f"{rating}", "\\%",
)
label[2:].set_color(color)
label.set_height(1)
label.next_to(rect, UP, aligned_edge=RIGHT)
label.set_stroke(BLACK, 4, background=True)
fix_percent(label[3][0])
review_labels.add(label)
# Animations
curr_fsr = inv_rects[0]
curr_label = None
tuples = list(zip(inv_rects, review_labels))
if self.reverse_order:
tuples = reversed(tuples)
curr_fsr = inv_rects[-1]
for fsr, label in tuples:
if curr_fsr is fsr:
self.play(VFadeIn(fsr))
else:
self.play(
Transform(curr_fsr, fsr),
MoveToTarget(curr_label),
)
first, second = label[2:], label[:2]
if self.reverse_order:
first, second = second, first
self.add(first)
self.wait(2)
self.add(second)
self.wait(2)
label.generate_target()
label.target.scale(0.3)
if curr_label is None:
label.target.to_corner(UR)
label.target.shift(MED_LARGE_BUFF * LEFT)
else:
label.target.next_to(curr_label, DOWN)
curr_label = label
self.play(MoveToTarget(curr_label))
self.wait()
br = BackgroundRectangle(review_labels, buff=0.25)
br.set_fill(BLACK, 0.85)
br.set_width(FRAME_WIDTH)
br.set_height(FRAME_HEIGHT, stretch=True)
br.center()
self.add(br, review_labels)
self.play(
FadeOut(curr_fsr),
FadeIn(br),
)
self.wait()
class ShowThreeCases(Scene):
def construct(self):
titles = self.get_titles()
reviews = self.get_reviews(titles)
for review in reviews:
review.match_x(reviews[2])
# Introduce everything
self.play(LaggedStartMap(
FadeInFrom, titles,
lambda m: (m, DOWN),
lag_ratio=0.2
))
self.play(LaggedStart(*[
LaggedStartMap(
FadeInFromLarge, review,
lag_ratio=0.1
)
for review in reviews
], lag_ratio=0.1))
self.add(reviews)
self.wait()
self.play(ShowCreationThenFadeAround(reviews[2]))
self.wait()
# Suspicious of 100%
randy = Randolph()
randy.flip()
randy.set_height(2)
randy.next_to(
reviews[0], RIGHT, LARGE_BUFF,
aligned_edge=UP,
)
randy.look_at(reviews[0])
self.play(FadeIn(randy))
self.play(randy.change, "sassy")
self.play(Blink(randy))
self.wait()
self.play(FadeOut(randy))
# Low number means it could be a fluke.
review = reviews[0]
review.generate_target()
review.target.scale(2)
review.target.arrange(RIGHT)
review.target.move_to(review)
self.play(MoveToTarget(review))
alt_negs = [1, 2, 1, 0]
alt_reviews = VGroup()
for k in alt_negs:
alt_reviews.add(self.get_plusses_and_minuses(titles[0], 1, 10, k))
for ar in alt_reviews:
for m1, m2 in zip(ar, review):
m1.replace(m2)
alt_percents = VGroup(*[
TexMobject(str(10 * (10 - k)) + "\\%")
for k in alt_negs
])
hundo = titles[0][0]
for ap in alt_percents:
fix_percent(ap.family_members_with_points()[-1])
ap.match_style(hundo)
ap.match_height(hundo)
ap.move_to(hundo, RIGHT)
last_review = review
last_percent = hundo
for ar, ap in zip(alt_reviews, alt_percents):
self.play(
FadeInFrom(ar, 0.5 * DOWN, lag_ratio=0.2),
FadeOut(last_review),
FadeInFrom(ap, 0.5 * DOWN),
FadeOutAndShift(last_percent, 0.5 * UP),
run_time=1.5
)
last_review = ar
last_percent = ap
self.remove(last_review, last_percent)
self.add(titles, *reviews)
# How do you think about the tradeoff?
p1 = titles[1][0]
p2 = titles[2][0]
nums = VGroup(p1, p2)
lower_reviews = reviews[1:]
lower_reviews.generate_target()
lower_reviews.target.arrange(LEFT, buff=1.5)
lower_reviews.target.center()
nums.generate_target()
for nt, review in zip(nums.target, lower_reviews.target):
nt.next_to(review, UP, MED_LARGE_BUFF)
nums.target[0].match_y(nums.target[1])
self.clear()
self.play(
MoveToTarget(lower_reviews),
MoveToTarget(nums),
FadeOut(titles[1][1:]),
FadeOut(titles[2][1:]),
FadeOut(titles[0]),
FadeOut(reviews[0]),
run_time=2,
)
greater_than = TexMobject(">")
greater_than.scale(2)
greater_than.move_to(midpoint(
reviews[2].get_right(),
reviews[1].get_left(),
))
less_than = greater_than.copy().flip()
less_than.match_height(nums[0][0])
less_than.match_y(nums, DOWN)
nums.generate_target()
nums.target[1].next_to(less_than, LEFT, MED_LARGE_BUFF)
nums.target[0].next_to(less_than, RIGHT, MED_LARGE_BUFF)
squares = VGroup(*[
SurroundingRectangle(
submob, buff=0.01,
stroke_color=LIGHT_GREY,
stroke_width=1,
)
for submob in reviews[2]
])
squares.shuffle()
self.play(
LaggedStartMap(
ShowCreationThenFadeOut, squares,
lag_ratio=0.5 / len(squares),
run_time=3,
),
Write(greater_than),
)
self.wait()
self.play(
MoveToTarget(nums),
TransformFromCopy(
greater_than, less_than,
)
)
self.wait()
def get_titles(self):
titles = VGroup(
TextMobject(
"$100\\%$ \\\\",
"10 reviews"
),
TextMobject(
"$96\\%$ \\\\",
"50 reviews"
),
TextMobject(
"$93\\%$ \\\\",
"200 reviews"
),
)
colors = [PINK, BLUE, YELLOW]
for title, color in zip(titles, colors):
fix_percent(title[0][-1])
title[0].set_color(color)
titles.scale(1.25)
titles.arrange(DOWN, buff=1.5)
titles.to_corner(UL)
return titles
def get_reviews(self, titles):
return VGroup(
self.get_plusses_and_minuses(
titles[0], 5, 2, 0,
),
self.get_plusses_and_minuses(
titles[1], 5, 10, 2,
),
self.get_plusses_and_minuses(
titles[2], 8, 25, 14,
),
)
def get_plusses_and_minuses(self, title, n_rows, n_cols, n_minus):
check = TexMobject(CMARK_TEX, color=GREEN)
cross = TexMobject(XMARK_TEX, color=RED)
checks = VGroup(*[
check.copy() for x in range(n_rows * n_cols)
])
checks.arrange_in_grid(n_rows=n_rows, n_cols=n_cols)
checks.scale(0.5)
# if checks.get_height() > title.get_height():
# checks.match_height(title)
checks.next_to(title, RIGHT, LARGE_BUFF)
for check in random.sample(list(checks), n_minus):
mob = cross.copy()
mob.replace(check, dim_to_match=0)
check.become(mob)
return checks
class PreviewThreeVideos(Scene):
def construct(self):
# Write equations
equations = VGroup(
TexMobject("{10", "\\over", "10}", "=", "100\\%"),
TexMobject("{48", "\\over", "50}", "=", "96\\%"),
TexMobject("{186", "\\over", "200}", "=", "93\\%"),
)
equations.arrange(RIGHT, buff=3)
equations.to_edge(UP)
colors = [PINK, BLUE, YELLOW]
for eq, color in zip(equations, colors):
eq[-1].set_color(color)
fix_percent(eq[-1][-1])
vs_labels = VGroup(*[TextMobject("vs.") for x in range(2)])
for eq1, eq2, vs in zip(equations, equations[1:], vs_labels):
vs.move_to(midpoint(eq1.get_right(), eq2.get_left()))
self.add(equations)
self.add(vs_labels)
# Show topics
title = TextMobject("To be explained:")
title.set_height(0.7)
title.next_to(equations, DOWN, LARGE_BUFF)
title.to_edge(LEFT)
title.add(Underline(title))
topics = VGroup(
TextMobject("Binomial distributions"),
TextMobject("Bayesian updating"),
TextMobject("Probability density functions"),
TextMobject("Beta distribution"),
)
topics.arrange(DOWN, buff=MED_LARGE_BUFF, aligned_edge=LEFT)
topics.next_to(title, DOWN, MED_LARGE_BUFF)
topics.to_edge(LEFT, buff=LARGE_BUFF)
bullets = VGroup()
for topic in topics:
bullet = Dot()
bullet.next_to(topic, LEFT)
bullets.add(bullet)
self.play(
Write(title),
Write(bullets),
run_time=1,
)
self.play(LaggedStart(*[
FadeIn(topic, lag_ratio=0.1)
for topic in topics
], run_time=3, lag_ratio=0.3))
self.wait()
# Show videos
images = [
ImageMobject(os.path.join(
consts.VIDEO_DIR,
OUTPUT_DIRECTORY,
"images",
name
))
for name in ["Thumbnail1", "Thumbnail2", "Thumbnail3"]
]
thumbnails = Group()
for image in images:
image.set_width(FRAME_WIDTH / 3 - 1)
rect = SurroundingRectangle(image, buff=0)
rect.set_stroke(WHITE, 3)
rect.set_fill(BLACK, 1)
thumbnails.add(Group(rect, image))
thumbnails.arrange(RIGHT, buff=LARGE_BUFF)
for topic, i in zip(topics, [0, 1, 1, 2]):
thumbnail = thumbnails[i]
topic.generate_target()
topic.target.scale(0.6)
topic.target.next_to(thumbnail, DOWN, aligned_edge=LEFT)
topics[2].target.next_to(
topics[1].target, DOWN,
aligned_edge=LEFT,
)
self.play(
FadeOutAndShift(title, LEFT),
FadeOutAndShift(bullets, LEFT),
LaggedStartMap(MoveToTarget, topics),
LaggedStartMap(FadeIn, thumbnails),
)
self.wait()
tn_groups = Group(
Group(thumbnails[0], topics[0]),
Group(thumbnails[1], topics[1], topics[2]),
Group(thumbnails[2], topics[3]),
)
setup_words = TextMobject("Set up the model")
analysis_words = TextMobject("Analysis")
for words in [setup_words, analysis_words]:
words.scale(topics[0][0].get_height() / words[0][0].get_height())
words.set_color(YELLOW)
setup_words.move_to(topics[0], UL)
analysis_words.next_to(topics[3], DOWN, aligned_edge=LEFT)
def set_opacity(mob, alpha):
for sm in mob.family_members_with_points():
sm.set_opacity(alpha)
return mob
self.play(ApplyFunction(lambda m: set_opacity(m, 0.2), tn_groups[1:]))
self.play(
FadeIn(setup_words, lag_ratio=0.1),
topics[0].next_to, setup_words, DOWN, {"aligned_edge": LEFT},
)
tn_groups[0].add(setup_words)
self.wait(2)
for i in 0, 1:
self.play(
ApplyFunction(lambda m: set_opacity(m, 0.2), tn_groups[i]),
ApplyFunction(lambda m: set_opacity(m, 1), tn_groups[i + 1]),
)
self.wait(2)
self.play(FadeInFrom(analysis_words, 0.25 * UP))
tn_groups[2].add(analysis_words)
self.wait(2)
self.play(
FadeOut(setup_words),
FadeOut(topics[0]),
FadeOut(tn_groups[1]),
FadeOut(tn_groups[2]),
FadeOutAndShift(vs_labels, UP),
FadeOutAndShift(equations, UP),
ApplyFunction(lambda m: set_opacity(m, 1), thumbnails[0]),
)
thumbnails[0].generate_target()
# thumbnails[0].target.set_width(FRAME_WIDTH)
# thumbnails[0].target.center()
thumbnails[0].target.to_edge(UP)
self.play(MoveToTarget(thumbnails[0], run_time=4))
self.wait()
class LetsLookAtOneAnswer(TeacherStudentsScene):
def construct(self):
self.remove(self.background)
self.teacher_says(
"Let me show you\\\\one answer.",
added_anims=[
self.get_student_changes("pondering", "thinking", "pondering")
]
)
self.look_at(self.screen)
self.change_all_student_modes("thinking", look_at_arg=self.screen)
self.wait(4)
class LaplacesRuleOfSuccession(Scene):
def construct(self):
# Setup
title = TextMobject("How to read a rating")
title.set_height(0.75)
title.to_edge(UP)
underline = Underline(title)
underline.scale(1.2)
self.add(title, underline)
data = get_checks_and_crosses(11 * [True] + [False], width=10)
data.shift(DOWN)
underlines = get_underlines(data)
real_data = data[:10]
fake_data = data[10:]
def get_review_label(num, denom):
result = VGroup(
Integer(num, color=GREEN),
TextMobject("out of"),
Integer(denom),
)
result.arrange(RIGHT)
result.set_height(0.6)
return result
review_label = get_review_label(10, 10)
review_label.next_to(data[:10], UP, MED_LARGE_BUFF)
# Show initial review
self.add(review_label)
self.add(underlines[:10])
self.play(
ShowIncreasingSubsets(real_data, int_func=np.ceil),
CountInFrom(review_label[0], 0),
rate_func=lambda t: smooth(t, 3),
)
self.wait()
# Fake data
fd_rect = SurroundingRectangle(VGroup(fake_data, underlines[10:]))
fd_rect.set_stroke(WHITE, 2)
fd_rect.set_fill(GREY_E, 1)
fd_label = TextMobject("Pretend you see\\\\two more")
fd_label.next_to(fd_rect, DOWN)
fd_label.shift_onto_screen()
self.play(
FadeInFrom(fd_label, UP),
DrawBorderThenFill(fd_rect),
ShowCreation(underlines[10:])
)
self.wait()
for mark in data[10:]:
self.play(Write(mark))
self.wait()
# Update rating
review_center = VectorizedPoint(review_label.get_center())
pretend_label = TextMobject("Pretend that it's")
pretend_label.match_width(review_label)
pretend_label.next_to(review_label, UP, MED_LARGE_BUFF)
pretend_label.match_x(data)
pretend_label.set_color(BLUE_D)
old_review_label = VGroup(Integer(0), TextMobject("out of"), Integer(0))
old_review_label.become(review_label)
self.add(old_review_label, review_label)
self.play(
review_center.set_x, data.get_center()[0],
MaintainPositionRelativeTo(review_label, review_center),
UpdateFromAlphaFunc(
review_label[0],
lambda m, a: m.set_value(int(interpolate(10, 11, a)))
),
UpdateFromAlphaFunc(
review_label[2],
lambda m, a: m.set_value(int(interpolate(10, 12, a)))
),
FadeInFrom(pretend_label, LEFT),
old_review_label.scale, 0.5,
old_review_label.set_opacity, 0.5,
old_review_label.to_edge, LEFT,
)
self.wait()
# Show fraction
eq = TexMobject(
"{11", "\\over", "12}",
"\\approx", "91.7\\%"
)
fix_percent(eq[-1][-1])
eq.set_color_by_tex("11", GREEN)
eq.next_to(pretend_label, RIGHT)
eq.to_edge(RIGHT, buff=MED_LARGE_BUFF)
self.play(Write(eq))
self.wait()
self.play(ShowCreationThenFadeAround(eq))
self.wait()
# Remove clutter
old_review_label.generate_target()
old_review_label.target.next_to(title, DOWN, LARGE_BUFF)
old_review_label.target.to_edge(LEFT)
old_review_label.target.set_opacity(1)
arrow = Vector(0.5 * RIGHT)
arrow.next_to(old_review_label.target, RIGHT)
self.play(
MoveToTarget(old_review_label),
FadeIn(arrow),
eq.next_to, arrow, RIGHT,
FadeOutAndShift(
VGroup(
fake_data,
underlines,
pretend_label,
review_label,
fd_rect, fd_label,
),
DOWN,
lag_ratio=0.01,
),
real_data.match_width, old_review_label.target,
real_data.next_to, old_review_label.target, DOWN,
)
self.wait()
# Show 48 of 50 case
# Largely copied from above...not great
data = get_checks_and_crosses(
48 * [True] + 2 * [False] + [True, False],
width=FRAME_WIDTH - 1,
)
data.shift(DOWN)
underlines = get_underlines(data)
review_label = get_review_label(48, 50)
review_label.next_to(data, UP, MED_LARGE_BUFF)
true_data = data[:-2]
fake_data = data[-2:]
fd_rect.replace(fake_data, stretch=True)
fd_rect.stretch(1.2, 0)
fd_rect.stretch(2.2, 1)
fd_rect.shift(0.025 * DOWN)
fd_label.next_to(fd_rect, DOWN, LARGE_BUFF)
fd_label.shift_onto_screen()
fd_arrow = Arrow(fd_label.get_top(), fd_rect.get_corner(DL))
self.play(
FadeIn(underlines[:-2]),
ShowIncreasingSubsets(true_data, int_func=np.ceil),
CountInFrom(review_label[0], 0),
UpdateFromAlphaFunc(
review_label,
lambda m, a: m.set_opacity(a),
),
)
self.wait()
self.play(
FadeIn(fd_label),
GrowArrow(fd_arrow),
FadeIn(fd_rect),
Write(fake_data),
Write(underlines[-2:]),
)
self.wait()
# Pretend it's 49 / 52
old_review_label = VGroup(Integer(0), TextMobject("out of"), Integer(0))
old_review_label.become(review_label)
review_center = VectorizedPoint(review_label.get_center())
self.play(
review_center.set_x, data.get_center()[0] + 3,
MaintainPositionRelativeTo(review_label, review_center),
UpdateFromAlphaFunc(
review_label[0],
lambda m, a: m.set_value(int(interpolate(48, 49, a)))
),
UpdateFromAlphaFunc(
review_label[2],
lambda m, a: m.set_value(int(interpolate(50, 52, a)))
),
old_review_label.scale, 0.5,
old_review_label.to_edge, LEFT,
)
self.wait()
arrow2 = Vector(0.5 * RIGHT)
arrow2.next_to(old_review_label, RIGHT)
eq2 = TexMobject(
"{49", "\\over", "52}",
"\\approx", "94.2\\%"
)
fix_percent(eq2[-1][-1])
eq2[0].set_color(GREEN)
eq2.next_to(arrow2, RIGHT)
eq2.save_state()
eq2[1].set_opacity(0)
eq2[3:].set_opacity(0)
eq2[0].replace(review_label[0])
eq2[2].replace(review_label[2])
self.play(
Restore(eq2, run_time=1.5),
FadeIn(arrow2),
)
self.wait()
faders = VGroup(
fd_rect, fd_arrow, fd_label,
fake_data, underlines,
review_label,
)
self.play(
FadeOut(faders),
true_data.match_width, old_review_label,
true_data.next_to, old_review_label, DOWN,
)
# 200 review case
final_review_label = get_review_label(186, 200)
final_review_label.match_height(old_review_label)
final_review_label.move_to(old_review_label, LEFT)
final_review_label.shift(
arrow2.get_center() -
arrow.get_center()
)
data = get_checks_and_crosses([True] * 186 + [False] * 14 + [True, False])
data[:200].arrange_in_grid(10, 20, buff=0)
data[-2:].next_to(data[:200], DOWN, buff=0)
data.set_width(FRAME_WIDTH / 2 - 1)
data.to_edge(RIGHT, buff=MED_SMALL_BUFF)
data.to_edge(DOWN)
for mark in data:
mark.scale(0.5)
true_data = data[:-2]
fake_data = data[-2:]
self.play(
UpdateFromAlphaFunc(
final_review_label,
lambda m, a: m.set_opacity(a),
),
CountInFrom(final_review_label[0], 0),
ShowIncreasingSubsets(true_data),
)
self.wait()
arrow3 = Vector(0.5 * RIGHT)
arrow3.next_to(final_review_label, RIGHT)
eq3 = TexMobject(
"{187", "\\over", "202}",
"\\approx", "92.6\\%"
)
fix_percent(eq3[-1][-1])
eq3[0].set_color(GREEN)
eq3.next_to(arrow3, RIGHT)
self.play(
GrowArrow(arrow3),
FadeIn(eq3),
Write(fake_data)
)
self.wait()
self.play(
true_data.match_width, final_review_label,
true_data.next_to, final_review_label, DOWN,
FadeOut(fake_data)
)
self.wait()
# Make a selection
rect = SurroundingRectangle(VGroup(eq2, old_review_label))
rect.set_stroke(YELLOW, 2)
self.play(
ShowCreation(rect),
eq2[-1].set_color, YELLOW,
)
self.wait()
# Retitle
name = TextMobject("Laplace's rule of succession")
name.match_height(title)
name.move_to(title)
name.set_color(TEAL)
self.play(
FadeInFromDown(name),
FadeOutAndShift(title, UP),
underline.match_width, name,
)
self.wait()
class AskWhy(TeacherStudentsScene):
def construct(self):
self.student_says(
"Wait...why?",
look_at_arg=self.screen,
)
self.play(
self.students[0].change, "confused", self.screen,
self.students[1].change, "confused", self.screen,
self.teacher.change, "tease", self.students[2].eyes,
)
self.wait(3)
self.students[2].bubble.content.unlock_triangulation()
self.student_says(
"Is that really\\\\the answer?",
target_mode="raise_right_hand",
added_anims=[self.teacher.change, "thinking"],
)
self.wait(2)
self.teacher_says("Let's dive in!", target_mode="hooray")
self.change_all_student_modes("hooray")
self.wait(3)
class BinomialName(Scene):
def construct(self):
text = TextMobject("Probabilities of probabilities\\\\", "Part 1")
text.set_width(FRAME_WIDTH - 1)
text[0].set_color(BLUE)
self.add(text[0])
self.play(Write(text[1], run_time=2))
self.wait(2)
class WhatsTheModel(Scene):
CONFIG = {
"random_seed": 5,
}
def construct(self):
self.add_questions()
self.introduce_buyer_and_seller()
for x in range(3):
self.play(*self.experience_animations(self.seller, self.buyer))
self.wait()
self.add_probability_label()
self.bring_up_goal()
def add_questions(self):
questions = VGroup(
TextMobject("What's the model?"),
TextMobject("What are you optimizing?"),
)
for question, vect in zip(questions, [LEFT, RIGHT]):
question.move_to(vect * FRAME_WIDTH / 4)
questions.arrange(DOWN, buff=LARGE_BUFF)
questions.scale(1.5)
# Intro questions
self.play(FadeInFrom(questions[0]))
self.play(FadeInFrom(questions[1], UP))
self.wait()
questions[1].save_state()
self.questions = questions
def introduce_buyer_and_seller(self):
if hasattr(self, "questions"):
questions = self.questions
added_anims = [
questions[0].to_edge, UP,
questions[1].set_opacity, 0.5,
questions[1].scale, 0.25,
questions[1].to_corner, UR,
]
else:
added_anims = []
seller = Randolph(mode="coin_flip_1")
seller.to_edge(LEFT)
seller.label = TextMobject("Seller")
buyer = Mortimer()
buyer.to_edge(RIGHT)
buyer.label = TextMobject("Buyer")
VGroup(buyer, seller).shift(DOWN)
labels = VGroup()
for pi in seller, buyer:
pi.set_height(2)
pi.label.scale(1.5)
pi.label.next_to(pi, DOWN, MED_LARGE_BUFF)
labels.add(pi.label)
buyer.make_eye_contact(seller)
self.play(
LaggedStartMap(
FadeInFromDown, VGroup(seller, buyer, *labels),
lag_ratio=0.2
),
*added_anims
)
self.wait()
self.buyer = buyer
self.seller = seller
def add_probability_label(self):
seller = self.seller
buyer = self.buyer
label = get_prob_positive_experience_label()
label.add(TexMobject("=").next_to(label, RIGHT))
rhs = DecimalNumber(0.75)
rhs.next_to(label, RIGHT)
rhs.align_to(label[0], DOWN)
label.add(rhs)
label.scale(1.5)
label.next_to(seller, UP, MED_LARGE_BUFF, aligned_edge=LEFT)
rhs.set_color(YELLOW)
brace = Brace(rhs, UP)
success_rate = brace.get_text("Success rate")[0]
s_sym = brace.get_tex("s").scale(1.5, about_edge=DOWN)
success_rate.match_color(rhs)
s_sym.match_color(rhs)
self.add(label)
self.play(
GrowFromCenter(brace),
FadeInFrom(success_rate, 0.5 * DOWN)
)
self.wait()
self.play(
TransformFromCopy(success_rate[0], s_sym),
FadeOutAndShift(success_rate, 0.1 * RIGHT, lag_ratio=0.1),
)
for x in range(2):
self.play(*self.experience_animations(seller, buyer, arc=30 * DEGREES))
self.wait()
grey_box = SurroundingRectangle(rhs, buff=SMALL_BUFF)
grey_box.set_stroke(GREY_E, 0.5)
grey_box.set_fill(GREY_D, 1)
lil_q_marks = TexMobject("???")
lil_q_marks.scale(0.5)
lil_q_marks.next_to(buyer, UP)
self.play(
FadeOutAndShift(rhs, 0.5 * DOWN),
FadeInFrom(grey_box, 0.5 * UP),
FadeInFrom(lil_q_marks, DOWN),
buyer.change, "confused", grey_box,
)
rhs.set_opacity(0)
for x in range(2):
self.play(*self.experience_animations(seller, buyer, arc=30 * DEGREES))
self.play(buyer.change, "confused", lil_q_marks)
self.play(Blink(buyer))
self.prob_group = VGroup(
label, grey_box, brace, s_sym,
)
self.buyer_q_marks = lil_q_marks
def bring_up_goal(self):
prob_group = self.prob_group
questions = self.questions
questions.generate_target()
questions.target[1].replace(questions[0], dim_to_match=1)
questions.target[1].match_style(questions[0])
questions.target[0].replace(questions[1], dim_to_match=1)
questions.target[0].match_style(questions[1])
prob_group.generate_target()
prob_group.target.scale(0.5)
prob_group.target.next_to(self.seller, RIGHT)
self.play(
FadeOut(self.buyer_q_marks),
self.buyer.change, "pondering", questions[0],
self.seller.change, "pondering", questions[0],
MoveToTarget(prob_group),
MoveToTarget(questions),
)
self.play(self.seller.change, "coin_flip_1")
for x in range(3):
self.play(*self.experience_animations(self.seller, self.buyer))
self.wait()
#
def experience_animations(self, seller, buyer, arc=-30 * DEGREES, p=0.75):
positive = (random.random() < p)
words = TextMobject(
"Positive\\\\experience"
if positive else
"Negative\\\\experience"
)
words.set_color(GREEN if positive else RED)
if positive:
new_mode = random.choice([
"hooray",
"coin_flip_1",
])
else:
new_mode = random.choice([
"tired",
"angry",
"sad",
])
words.move_to(seller.get_corner(UR))
result = [
ApplyMethod(
words.move_to, buyer.get_corner(UL),
path_arc=arc,
run_time=2
),
VFadeInThenOut(words, run_time=2),
ApplyMethod(
buyer.change, new_mode, seller.eyes,
run_time=2,
rate_func=squish_rate_func(smooth, 0.5, 1),
),
ApplyMethod(
seller.change, "coin_flip_2", buyer.eyes,
rate_func=there_and_back,
),
]
return result
class IsSellerOne100(Scene):
def construct(self):
self.add_review()
self.show_probability()
self.show_simulated_reviews()
def add_review(self):
reviews = VGroup(*[TexMobject(CMARK_TEX) for x in range(10)])
reviews.arrange(RIGHT)
reviews.scale(2)
reviews.set_color(GREEN)
reviews.next_to(ORIGIN, UP)
blanks = VGroup(*[
Line(LEFT, RIGHT).match_width(rev).next_to(rev, DOWN, SMALL_BUFF)
for rev in reviews
])
blanks.shift(0.25 * reviews[0].get_width() * LEFT)
label = TextMobject(
" out of ",
)
tens = VGroup(*[Integer(10) for x in range(2)])
tens[0].next_to(label, LEFT)
tens[1].next_to(label, RIGHT)
tens.set_color(GREEN)
label.add(tens)
label.scale(2)
label.next_to(reviews, DOWN, LARGE_BUFF)
self.add(label)
self.add(blanks)
tens[0].to_count = reviews
self.play(
ShowIncreasingSubsets(reviews, int_func=np.ceil),
UpdateFromAlphaFunc(
tens[0],
lambda m, a: m.set_color(
interpolate_color(RED, GREEN, a)
).set_value(len(m.to_count))
),
run_time=2,
rate_func=bezier([0, 0, 1, 1]),
)
self.wait()
self.review_group = VGroup(reviews, blanks, label)
def show_probability(self):
review_group = self.review_group
prob_label = get_prob_positive_experience_label()
prob_label.add(TexMobject("=").next_to(prob_label, RIGHT))
rhs = DecimalNumber(1)
rhs.next_to(prob_label, RIGHT)
rhs.set_color(YELLOW)
prob_label.add(rhs)
prob_label.scale(2)
prob_label.to_corner(UL)
q_mark = TexMobject("?")
q_mark.set_color(YELLOW)
q_mark.match_height(rhs)
q_mark.reference = rhs
q_mark.add_updater(lambda m: m.next_to(m.reference, RIGHT))
rhs_rect = SurroundingRectangle(rhs, buff=0.2)
rhs_rect.set_color(RED)
not_necessarily = TextMobject("Not necessarily!")
not_necessarily.set_color(RED)
not_necessarily.scale(1.5)
not_necessarily.next_to(prob_label, DOWN, 1.5)
arrow = Arrow(
not_necessarily.get_top(),
rhs_rect.get_corner(DL),
buff=MED_SMALL_BUFF,
)
arrow.set_color(RED)
rhs.set_value(0)
self.play(
ChangeDecimalToValue(rhs, 1),
UpdateFromAlphaFunc(
prob_label,
lambda m, a: m.set_opacity(a),
),
FadeIn(q_mark),
)
self.wait()
self.play(
ShowCreation(rhs_rect),
Write(not_necessarily),
ShowCreation(arrow),
review_group.to_edge, DOWN,
run_time=1,
)
self.wait()
self.play(
ChangeDecimalToValue(rhs, 0.95),
FadeOut(rhs_rect),
FadeOut(arrow),
FadeOut(not_necessarily),
)
self.wait()
self.prob_label_group = VGroup(
prob_label, rhs, q_mark,
)
def show_simulated_reviews(self):
prob_label_group = self.prob_label_group
review_group = self.review_group
# Set up decimals
random.seed(2)
decimals = VGroup()
for x in range(10):
dec = DecimalNumber()
decimals.add(dec)
def randomize_decimals(decimals):
for dec in decimals:
value = random.random()
dec.set_value(value)
if value > 0.95:
dec.set_color(RED)
else:
dec.set_color(WHITE)
randomize_decimals(decimals)
decimals.set_height(0.3)
decimals.arrange(RIGHT, buff=MED_LARGE_BUFF)
decimals.next_to(ORIGIN, DOWN)
decimals[0].set_value(0.42)
decimals[0].set_color(WHITE)
decimals[1].set_value(0.97)
decimals[1].set_color(RED)
random_label = TextMobject("Random number\\\\in [0, 1]")
random_label.scale(0.7)
random_label.next_to(decimals[0], DOWN)
random_label.set_color(GREY_B)
arrows = VGroup()
for dec in decimals:
arrow = Vector(0.4 * UP)
arrow.next_to(dec, UP)
arrows.add(arrow)
# Set up marks
def get_marks(decs, arrows):
marks = VGroup()
for dec, arrow in zip(decs, arrows):
if dec.get_value() < 0.95:
mark = TexMobject(CMARK_TEX)
mark.set_color(GREEN)
else:
mark = TexMobject(XMARK_TEX)
mark.set_color(RED)
mark.set_height(0.5)
mark.next_to(arrow, UP)
marks.add(mark)
return marks
marks = get_marks(decimals, arrows)
lt_p95 = TexMobject("< 0.95")
gte_p95 = TexMobject("\\ge 0.95")
for label in lt_p95, gte_p95:
label.match_height(decimals[0])
lt_p95.next_to(decimals[0], RIGHT, MED_SMALL_BUFF)
gte_p95.next_to(decimals[1], RIGHT, MED_SMALL_BUFF)
lt_p95.set_color(GREEN)
gte_p95.set_color(RED)
# Introduce simulation
review_group.save_state()
self.play(
review_group.scale, 0.25,
review_group.to_corner, UR,
Write(random_label),
CountInFrom(decimals[0], 0),
)
self.wait()
self.play(FadeInFrom(lt_p95, LEFT))
self.play(
GrowArrow(arrows[0]),
FadeInFrom(marks[0], DOWN)
)
self.wait()
self.play(
FadeOutAndShift(lt_p95, 0.5 * RIGHT),
FadeInFrom(gte_p95, 0.5 * LEFT),
)
self.play(
random_label.match_x, decimals[1],
CountInFrom(decimals[1], 0),
UpdateFromAlphaFunc(
decimals[1],
lambda m, a: m.set_opacity(a),
),
)
self.play(
GrowArrow(arrows[1]),
FadeInFrom(marks[1], DOWN),
)
self.wait()
self.play(
LaggedStartMap(
CountInFrom, decimals[2:],
),
UpdateFromAlphaFunc(
decimals[2:],
lambda m, a: m.set_opacity(a),
),
FadeOut(gte_p95),
run_time=1,
)
self.add(decimals)
self.play(
LaggedStartMap(GrowArrow, arrows[2:]),
LaggedStartMap(FadeInFromDown, marks[2:]),
run_time=1
)
self.add(arrows, marks)
self.wait()
# Add new rows
decimals.arrows = arrows
decimals.add_updater(lambda d: d.next_to(d.arrows, DOWN))
added_anims = [FadeOut(random_label)]
rows = VGroup(marks)
for x in range(3):
self.play(
arrows.shift, DOWN,
UpdateFromFunc(decimals, randomize_decimals),
*added_anims,
)
added_anims = []
new_marks = get_marks(decimals, arrows)
self.play(LaggedStartMap(FadeInFromDown, new_marks))
self.wait()
rows.add(new_marks)
# Create a stockpile of new rows
added_rows = VGroup()
decimals.clear_updaters()
decimals.save_state()
for x in range(100):
randomize_decimals(decimals)
added_rows.add(get_marks(decimals, arrows))
decimals.restore()
# Compress rows
rows.generate_target()
for group in rows.target, added_rows:
group.scale(0.3)
for row in group:
row.arrange(RIGHT, buff=SMALL_BUFF)
group.arrange(DOWN, buff=0.2)
rows.target.next_to(prob_label_group, DOWN, MED_LARGE_BUFF)
rows.target.set_x(-3.5)
nr = 15
added_rows[:nr].move_to(rows.target, UP)
added_rows[nr:2 * nr].move_to(rows.target, UP)
added_rows[nr:2 * nr].shift(3.5 * RIGHT)
added_rows[2 * nr:3 * nr].move_to(rows.target, UP)
added_rows[2 * nr:3 * nr].shift(7 * RIGHT)
added_rows = added_rows[4:3 * nr]
self.play(
MoveToTarget(rows),
FadeOut(decimals),
FadeOut(arrows),
)
self.play(ShowIncreasingSubsets(added_rows), run_time=3)
# Show scores
all_rows = VGroup(*rows, *added_rows)
scores = VGroup()
ten_rects = VGroup()
for row in all_rows:
score = Integer(sum([
mark.get_color() == Color(GREEN)
for mark in row
]))
score.match_height(row)
score.next_to(row, RIGHT)
if score.get_value() == 10:
score.set_color(TEAL)
ten_rects.add(SurroundingRectangle(score))
scores.add(score)
ten_rects.set_stroke(YELLOW, 2)
self.play(FadeIn(scores))
self.wait()
self.play(LaggedStartMap(ShowCreation, ten_rects))
self.play(LaggedStartMap(FadeOut, ten_rects))
self.wait(2)
# Show alternate possibilities
prob = DecimalNumber(0.95)
prob.set_color(YELLOW)
template = prob_label_group[0][-1]
prob.match_height(template)
prob.move_to(template, LEFT)
rect = BackgroundRectangle(template, buff=SMALL_BUFF)
rect.set_fill(BLACK, 1)
self.add(rect)
self.add(prob)
self.play(
LaggedStartMap(FadeOutAndShift, all_rows, lag_ratio=0.01),
LaggedStartMap(FadeOutAndShift, scores, lag_ratio=0.01),
Restore(review_group),
)
for value in [0.9, 0.99, 0.8, 0.95]:
self.play(ChangeDecimalToValue(prob, value))
self.wait()
# No longer used
def show_random_numbers(self):
prob_label_group = self.prob_label_group
random.seed(2)
rows = VGroup(*[
VGroup(*[
Integer(
random.randint(0, 99)
).move_to(0.85 * x * RIGHT)
for x in range(10)
])
for y in range(10 * 2)
])
rows.arrange_in_grid(n_cols=2, buff=MED_LARGE_BUFF)
rows[:10].shift(LEFT)
rows.set_height(5.5)
rows.center().to_edge(DOWN)
lt_95 = VGroup(*[
mob
for row in rows
for mob in row
if mob.get_value() < 95
])
square = Square()
square.set_stroke(width=0)
square.set_fill(YELLOW, 0.5)
square.set_width(1.5 * rows[0][0].get_height())
# highlights = VGroup(*[
# square.copy().move_to(mob)
# for row in rows
# for mob in row
# if mob.get_value() < 95
# ])
row_rects = VGroup(*[
SurroundingRectangle(row)
for row in rows
if all([m.get_value() < 95 for m in row])
])
row_rects.set_stroke(GREEN, 2)
self.play(
LaggedStartMap(
ShowIncreasingSubsets, rows,
run_time=3,
lag_ratio=0.25,
),
FadeOutAndShift(self.review_group, DOWN),
prob_label_group.set_height, 0.75,
prob_label_group.to_corner, UL,
)
self.wait()
# self.add(highlights, rows)
self.play(
# FadeIn(highlights)
lt_95.set_fill, BLUE,
lt_95.set_stroke, BLUE, 2, {"background": True},
)
self.wait()
self.play(LaggedStartMap(ShowCreation, row_rects))
self.wait()
class LookAtAllPossibleSuccessRates(Scene):
def construct(self):
axes = get_beta_dist_axes(y_max=6, y_unit=1)
dist = scipy.stats.beta(10, 2)
graph = axes.get_graph(dist.pdf)
graph.set_stroke(BLUE, 3)
flat_graph = graph.copy()
flat_graph.points[:, 1] = axes.c2p(0, 0)[1]
flat_graph.set_stroke(YELLOW, 3)
x_labels = axes.x_axis.numbers
x_labels.set_opacity(0)
sellers = VGroup(*[
self.get_example_seller(label.get_value())
for label in x_labels
])
sellers.arrange(RIGHT, buff=LARGE_BUFF)
sellers.set_width(FRAME_WIDTH - 1)
sellers.to_edge(UP, buff=LARGE_BUFF)
sellers.generate_target()
for seller, label in zip(sellers.target, x_labels):
seller.next_to(label, DOWN)
seller[0].set_opacity(0)
seller[1].set_opacity(0)
seller[2].replace(label, dim_to_match=1)
x_label = TextMobject("All possible success rates")
x_label.next_to(axes.c2p(0.5, 0), UP)
x_label.shift(2 * LEFT)
y_axis_label = TextMobject(
"A kind of probability\\\\",
"of probabilities"
)
y_axis_label.scale(0.75)
y_axis_label.next_to(axes.y_axis, RIGHT)
y_axis_label.to_edge(UP)
y_axis_label[1].set_color(YELLOW)
graph_label = TextMobject(
"Some notion of likelihood\\\\",
"for each one"
)
graph_label[1].align_to(graph_label[0], LEFT)
graph_label.next_to(graph.get_boundary_point(UP), UP)
graph_label.shift(0.5 * DOWN)
graph_label.to_edge(RIGHT)
x_axis_line = Line(axes.c2p(0, 0), axes.c2p(1, 0))
x_axis_line.set_stroke(YELLOW, 3)
shuffled_sellers = VGroup(*sellers)
shuffled_sellers.shuffle()
self.play(GrowFromCenter(shuffled_sellers[0]))
self.play(LaggedStartMap(
FadeInFromPoint, shuffled_sellers[1:],
lambda m: (m, sellers.get_center())
))
self.wait()
self.play(
MoveToTarget(sellers),
FadeIn(axes),
run_time=2,
)
self.play(
x_label.shift, 4 * RIGHT,
UpdateFromAlphaFunc(
x_label,
lambda m, a: m.set_opacity(a),
rate_func=there_and_back,
),
ShowCreation(x_axis_line),
run_time=3,
)
self.play(FadeOut(x_axis_line))
self.wait()
self.play(
FadeInFromDown(graph_label),
ReplacementTransform(flat_graph, graph),
)
self.wait()
self.play(FadeInFromDown(y_axis_label))
# Show probabilities
x_tracker = ValueTracker(0.5)
prob_label = get_prob_positive_experience_label(True, True, True)
prob_label.next_to(axes.c2p(0, 2), RIGHT, MED_LARGE_BUFF)
prob_label.decimal.tracker = x_tracker
prob_label.decimal.add_updater(
lambda m: m.set_value(m.tracker.get_value())
)
v_line = Line(DOWN, UP)
v_line.set_stroke(YELLOW, 2)
v_line.tracker = x_tracker
v_line.graph = graph
v_line.axes = axes
v_line.add_updater(
lambda m: m.put_start_and_end_on(
m.axes.x_axis.n2p(m.tracker.get_value()),
m.axes.input_to_graph_point(m.tracker.get_value(), m.graph),
)
)
self.add(v_line)
for x in [0.95, 0.8, 0.9]:
self.play(
x_tracker.set_value, x,
run_time=4,
)
self.wait()
def get_example_seller(self, success_rate):
randy = Randolph(mode="coin_flip_1", height=1)
label = TexMobject("s = ")
decimal = DecimalNumber(success_rate)
decimal.match_height(label)
decimal.next_to(label[-1], RIGHT)
label.set_color(YELLOW)
decimal.set_color(YELLOW)
VGroup(label, decimal).next_to(randy, DOWN)
result = VGroup(randy, label, decimal)
result.randy = randy
result.label = label
result.decimal = decimal
return result
class AskAboutUnknownProbabilities(Scene):
def construct(self):
# Setup
unknown_title, prob_title = titles = self.get_titles()
v_line = Line(UP, DOWN)
v_line.set_height(FRAME_HEIGHT)
v_line.set_stroke([WHITE, LIGHT_GREY], 3)
h_line = Line(LEFT, RIGHT)
h_line.set_width(FRAME_WIDTH)
h_line.next_to(titles, DOWN)
processes = VGroup(
get_random_coin(shuffle_time=1),
get_random_die(shuffle_time=1.5),
get_random_card(shuffle_time=2),
)
processes.arrange(DOWN, buff=0.7)
processes.next_to(unknown_title, DOWN, LARGE_BUFF)
processes_rect = BackgroundRectangle(processes)
processes_rect.set_fill(BLACK, 1)
prob_labels = VGroup(
TexMobject("P(", "00", ")", "=", "1 / 2"),
TexMobject("P(", "00", ")", "=", "1 / 6}"),
TexMobject("P(", "00", ")", "=", "1 / 52}"),
)
prob_labels.scale(1.5)
prob_labels.arrange(DOWN, aligned_edge=LEFT)
prob_labels.match_x(prob_title)
for pl, pr in zip(prob_labels, processes):
pl.match_y(pr)
content = pr[1].copy()
content.replace(pl[1], dim_to_match=0)
pl.replace_submobject(1, content)
# Putting numbers to the unknown
number_rects = VGroup(*[
SurroundingRectangle(pl[-1])
for pl in prob_labels
])
number_rects.set_stroke(YELLOW, 2)
for pl in prob_labels:
pl.save_state()
pl[:3].match_x(prob_title)
pl[3:].match_x(prob_title)
pl.set_opacity(0)
self.add(processes)
self.play(
LaggedStartMap(FadeInFromDown, titles),
LaggedStart(
ShowCreation(v_line),
ShowCreation(h_line),
lag_ratio=0.1,
),
LaggedStartMap(Restore, prob_labels),
run_time=1
)
self.wait(10)
# self.play(
# LaggedStartMap(
# ShowCreationThenFadeOut,
# number_rects,
# run_time=3,
# )
# )
# self.wait(2)
# Highlight coin flip
fade_rects = VGroup(*[
VGroup(
BackgroundRectangle(pl, buff=MED_SMALL_BUFF),
BackgroundRectangle(pr, buff=MED_SMALL_BUFF),
)
for pl, pr in zip(prob_labels, processes)
])
fade_rects.set_fill(BLACK, 0.8)
prob_half = prob_labels[0]
half = prob_half[-1]
half_underline = Line(LEFT, RIGHT)
half_underline.set_width(half.get_width() + MED_SMALL_BUFF)
half_underline.next_to(half, DOWN, buff=SMALL_BUFF)
half_underline.set_stroke(YELLOW, 3)
self.play(
FadeIn(fade_rects[1]),
FadeIn(fade_rects[2]),
)
self.wait(2)
self.play(
FadeIn(fade_rects[0]),
FadeOut(fade_rects[1]),
)
self.wait(3)
self.play(
FadeOut(fade_rects[0]),
FadeOut(fade_rects[2]),
)
self.wait(4)
# Transition to question
processes.suspend_updating()
self.play(
LaggedStart(
FadeOutAndShift(unknown_title, UP),
FadeOutAndShift(prob_title, UP),
lag_ratio=0.2,
),
FadeOutAndShift(h_line, UP, lag_ratio=0.1),
FadeOutAndShift(processes, LEFT, lag_ratio=0.1),
FadeOut(prob_labels[1]),
FadeOut(prob_labels[2]),
v_line.rotate, 90 * DEGREES,
v_line.shift, 0.6 * FRAME_HEIGHT * UP,
prob_half.center,
prob_half.to_edge, UP,
run_time=2,
)
self.clear()
self.add(prob_half)
arrow = Vector(UP)
arrow.next_to(half, DOWN)
question = TextMobject("What exactly does\\\\this mean?")
question.next_to(arrow, DOWN)
self.play(
GrowArrow(arrow),
FadeInFrom(question, UP),
)
self.wait(2)
self.play(
FadeOutAndShift(question, RIGHT),
Rotate(arrow, 90 * DEGREES),
VFadeOut(arrow),
)
# Show long run averages
self.show_many_coins(20, 50)
self.show_many_coins(40, 100)
# Make probability itself unknown
q_marks = TexMobject("???")
q_marks.set_color(YELLOW)
q_marks.replace(half, dim_to_match=0)
randy = Randolph(mode="confused")
randy.center()
randy.look_at(prob_half)
self.play(
FadeOutAndShift(half, UP),
FadeInFrom(q_marks, DOWN),
)
self.play(FadeIn(randy))
self.play(Blink(randy))
self.wait()
# self.embed()
def get_titles(self):
unknown_label = TextMobject("Random process")
prob_label = TextMobject("Long-run frequency")
titles = VGroup(unknown_label, prob_label)
titles.scale(1.25)
unknown_label.move_to(FRAME_WIDTH * LEFT / 4)
prob_label.move_to(FRAME_WIDTH * RIGHT / 4)
titles.to_edge(UP, buff=MED_SMALL_BUFF)
titles.set_color(BLUE)
return titles
def show_many_coins(self, n_rows, n_cols):
coin_choices = VGroup(
get_coin("H"),
get_coin("T"),
)
coin_choices.set_stroke(width=0)
coins = VGroup(*[
random.choice(coin_choices).copy()
for x in range(n_rows * n_cols)
])
def organize_coins(coin_group):
coin_group.scale(1 / coin_group[0].get_height())
coin_group.arrange_in_grid(n_rows=n_rows)
coin_group.set_width(FRAME_WIDTH - 1)
coin_group.to_edge(DOWN, MED_LARGE_BUFF)
organize_coins(coins)
sorted_coins = VGroup()
for coin in coins:
coin.generate_target()
sorted_coins.add(coin.target)
sorted_coins.submobjects.sort(key=lambda m: m.symbol)
organize_coins(sorted_coins)
self.play(LaggedStartMap(
FadeInFrom, coins,
lambda m: (m, 0.2 * DOWN),
run_time=3,
rate_func=linear
))
self.wait()
self.play(LaggedStartMap(
MoveToTarget, coins,
path_arc=30 * DEGREES,
run_time=2,
lag_ratio=1 / len(coins),
))
self.wait()
self.play(FadeOut(coins))
class AskProbabilityOfCoins(Scene):
def construct(self):
condition = VGroup(
TextMobject("If you've seen"),
Integer(80, color=BLUE_C),
get_coin("H").set_height(0.5),
TextMobject("and"),
Integer(20, color=RED_C),
get_coin("T").set_height(0.5),
)
condition.arrange(RIGHT)
condition.to_edge(UP)
self.add(condition)
question = TexMobject(
"\\text{What is }",
"P(", "00", ")", "?"
)
coin = get_coin("H")
coin.replace(question.get_part_by_tex("00"))
question.replace_submobject(
question.index_of_part_by_tex("00"),
coin
)
question.next_to(condition, DOWN)
self.add(question)
values = ["H"] * 80 + ["T"] * 20
random.shuffle(values)
coins = VGroup(*[
get_coin(symbol)
for symbol in values
])
coins.arrange_in_grid(10, 10, buff=MED_SMALL_BUFF)
coins.set_width(5)
coins.next_to(question, DOWN, MED_LARGE_BUFF)
self.play(
ShowIncreasingSubsets(coins),
run_time=8,
rate_func=bezier([0, 0, 1, 1])
)
self.wait()
self.embed()
class RunCarFactory(Scene):
def construct(self):
# Factory
factory = SVGMobject(file_name="factory")
factory.set_fill(GREY_D)
factory.set_stroke(width=0)
factory.flip()
factory.set_height(6)
factory.to_edge(LEFT)
self.add(factory)
# Dumb hack
l1 = Line(
factory[0].points[-200],
factory[0].points[-216],
)
l2 = Line(
factory[0].points[-300],
factory[0].points[-318],
)
for line in l1, l2:
square = Square()
square.set_fill(BLACK, 1)
square.set_stroke(width=0)
square.replace(line)
factory.add(square)
rect = Rectangle()
rect.match_style(factory)
rect.set_height(1.1)
rect.set_width(6.75, stretch=True)
rect.move_to(factory, DL)
# Get cars
car = Car(color=interpolate_color(BLUE_E, GREY_C, 0.5))
car.set_height(0.9)
for tire in car.get_tires():
tire.set_fill(GREY_C)
tire.set_stroke(BLACK)
car.randy.set_opacity(0)
car.move_to(rect.get_corner(DR))
cars = VGroup()
n_cars = 20
for x in range(n_cars):
cars.add(car.copy())
for car in cars[4], cars[6]:
scratch = VMobject()
scratch.start_new_path(UP)
scratch.add_line_to(0.25 * DL)
scratch.add_line_to(0.25 * UR)
scratch.add_line_to(DOWN)
scratch.set_stroke([RED_A, RED_C], [0.1, 2, 2, 0.1])
scratch.set_height(0.25)
scratch.move_to(car)
scratch.shift(0.1 * DOWN)
car.add(scratch)
self.add(cars, rect)
self.play(LaggedStartMap(
MoveCar, cars,
lambda m: (m, m.get_corner(DR) + 10 * RIGHT),
lag_ratio=0.3,
rate_func=linear,
run_time=1.5 * n_cars,
))
self.remove(cars)
class CarFactoryNumbers(Scene):
def construct(self):
# Test words
denom_words = TextMobject(
"in a test of 100 cars",
tex_to_color_map={"100": BLUE},
)
denom_words.to_corner(UR)
numer_words = TextMobject(
"2 defects found",
tex_to_color_map={"2": RED}
)
numer_words.move_to(denom_words, LEFT)
self.play(Write(denom_words, run_time=1))
self.wait()
self.play(
denom_words.next_to, numer_words, DOWN, {"aligned_edge": LEFT},
FadeIn(numer_words),
)
self.wait()
# Question words
question = VGroup(
TextMobject("How do you plan"),
TextMobject("for"),
Integer(int(1e6), color=BLUE),
TextMobject("cars?")
)
question[1:].arrange(RIGHT, aligned_edge=DOWN)
question[2].shift(
(question[2][1].get_bottom()[1] - question[2][0].get_bottom()[1]) * UP
)
question[1:].next_to(question[0], DOWN, aligned_edge=LEFT)
question.next_to(denom_words, DOWN, LARGE_BUFF, aligned_edge=LEFT)
self.play(
UpdateFromAlphaFunc(
question,
lambda m, a: m.set_opacity(a),
),
CountInFrom(question[2], 0, run_time=1.5)
)
self.wait()
class ComplainAboutSimplisticModel(TeacherStudentsScene):
def construct(self):
axes = self.get_experience_graph()
self.add(axes)
self.play(
self.teacher.change, "raise_right_hand", axes,
self.get_student_changes(
"pondering", "erm", "sassy",
look_at_arg=axes,
),
ShowCreation(
axes.graph,
run_time=3,
rate_func=linear,
),
)
self.wait(2)
student = self.students[2]
bubble = SpeechBubble(
direction=LEFT,
height=3,
width=5,
)
bubble.pin_to(student)
bubble.write("What about something\\\\like this?")
self.play(
axes.next_to, student, UL,
VFadeOut(axes.graph),
FadeIn(bubble),
Write(bubble.content, run_time=1),
student.change, "raise_left_hand",
self.students[0].change, "thinking", axes,
self.students[1].change, "thinking", axes,
self.teacher.change, "happy",
)
new_graph = VMobject()
new_graph.set_points_as_corners([
axes.c2p(0, 0.75),
axes.c2p(2, 0.9),
axes.c2p(4, 0.5),
axes.c2p(6, 0.75),
axes.c2p(8, 0.55),
axes.c2p(10, 0.95),
])
new_graph.make_smooth()
new_graph.set_stroke([YELLOW, RED, GREEN], 2)
self.play(
ShowCreation(new_graph),
*[
ApplyMethod(pi.look_at, new_graph)
for pi in self.pi_creatures
]
)
self.wait(3)
def get_experience_graph(self):
axes = Axes(
x_min=-1,
x_max=10,
y_min=0,
y_max=1.25,
y_axis_config={
"unit_size": 5,
"tick_frequency": 0.25,
"include_tip": False,
}
)
axes.set_stroke(LIGHT_GREY, 1)
axes.set_height(3)
y_label = TextMobject("Experience quality")
y_label.scale(0.5)
y_label.next_to(axes.y_axis.get_top(), RIGHT, SMALL_BUFF)
axes.add(y_label)
lines = VGroup()
for x in range(10):
lines.add(
Line(axes.c2p(x, 0), axes.c2p(x + 0.9, 0))
)
lines.set_stroke(RED, 3)
for line in lines:
if random.random() < 0.5:
line.set_y(axes.c2p(0, 1)[1])
line.set_stroke(GREEN)
axes.add(lines)
axes.graph = lines
rect = BackgroundRectangle(axes, buff=0.25)
rect.set_stroke(WHITE, 1)
rect.set_fill(BLACK, 1)
axes.add_to_back(rect)
axes.to_corner(UR)
return axes
class ComingUpWrapper(Scene):
def construct(self):
background = FullScreenFadeRectangle()
background.set_fill(GREY_E, 1)
title = TextMobject("What's coming...")
title.scale(1.5)
title.to_edge(UP)
rect = ScreenRectangle()
rect.set_height(6)
rect.set_stroke(WHITE)
rect.set_fill(BLACK, 1)
rect.next_to(title, DOWN)
self.add(background, rect)
self.play(FadeInFromDown(title))
self.wait()
class PreviewBeta(Scene):
def construct(self):
axes = get_beta_dist_axes(label_y=True)
axes.y_axis.remove(axes.y_axis.numbers)
marks = get_plusses_and_minuses(p=0.75)
marks.next_to(axes.y_axis.get_top(), DR, buff=0.75)
beta_label = get_beta_label(0, 0)
beta_label.next_to(marks, UR, buff=LARGE_BUFF)
beta_label.to_edge(UP)
bl_left = beta_label.get_left()
beta_container = VGroup()
graph_container = VGroup()
n_graphs = 2
for x in range(n_graphs):
graph_container.add(VMobject())
def get_counts(marks):
is_plusses = [m.is_plus for m in marks]
p = sum(is_plusses)
n = len(is_plusses) - p
return p, n
def update_beta(container):
counts = get_counts(marks)
new_label = get_beta_label(*counts)
new_label.move_to(bl_left, LEFT)
container.set_submobjects([new_label])
return container
def update_graph(container):
counts = get_counts(marks)
new_graph = get_beta_graph(axes, *counts)
new_graphs = [*container[1:], new_graph]
for g, a in zip(new_graphs, np.linspace(0.2, 1, n_graphs)):
g.set_opacity(a)
container.set_submobjects(new_graphs)
return container
self.add(axes)
self.play(
ShowIncreasingSubsets(marks),
UpdateFromFunc(
beta_container,
update_beta,
),
UpdateFromFunc(
graph_container,
update_graph,
),
run_time=15,
rate_func=bezier([0, 0, 1, 1]),
)
self.wait()
class AskInverseQuestion(WhatsTheModel):
def construct(self):
self.force_skipping()
self.introduce_buyer_and_seller()
self.bs_group = VGroup(
self.buyer,
self.seller,
self.buyer.label,
self.seller.label,
)
self.bs_group.to_edge(DOWN)
self.revert_to_original_skipping_status()
self.add_probability_label()
self.show_many_review_animations()
self.ask_question()
def add_probability_label(self):
label = get_prob_positive_experience_label(True, True, False)
label.decimal.set_value(0.95)
label.next_to(self.seller, UP, aligned_edge=LEFT, buff=MED_LARGE_BUFF)
self.add(label)
self.probability_label = label
def show_many_review_animations(self):
for x in range(7):
self.play(*self.experience_animations(
self.seller,
self.buyer,
arc=30 * DEGREES,
p=0.95,
))
def ask_question(self):
pis = [self.buyer, self.seller]
labels = VGroup(
self.get_prob_review_label(10, 0),
self.get_prob_review_label(48, 2),
self.get_prob_review_label(186, 14),
)
labels.arrange(DOWN)
labels.to_edge(UP)
labels[0].save_state()
labels[0].set_opacity(0)
words = labels[0][-3:-1]
words.set_opacity(1)
words.scale(1.5)
words.center().to_edge(UP)
self.play(
FadeInFromDown(words),
)
self.wait()
self.play(
Restore(labels[0]),
*[
ApplyMethod(pi.change, 'pondering', labels)
for pi in pis
]
)
self.play(Blink(pis[0]))
self.play(Blink(pis[1]))
self.play(LaggedStartMap(FadeInFromDown, labels[1:]))
self.wait(2)
# Succinct
short_label = TexMobject(
"P(\\text{data} | s)",
tex_to_color_map={
"\\text{data}": LIGHT_GREY,
"s": YELLOW
}
)
short_label.scale(2)
short_label.next_to(labels, DOWN, LARGE_BUFF),
rect = SurroundingRectangle(short_label, buff=MED_SMALL_BUFF)
bs_group = self.bs_group
bs_group.add(self.probability_label)
self.play(
FadeInFrom(short_label, UP),
bs_group.scale, 0.5, {"about_edge": DOWN},
)
self.play(ShowCreation(rect))
self.wait()
def get_prob_review_label(self, n_positive, n_negative):
label = TexMobject(
"P(",
f"{n_positive}\\,{CMARK_TEX}", ",\\,",
f"{n_negative}\\,{XMARK_TEX}",
"\\,\\text{ Given that }",
"s = 0.95",
")",
)
label.set_color_by_tex_to_color_map({
CMARK_TEX: GREEN,
XMARK_TEX: RED,
"0.95": YELLOW,
})
return label
class SimulationsOf10Reviews(Scene):
CONFIG = {
"s": 0.95,
"histogram_height": 5,
"histogram_width": 10,
}
def construct(self):
# Add s label
s_label = TexMobject("s = 0.95")
s_label.set_height(0.3)
s_label.to_corner(UL, buff=MED_SMALL_BUFF)
s_label.set_color(YELLOW)
self.add(s_label)
self.camera.frame.shift(LEFT)
s_label.shift(LEFT)
# Add random row
np.random.seed(0)
row = get_random_num_row(self.s)
count = self.get_count(row)
count.add_updater(
lambda m: m.set_value(
sum([s.positive for s in row.syms])
)
)
def update_nums(nums):
for num in nums:
num.set_value(np.random.random())
row.nums.save_state()
row.nums.set_color(WHITE)
self.play(
UpdateFromFunc(row.nums, update_nums),
run_time=2,
)
row.nums.restore()
self.wait()
self.add(count)
self.play(
ShowIncreasingSubsets(row.syms),
run_time=2,
rate_func=linear,
)
count.clear_updaters()
self.wait()
# Histogram
data = np.zeros(11)
histogram = self.get_histogram(data)
stacks = VGroup()
for bar in histogram.bars:
stacks.add(VGroup(bar.copy()))
def put_into_histogram(row_count_group):
row, count = row_count_group
count.clear_updaters()
index = int(count.get_value())
stack = stacks[index]
row.set_width(stack.get_width() - SMALL_BUFF)
row.next_to(stack, UP, SMALL_BUFF)
count.replace(histogram.axes.x_labels[index])
stack.add(row)
return row_count_group
# Random samples in histogram
self.play(
FadeIn(histogram),
ApplyFunction(
put_into_histogram,
VGroup(row, count),
)
)
self.wait()
for x in range(2):
row = get_random_num_row(self.s)
count = self.get_count(row)
group = VGroup(row, count)
self.play(FadeIn(group, lag_ratio=0.2))
self.wait(0.5)
self.play(
ApplyFunction(
put_into_histogram,
VGroup(row, count),
)
)
# More!
for x in range(40):
row = get_random_num_row(self.s)
count = self.get_count(row)
lower_group = VGroup(row, count).copy()
put_into_histogram(lower_group)
self.add(row, count, lower_group)
self.wait(0.1)
self.remove(row, count)
data = np.array([len(stack) - 1 for stack in stacks])
self.add(row, count)
self.play(
FadeOut(stacks),
FadeOut(count),
histogram.bars.become, histogram.get_bars(data),
histogram.axes.y_labels.set_opacity, 1,
histogram.axes.h_lines.set_opacity, 1,
histogram.axes.y_axis.set_opacity, 1,
)
self.remove(stacks)
arrow = Vector(0.5 * DOWN)
arrow.set_stroke(width=5)
arrow.set_color(YELLOW)
arrow.next_to(histogram.bars[10], UP, SMALL_BUFF)
def update(dummy):
new_row = get_random_num_row(self.s)
row.become(new_row)
count = sum([m.positive for m in new_row.nums])
data[count] += 1
histogram.bars.become(histogram.get_bars(data))
arrow.next_to(histogram.bars[count], UP, SMALL_BUFF)
self.add(arrow)
self.play(
UpdateFromFunc(Group(row, arrow, histogram.bars), update),
run_time=10,
)
#
def get_histogram(self, data):
histogram = Histogram(
data,
bar_colors=[RED, RED, BLUE, GREEN],
height=self.histogram_height,
width=self.histogram_width,
)
histogram.to_edge(DOWN)
histogram.axes.y_labels.set_opacity(0)
histogram.axes.h_lines.set_opacity(0)
return histogram
def get_count(self, row):
count = Integer()
count.set_height(0.75)
count.next_to(row, DOWN, buff=0.65)
count.set_value(sum([s.positive for s in row.syms]))
return count
class SimulationsOf10ReviewsSquished(SimulationsOf10Reviews):
CONFIG = {
"histogram_height": 2,
"histogram_width": 11,
}
def get_histogram(self, data):
hist = super().get_histogram(data)
hist.to_edge(UP, buff=1.5)
return hist
class SimulationsOf50Reviews(Scene):
CONFIG = {
"s": 0.95,
"histogram_config": {
"x_label_freq": 5,
"y_axis_numbers_to_show": range(10, 70, 10),
"y_max": 0.6,
"y_tick_freq": 0.1,
"height": 5,
"bar_colors": [BLUE],
},
"random_seed": 1,
}
def construct(self):
self.add_s_label()
data = np.zeros(51)
histogram = self.get_histogram(data)
row = self.get_row()
count = self.get_count(row)
original_count = count.get_value()
count.set_value(0)
self.add(histogram)
self.play(
ShowIncreasingSubsets(row),
ChangeDecimalToValue(count, original_count)
)
# Run many samples
arrow = Vector(0.5 * DOWN)
arrow.set_stroke(width=5)
arrow.set_color(YELLOW)
arrow.next_to(histogram.bars[10], UP, SMALL_BUFF)
total_data_label = VGroup(
TextMobject("Total samples: "),
Integer(1),
)
total_data_label.arrange(RIGHT)
total_data_label.next_to(row, DOWN)
total_data_label.add_updater(
lambda m: m[1].set_value(data.sum())
)
def update(dummy, n_added_data_points=0):
new_row = self.get_row()
row.become(new_row)
num_positive = sum([m.positive for m in new_row])
count.set_value(num_positive)
data[num_positive] += 1
if n_added_data_points:
values = np.random.random((n_added_data_points, 50))
counts = (values < self.s).sum(1)
for i in range(len(data)):
data[i] += (counts == i).sum()
histogram.bars.become(histogram.get_bars(data))
histogram.bars.set_fill(GREY_C)
histogram.bars[48].set_fill(GREEN)
arrow.next_to(histogram.bars[num_positive], UP, SMALL_BUFF)
self.add(arrow, total_data_label)
group = VGroup(histogram.bars, row, count, arrow)
self.play(
UpdateFromFunc(group, update),
run_time=4
)
self.play(
UpdateFromFunc(
group,
lambda m: update(m, 1000)
),
run_time=4
)
random.seed(0)
np.random.seed(0)
update(group)
self.wait()
# Show 48 bar
axes = histogram.axes
y = choose(50, 48) * (self.s)**48 * (1 - self.s)**2
line = DashedLine(
axes.c2p(0, y),
axes.c2p(51, y),
)
label = TexMobject("{:.1f}\\%".format(100 * y))
fix_percent(label.family_members_with_points()[-1])
label.next_to(line, RIGHT)
self.play(
ShowCreation(line),
FadeInFromPoint(label, line.get_start())
)
def add_s_label(self):
s_label = TexMobject("s = 0.95")
s_label.set_height(0.3)
s_label.to_corner(UL, buff=MED_SMALL_BUFF)
s_label.shift(0.8 * DOWN)
s_label.set_color(YELLOW)
self.add(s_label)
def get_histogram(self, data):
histogram = Histogram(
data, **self.histogram_config
)
histogram.to_edge(DOWN)
return histogram
def get_row(self, n=50):
row = get_random_checks_and_crosses(n, self.s)
row.move_to(3.5 * UP)
return row
def get_count(self, row):
count = Integer(sum([m.positive for m in row]))
count.set_height(0.3)
count.next_to(row, RIGHT)
return count
class ShowBinomialFormula(SimulationsOf50Reviews):
CONFIG = {
"histogram_config": {
"x_label_freq": 5,
"y_axis_numbers_to_show": range(10, 40, 10),
"y_max": 0.3,
"y_tick_freq": 0.1,
"height": 2.5,
"bar_colors": [BLUE],
},
"random_seed": 0,
}
def construct(self):
# Add histogram
dist = scipy.stats.binom(50, self.s)
data = np.array([
dist.pmf(x)
for x in range(0, 51)
])
histogram = self.get_histogram(data)
histogram.bars.set_fill(GREY_C)
histogram.bars[48].set_fill(GREEN)
self.add(histogram)
row = self.get_row()
self.add(row)
# Formula
prob_label = get_prob_review_label(48, 2)
eq = TexMobject("=")
formula = get_binomial_formula(50, 48, self.s)
equation = VGroup(
prob_label,
eq,
formula,
)
equation.arrange(RIGHT)
equation.next_to(histogram, UP, LARGE_BUFF)
equation.to_edge(RIGHT)
prob_label.save_state()
arrow = Vector(DOWN)
arrow.next_to(histogram.bars[48], UP, SMALL_BUFF)
prob_label.next_to(arrow, UP)
self.play(
FadeIn(prob_label),
GrowArrow(arrow),
)
for mob in prob_label[1::2]:
line = Underline(mob)
line.match_color(mob)
self.play(ShowCreationThenDestruction(line))
self.wait(0.5)
self.play(
Restore(prob_label),
FadeIn(equation[1:], lag_ratio=0.1),
)
self.wait()
self.explain_n_choose_k(row, formula)
# Circle formula parts
rect1 = SurroundingRectangle(formula[4:8])
rect2 = SurroundingRectangle(formula[8:])
rect1.set_stroke(GREEN, 2)
rect2.set_stroke(RED, 2)
for rect in rect1, rect2:
self.play(ShowCreation(rect))
self.wait()
self.play(FadeOut(rect))
# Show numerical answer
eq2 = TexMobject("=")
value = DecimalNumber(dist.pmf(48), num_decimal_places=5)
rhs = VGroup(eq2, value)
rhs.arrange(RIGHT)
rhs.match_y(eq)
rhs.to_edge(RIGHT, buff=MED_SMALL_BUFF)
self.play(
FadeInFrom(value, LEFT),
FadeIn(eq2),
equation.next_to, eq2, LEFT,
)
self.wait()
# Show alternate values of k
n = 50
for k in it.chain(range(47, 42, -1), range(43, 51), [49, 48]):
new_prob_label = get_prob_review_label(k, n - k)
new_prob_label.replace(prob_label)
prob_label.become(new_prob_label)
new_formula = get_binomial_formula(n, k, self.s)
new_formula.replace(formula)
formula.set_submobjects(new_formula)
value.set_value(dist.pmf(k))
histogram.bars.set_fill(LIGHT_GREY)
histogram.bars[k].set_fill(GREEN)
arrow.next_to(histogram.bars[k], UP, SMALL_BUFF)
new_row = get_checks_and_crosses((n - k) * [False] + k * [True])
new_row.replace(row)
row.become(new_row)
self.wait(0.5)
# Name it as the Binomial distribution
long_equation = VGroup(prob_label, eq, formula, eq2, value)
bin_name = TextMobject("Binomial", " Distribution")
bin_name.scale(1.5)
bin_name.next_to(histogram, UP, MED_LARGE_BUFF)
underline = Underline(bin_name[0])
underline.set_stroke(PINK, 2)
nck_rect = SurroundingRectangle(formula[:4])
nck_rect.set_stroke(PINK, 2)
self.play(
long_equation.next_to, self.slots, DOWN, MED_LARGE_BUFF,
long_equation.to_edge, RIGHT,
FadeInFrom(bin_name, DOWN),
)
self.wait()
self.play(ShowCreationThenDestruction(underline))
self.wait()
bools = [True] * 50
bools[random.randint(0, 49)] = False
bools[random.randint(0, 49)] = False
row.become(get_checks_and_crosses(bools).replace(row))
self.play(ShowIncreasingSubsets(row, run_time=4))
self.wait()
# Show likelihood and posterior labels
likelihood_label = TexMobject(
"P(",
"\\text{data}", "\\,|\\,",
"\\text{success rate}",
")",
)
posterior_label = TexMobject(
"P(",
"\\text{success rate}",
"\\,|\\,",
"\\text{data}",
")",
)
for label in (likelihood_label, posterior_label):
label.set_color_by_tex_to_color_map({
"data": GREEN,
"success": YELLOW,
})
likelihood_label.next_to(
prob_label, DOWN, LARGE_BUFF, aligned_edge=LEFT
)
right_arrow = Vector(RIGHT)
right_arrow.next_to(likelihood_label, RIGHT)
ra_label = TextMobject("But we want")
ra_label.match_width(right_arrow)
ra_label.next_to(right_arrow, UP, SMALL_BUFF)
posterior_label.next_to(right_arrow, RIGHT)
self.play(
FadeInFrom(likelihood_label, UP),
bin_name.set_height, 0.4,
bin_name.set_y, histogram.axes.c2p(0, .25)[1]
)
self.wait()
self.play(
GrowArrow(right_arrow),
FadeInFrom(ra_label, 0.5 * LEFT),
)
anims = []
for i, j in enumerate([0, 3, 2, 1, 4]):
anims.append(
TransformFromCopy(
likelihood_label[i],
posterior_label[j],
path_arc=-45 * DEGREES,
run_time=2,
)
)
self.play(*anims)
self.add(posterior_label)
self.wait()
# Prepare for new plot
histogram.add(bin_name)
always(arrow.next_to, histogram.bars[48], UP, SMALL_BUFF)
self.play(
FadeOut(likelihood_label),
FadeOut(posterior_label),
FadeOut(right_arrow),
FadeOut(ra_label),
FadeOutAndShift(row, UP),
FadeOutAndShift(self.slots, UP),
histogram.scale, 0.7,
histogram.to_edge, UP,
arrow.scale, 0.5,
arrow.set_stroke, None, 4,
long_equation.center,
run_time=1.5,
)
self.add(arrow)
# x_labels = histogram.axes.x_labels
# underline = Underline(x_labels)
# underline.set_stroke(GREEN, 3)
# self.play(
# LaggedStartMap(
# ApplyFunction, x_labels,
# lambda mob: (
# lambda m: m.scale(1.5).set_color(GREEN),
# mob,
# ),
# rate_func=there_and_back,
# ),
# ShowCreationThenDestruction(underline),
# )
# num_checks = TexMobject("\\# " + CMARK_TEX)
# num_checks.set_color(GREEN)
# num_checks.next_to(
# x_labels, RIGHT,
# MED_LARGE_BUFF,
# aligned_edge=DOWN,
# )
# self.play(Write(num_checks))
# self.wait()
low_axes = get_beta_dist_axes(y_max=0.3, y_unit=0.1, label_y=False)
low_axes.y_axis.set_height(
2,
about_point=low_axes.c2p(0, 0),
stretch=True,
)
low_axes.to_edge(DOWN)
low_axes.x_axis.numbers.set_color(YELLOW)
y_label_copies = histogram.axes.y_labels.copy()
y_label_copies.set_height(0.6 * low_axes.get_height())
y_label_copies.next_to(low_axes, LEFT, 0, aligned_edge=UP)
y_label_copies.shift(SMALL_BUFF * UP)
low_axes.y_axis.add(y_label_copies)
low_axes.y_axis.set_opacity(0)
# Show alternate values of s
s_tracker = ValueTracker(self.s)
s_tip = ArrowTip(start_angle=-90 * DEGREES)
s_tip.set_color(YELLOW)
s_tip.axis = low_axes.x_axis
s_tip.st = s_tracker
s_tip.add_updater(
lambda m: m.next_to(m.axis.n2p(m.st.get_value()), UP, buff=0)
)
pl_decimal = DecimalNumber(self.s)
pl_decimal.set_color(YELLOW)
pl_decimal.replace(prob_label[-2][2:])
prob_label[-2][2:].set_opacity(0)
s_label = VGroup(prob_label[-2][:2], pl_decimal).copy()
sl_rect = SurroundingRectangle(s_label)
sl_rect.set_stroke(YELLOW, 2)
self.add(pl_decimal)
self.play(
ShowCreation(sl_rect),
Write(low_axes),
)
self.play(
s_label.next_to, s_tip, UP, 0.2, ORIGIN, s_label[1],
ReplacementTransform(sl_rect, s_tip)
)
always(s_label.next_to, s_tip, UP, 0.2, ORIGIN, s_label[1])
decimals = VGroup(pl_decimal, s_label[1], formula[5], formula[9])
decimals.s_tracker = s_tracker
histogram.s_tracker = s_tracker
histogram.n = n
histogram.rhs_value = value
def update_decimals(decs):
for dec in decs:
dec.set_value(decs.s_tracker.get_value())
def update_histogram(hist):
new_dist = scipy.stats.binom(hist.n, hist.s_tracker.get_value())
new_data = np.array([
new_dist.pmf(x)
for x in range(0, 51)
])
new_bars = hist.get_bars(new_data)
new_bars.match_style(hist.bars)
hist.bars.become(new_bars)
hist.rhs_value.set_value(new_dist.pmf(48))
bar_copy = histogram.bars[48].copy()
value.initial_config["num_decimal_places"] = 3
value.set_value(value.get_value())
bar_copy.next_to(value, RIGHT, aligned_edge=DOWN)
bar_copy.add_updater(
lambda m: m.set_height(
max(
histogram.bars[48].get_height() * 0.75,
1e-6,
),
stretch=True,
about_edge=DOWN,
)
)
self.add(bar_copy)
self.add(histogram)
self.add(decimals)
for s in [0.95, 0.5, 0.99, 0.9]:
self.play(
s_tracker.set_value, s,
UpdateFromFunc(decimals, update_decimals),
UpdateFromFunc(histogram, update_histogram),
UpdateFromFunc(value, lambda m: m),
UpdateFromFunc(s_label, lambda m: m.update),
run_time=5,
)
self.wait()
# Plot
def func(x):
return scipy.stats.binom(50, x).pmf(48) + 1e-5
graph = low_axes.get_graph(func, step_size=0.05)
graph.set_stroke(BLUE, 3)
v_line = Line(DOWN, UP)
v_line.axes = low_axes
v_line.st = s_tracker
v_line.graph = graph
v_line.add_updater(
lambda m: m.put_start_and_end_on(
m.axes.c2p(m.st.get_value(), 0),
m.axes.input_to_graph_point(
m.st.get_value(),
m.graph,
),
)
)
v_line.set_stroke(GREEN, 2)
dot = Dot()
dot.line = v_line
dot.set_height(0.05)
dot.add_updater(lambda m: m.move_to(m.line.get_end()))
self.play(
ApplyMethod(
histogram.bars[48].stretch, 2, 1, {"about_edge": DOWN},
rate_func=there_and_back,
run_time=2,
),
)
self.wait()
self.play(low_axes.y_axis.set_opacity, 1)
self.play(
FadeIn(graph),
FadeOut(s_label),
FadeOut(s_tip),
)
self.play(
TransformFromCopy(histogram.bars[48], v_line),
FadeIn(dot),
)
self.add(histogram)
decimals.remove(decimals[1])
for s in [0.9, 0.96, 1, 0.8, 0.96]:
self.play(
s_tracker.set_value, s,
UpdateFromFunc(decimals, update_decimals),
UpdateFromFunc(histogram, update_histogram),
UpdateFromFunc(value, lambda m: m),
run_time=5,
)
self.wait()
# Write formula
clean_form = TexMobject(
"P(", "\\text{data}", "\\,|\\,", "{s}", ")", "=",
"c", "\\cdot",
"{s}", "^{\\#" + CMARK_TEX + "}",
"(1 - ", "{s}", ")", "^{\\#" + XMARK_TEX + "}",
tex_to_color_map={
"{s}": YELLOW,
"\\#" + CMARK_TEX: GREEN,
"\\#" + XMARK_TEX: RED,
}
)
clean_form.next_to(formula, DOWN, MED_LARGE_BUFF)
clean_form.save_state()
clean_form[:6].align_to(equation[1], RIGHT)
clean_form[6].match_x(formula[2])
clean_form[7].set_opacity(0)
clean_form[7].next_to(clean_form[6], RIGHT, SMALL_BUFF)
clean_form[8:11].match_x(formula[4:8])
clean_form[11:].match_x(formula[8:])
clean_form.saved_state.move_to(clean_form, LEFT)
fade_rects = VGroup(
BackgroundRectangle(equation[:2]),
BackgroundRectangle(formula),
BackgroundRectangle(VGroup(eq2, bar_copy)),
)
fade_rects.set_fill(BLACK, 0.8)
fade_rects[1].set_fill(opacity=0)
pre_c = formula[:4].copy()
pre_s = formula[4:8].copy()
pre_1ms = formula[8:].copy()
self.play(
FadeIn(fade_rects),
FadeIn(clean_form[:6])
)
self.play(ShowCreationThenFadeAround(clean_form[3]))
self.wait()
for cf, pre in (clean_form[6], pre_c), (clean_form[8:11], pre_s), (clean_form[11:], pre_1ms):
self.play(
GrowFromPoint(cf, pre.get_center()),
pre.move_to, cf,
pre.scale, 0,
)
self.remove(pre)
self.wait()
self.wait()
self.play(Restore(clean_form))
# Show with 480 and 20
top_fade_rect = BackgroundRectangle(histogram)
top_fade_rect.shift(SMALL_BUFF * DOWN)
top_fade_rect.scale(1.5, about_edge=DOWN)
top_fade_rect.set_fill(BLACK, 0)
new_formula = get_binomial_formula(500, 480, 0.96)
new_formula.move_to(formula)
def func500(x):
return scipy.stats.binom(500, x).pmf(480) + 1e-5
graph500 = low_axes.get_graph(func500, step_size=0.05)
graph500.set_stroke(TEAL, 3)
self.play(
top_fade_rect.set_opacity, 1,
fade_rects.set_opacity, 1,
FadeIn(new_formula)
)
self.clear()
self.add(new_formula, clean_form, low_axes, graph, v_line, dot)
self.add(low_axes.y_axis)
self.play(TransformFromCopy(graph, graph500))
self.wait()
y_axis = low_axes.y_axis
y_axis.save_state()
sf = 3
y_axis.stretch(sf, 1, about_point=low_axes.c2p(0, 0))
for label in y_label_copies:
label.stretch(1 / sf, 1)
v_line.suspend_updating()
v_line.graph = graph500
self.play(
Restore(y_axis, rate_func=reverse_smooth),
graph.stretch, sf, 1, {"about_edge": DOWN},
graph500.stretch, sf, 1, {"about_edge": DOWN},
)
v_line.resume_updating()
self.add(v_line, dot)
sub_decimals = VGroup(new_formula[5], new_formula[9])
sub_decimals.s_tracker = s_tracker
for s in [0.94, 0.98, 0.96]:
self.play(
s_tracker.set_value, s,
UpdateFromFunc(sub_decimals, update_decimals),
run_time=5,
)
self.wait()
def explain_n_choose_k(self, row, formula):
row.add_updater(lambda m: m)
brace = Brace(formula[:4], UP, buff=SMALL_BUFF)
words = brace.get_text("``50 choose 48''")
slots = self.slots = VGroup()
for sym in row:
line = Underline(sym)
line.scale(0.9)
slots.add(line)
for slot in slots:
slot.match_y(slots[0])
formula[1].counted = slots
k_rect = SurroundingRectangle(formula[2])
k_rect.set_stroke(GREEN, 2)
checks = VGroup()
for sym in row:
if sym.positive:
checks.add(sym)
self.play(
GrowFromCenter(brace),
FadeInFromDown(words),
)
self.wait()
self.play(FadeOut(words))
formula.save_state()
self.play(
ShowIncreasingSubsets(slots),
UpdateFromFunc(
formula[1],
lambda m: m.set_value(len(m.counted))
),
run_time=2,
)
formula.restore()
self.add(formula)
self.wait()
self.play(
LaggedStartMap(
ApplyMethod, checks,
lambda m: (m.shift, 0.3 * DOWN),
rate_func=there_and_back,
lag_ratio=0.05,
),
ShowCreationThenFadeOut(k_rect),
run_time=2,
)
self.remove(checks)
self.add(row)
self.wait()
# Example orderings
row_target = VGroup()
for sym in row:
sym.generate_target()
row_target.add(sym.target)
row_target.sort(submob_func=lambda m: -int(m.positive))
row_target.arrange(
RIGHT, buff=get_norm(row[0].get_right() - row[1].get_left())
)
row_target.move_to(row)
self.play(
LaggedStartMap(
MoveToTarget, row,
path_arc=30 * DEGREES,
lag_ratio=0,
),
)
self.wait()
row.sort()
self.play(Swap(*row[-3:-1]))
self.add(row)
self.wait()
# All orderings
nck_count = Integer(2)
nck_count.next_to(brace, UP)
nck_top = nck_count.get_top()
always(nck_count.move_to, nck_top, UP)
combs = list(it.combinations(range(50), 48))
bool_lists = [
[i in comb for i in range(50)]
for comb in combs
]
row.counter = nck_count
row.bool_lists = bool_lists
def update_row(r):
i = r.counter.get_value() - 1
new_row = get_checks_and_crosses(r.bool_lists[i])
new_row.replace(r, dim_to_match=0)
r.set_submobjects(new_row)
row.add_updater(update_row)
self.add(row)
self.play(
ChangeDecimalToValue(nck_count, choose(50, 48)),
run_time=10,
)
row.clear_updaters()
self.wait()
self.play(
FadeOut(nck_count),
FadeOut(brace),
)
class StateIndependence(Scene):
def construct(self):
row = get_random_checks_and_crosses()
row.to_edge(UP)
# self.add(row)
arrows = VGroup()
for m1, m2 in zip(row, row[1:]):
arrow = Arrow(
m1.get_bottom() + 0.025 * DOWN,
m2.get_bottom(),
path_arc=145 * DEGREES,
max_stroke_width_to_length_ratio=10,
max_tip_length_to_length_ratio=0.5,
)
arrow.tip.rotate(-10 * DEGREES)
arrow.shift(SMALL_BUFF * DOWN)
arrow.set_color(YELLOW)
arrows.add(arrow)
words = TextMobject("No influence")
words.set_height(0.25)
words.next_to(arrows[0], DOWN)
self.play(
ShowCreation(arrows[0]),
FadeIn(words)
)
for i in range(10):
self.play(
words.next_to, arrows[i + 1], DOWN,
FadeOut(arrows[i]),
ShowCreation(arrows[i + 1])
)
last_arrow = arrows[i + 1]
self.play(
FadeOut(words),
FadeOut(last_arrow),
)
class IllustrateBinomialSetupWithCoins(Scene):
def construct(self):
coins = [
get_coin("H"),
get_coin("T"),
]
coin_row = VGroup()
for x in range(12):
coin_row.add(random.choice(coins).copy())
coin_row.arrange(RIGHT)
coin_row.to_edge(UP)
first_coin = get_random_coin(shuffle_time=2, total_time=2)
first_coin.move_to(coin_row[0])
brace = Brace(coin_row, UP)
brace_label = brace.get_text("$N$ times")
prob_label = TexMobject(
"P(\\# 00 = k)",
tex_to_color_map={
"00": WHITE,
"k": GREEN,
}
)
heads = get_coin("H")
template = prob_label.get_part_by_tex("00")
heads.replace(template)
prob_label.replace_submobject(
prob_label.index_of_part(template),
heads,
)
prob_label.set_height(1)
prob_label.next_to(coin_row, DOWN, LARGE_BUFF)
self.camera.frame.set_height(1.5 * FRAME_HEIGHT)
self.add(first_coin)
for x in range(4):
self.wait()
first_coin.suspend_updating()
self.wait()
first_coin.resume_updating()
self.remove(first_coin)
self.play(
ShowIncreasingSubsets(coin_row, int_func=np.ceil),
GrowFromPoint(brace, brace.get_left()),
FadeInFrom(brace_label, 3 * LEFT)
)
self.wait()
self.play(FadeIn(prob_label, lag_ratio=0.1))
self.wait()
class WriteLikelihoodFunction(Scene):
def construct(self):
formula = TexMobject(
"f({s}) = (\\text{const.})",
"{s}^{\\#" + CMARK_TEX + "}",
"(1 - {s})^{\\#" + XMARK_TEX, "}",
tex_to_color_map={
"{s}": YELLOW,
"\\#" + CMARK_TEX: GREEN,
"\\#" + XMARK_TEX: RED,
}
)
formula.scale(2)
rect1 = SurroundingRectangle(formula[3:6])
rect2 = SurroundingRectangle(formula[6:])
self.play(FadeInFromDown(formula))
self.wait()
self.play(ShowCreationThenFadeOut(rect1))
self.wait()
self.play(ShowCreationThenFadeOut(rect2))
self.wait()
self.add(formula)
self.embed()
class Guess96Percent(Scene):
def construct(self):
randy = Randolph()
randy.set_height(1)
bubble = SpeechBubble(height=2, width=3)
bubble.pin_to(randy)
words = TextMobject("96$\\%$, right?")
fix_percent(words[0][2])
bubble.add_content(words)
arrow = Vector(2 * RIGHT + DOWN)
arrow.next_to(randy, RIGHT)
arrow.shift(2 * UP)
self.play(
FadeIn(randy),
ShowCreation(bubble),
Write(words),
)
self.play(randy.change, "shruggie", randy.get_right() + RIGHT)
self.play(ShowCreation(arrow))
for x in range(2):
self.play(Blink(randy))
self.wait()
self.embed()
class LikelihoodGraphFor10of10(ShowBinomialFormula):
CONFIG = {
"histogram_config": {
"x_label_freq": 2,
"y_axis_numbers_to_show": range(25, 125, 25),
"y_max": 1,
"y_tick_freq": 0.25,
"height": 2,
"bar_colors": [BLUE],
},
}
def construct(self):
# Add histogram
dist = scipy.stats.binom(10, self.s)
data = np.array([
dist.pmf(x)
for x in range(0, 11)
])
histogram = self.get_histogram(data)
histogram.bars.set_fill(GREY_C)
histogram.bars[10].set_fill(GREEN)
histogram.to_edge(UP)
x_label = TexMobject("\\#" + CMARK_TEX)
x_label.set_color(GREEN)
x_label.next_to(histogram.axes.x_axis.get_end(), RIGHT)
histogram.add(x_label)
self.add(histogram)
arrow = Vector(DOWN)
arrow.next_to(histogram.bars[10], UP, SMALL_BUFF)
self.add(arrow)
# Add formula
prob_label = get_prob_review_label(10, 0)
eq = TexMobject("=")
formula = get_binomial_formula(10, 10, self.s)
eq2 = TexMobject("=")
value = DecimalNumber(dist.pmf(10), num_decimal_places=2)
equation = VGroup(prob_label, eq, formula, eq2, value)
equation.arrange(RIGHT)
equation.next_to(histogram, DOWN, MED_LARGE_BUFF)
# Add lower axes
low_axes = get_beta_dist_axes(y_max=1, y_unit=0.25, label_y=False)
low_axes.y_axis.set_height(
2,
about_point=low_axes.c2p(0, 0),
stretch=True,
)
low_axes.to_edge(DOWN)
low_axes.x_axis.numbers.set_color(YELLOW)
y_label_copies = histogram.axes.y_labels.copy()
y_label_copies.set_height(0.7 * low_axes.get_height())
y_label_copies.next_to(low_axes, LEFT, 0, aligned_edge=UP)
y_label_copies.shift(SMALL_BUFF * UP)
low_axes.y_axis.add(y_label_copies)
# Add lower plot
s_tracker = ValueTracker(self.s)
def func(x):
return x**10
graph = low_axes.get_graph(func, step_size=0.05)
graph.set_stroke(BLUE, 3)
v_line = Line(DOWN, UP)
v_line.axes = low_axes
v_line.st = s_tracker
v_line.graph = graph
v_line.add_updater(
lambda m: m.put_start_and_end_on(
m.axes.c2p(m.st.get_value(), 0),
m.axes.input_to_graph_point(
m.st.get_value(),
m.graph,
),
)
)
v_line.set_stroke(GREEN, 2)
dot = Dot()
dot.line = v_line
dot.set_height(0.05)
dot.add_updater(lambda m: m.move_to(m.line.get_end()))
# Show simpler formula
brace = Brace(formula, DOWN, buff=SMALL_BUFF)
simpler_formula = TexMobject("s", "^{10}")
simpler_formula.set_color_by_tex("s", YELLOW)
simpler_formula.set_color_by_tex("10", GREEN)
simpler_formula.next_to(brace, DOWN)
rects = VGroup(
BackgroundRectangle(formula[:4]),
BackgroundRectangle(formula[8:]),
)
rects.set_opacity(0.75)
self.wait()
self.play(FadeIn(equation))
self.wait()
self.play(
FadeIn(rects),
GrowFromCenter(brace),
FadeInFrom(simpler_formula, UP)
)
self.wait()
# Show various values of s
pl_decimal = DecimalNumber(self.s)
pl_decimal.set_color(YELLOW)
pl_decimal.replace(prob_label[-2][2:])
prob_label[-2][2:].set_opacity(0)
decimals = VGroup(pl_decimal, formula[5], formula[9])
decimals.s_tracker = s_tracker
histogram.s_tracker = s_tracker
histogram.n = 10
histogram.rhs_value = value
def update_decimals(decs):
for dec in decs:
dec.set_value(decs.s_tracker.get_value())
def update_histogram(hist):
new_dist = scipy.stats.binom(hist.n, hist.s_tracker.get_value())
new_data = np.array([
new_dist.pmf(x)
for x in range(0, 11)
])
new_bars = hist.get_bars(new_data)
new_bars.match_style(hist.bars)
hist.bars.become(new_bars)
hist.rhs_value.set_value(new_dist.pmf(10))
self.add(histogram)
self.add(decimals, rects)
self.play(
FadeIn(low_axes),
)
self.play(
ShowCreation(v_line),
FadeIn(dot),
)
self.add(graph, v_line, dot)
self.play(ShowCreation(graph))
self.wait()
always(arrow.next_to, histogram.bars[10], UP, SMALL_BUFF)
for s in [0.8, 1]:
self.play(
s_tracker.set_value, s,
UpdateFromFunc(decimals, update_decimals),
UpdateFromFunc(histogram, update_histogram),
UpdateFromFunc(value, lambda m: m),
run_time=5,
)
self.wait()
class StateNeedForBayesRule(TeacherStudentsScene):
def construct(self):
axes = get_beta_dist_axes(y_max=1, y_unit=0.25, label_y=False)
axes.y_axis.set_height(
2,
about_point=axes.c2p(0, 0),
stretch=True,
)
axes.set_width(5)
graph = axes.get_graph(lambda x: x**10)
graph.set_stroke(BLUE, 3)
alt_graph = graph.copy()
alt_graph.add_line_to(axes.c2p(1, 0))
alt_graph.add_line_to(axes.c2p(0, 0))
alt_graph.set_stroke(width=0)
alt_graph.set_fill(BLUE_E, 1)
plot = VGroup(axes, alt_graph, graph)
student0, student1, student2 = self.students
plot.next_to(student2.get_corner(UL), UP, MED_LARGE_BUFF)
plot.shift(LEFT)
v_lines = VGroup(
DashedLine(axes.c2p(0.8, 0), axes.c2p(0.8, 1)),
DashedLine(axes.c2p(1, 0), axes.c2p(1, 1)),
)
v_lines.set_stroke(YELLOW, 2)
self.play(
LaggedStart(
ApplyMethod(student0.change, "pondering", plot),
ApplyMethod(student1.change, "pondering", plot),
ApplyMethod(student2.change, "raise_left_hand", plot),
),
FadeInFrom(plot, DOWN),
run_time=1.5
)
self.play(*map(ShowCreation, v_lines))
self.play(
self.teacher.change, "tease",
*[
ApplyMethod(
v_line.move_to,
axes.c2p(0.9, 0),
DOWN,
)
for v_line in v_lines
]
)
self.change_student_modes(
"thinking", "thinking", "pondering",
look_at_arg=v_lines,
)
self.wait(2)
self.teacher_says(
"But first...",
added_anims=[
FadeOutAndShift(plot, LEFT),
FadeOutAndShift(v_lines, LEFT),
self.get_student_changes(
"erm", "erm", "erm",
look_at_arg=self.teacher.eyes,
)
]
)
self.wait(5)
class Part1EndScreen(PatreonEndScreen):
CONFIG = {
"specific_patrons": [
"1stViewMaths",
"Adam Dřínek",
"Aidan Shenkman",
"Alan Stein",
"Alex Mijalis",
"Alexis Olson",
"Ali Yahya",
"Andrew Busey",
"Andrew Cary",
"Andrew R. Whalley",
"Aravind C V",
"Arjun Chakroborty",
"Arthur Zey",
"Ashwin Siddarth",
"Austin Goodman",
"Avi Finkel",
"Awoo",
"Axel Ericsson",
"Ayan Doss",
"AZsorcerer",
"Barry Fam",
"Bernd Sing",
"Boris Veselinovich",
"Bradley Pirtle",
"Brandon Huang",
"Brian Staroselsky",
"Britt Selvitelle",
"Britton Finley",
"Burt Humburg",
"Calvin Lin",
"Charles Southerland",
"Charlie N",
"Chenna Kautilya",
"Chris Connett",
"Christian Kaiser",
"cinterloper",
"Clark Gaebel",
"Colwyn Fritze-Moor",
"Cooper Jones",
"Corey Ogburn",
"D. Sivakumar",
"Dan Herbatschek",
"Daniel Herrera C",
"Dave B",
"Dave Kester",
"dave nicponski",
"David B. Hill",
"David Clark",
"David Gow",
"Delton Ding",
"Dominik Wagner",
"Douglas Cantrell",
"emptymachine",
"Eric Younge",
"Eryq Ouithaqueue",
"Farzaneh Sarafraz",
"Federico Lebron",
"Frank R. Brown, Jr.",
"Giovanni Filippi",
"Hal Hildebrand",
"Hitoshi Yamauchi",
"Ivan Sorokin",
"Jacob Baxter",
"Jacob Harmon",
"Jacob Hartmann",
"Jacob Magnuson",
"Jake Vartuli - Schonberg",
"Jalex Stark",
"Jameel Syed",
"Jason Hise",
"Jayne Gabriele",
"Jean-Manuel Izaret",
"Jeff Linse",
"Jeff Straathof",
"Jimmy Yang",
"John C. Vesey",
"John Haley",
"John Le",
"John V Wertheim",
"Jonathan Heckerman",
"Jonathan Wilson",
"Joseph John Cox",
"Joseph Kelly",
"Josh Kinnear",
"Joshua Claeys",
"Juan Benet",
"Kai-Siang Ang",
"Kanan Gill",
"Karl Niu",
"Kartik Cating-Subramanian",
"Kaustuv DeBiswas",
"Killian McGuinness",
"Kros Dai",
"L0j1k",
"LAI Oscar",
"Lambda GPU Workstations",
"Lee Redden",
"Linh Tran",
"Luc Ritchie",
"Ludwig Schubert",
"Lukas Biewald",
"Magister Mugit",
"Magnus Dahlström",
"Manoj Rewatkar - RITEK SOLUTIONS",
"Mark B Bahu",
"Mark Heising",
"Mark Mann",
"Martin Price",
"Mathias Jansson",
"Matt Godbolt",
"Matt Langford",
"Matt Roveto",
"Matt Russell",
"Matteo Delabre",
"Matthew Bouchard",
"Matthew Cocke",
"Mia Parent",
"Michael Hardel",
"Michael W White",
"Mirik Gogri",
"Mustafa Mahdi",
"Márton Vaitkus",
"Nicholas Cahill",
"Nikita Lesnikov",
"Oleg Leonov",
"Oliver Steele",
"Omar Zrien",
"Owen Campbell-Moore",
"Patrick Lucas",
"Pavel Dubov",
"Peter Ehrnstrom",
"Peter Mcinerney",
"Pierre Lancien",
"Quantopian",
"Randy C. Will",
"rehmi post",
"Rex Godby",
"Ripta Pasay",
"Rish Kundalia",
"Roman Sergeychik",
"Roobie",
"Ryan Atallah",
"Samuel Judge",
"SansWord Huang",
"Scott Gray",
"Scott Walter, Ph.D.",
"soekul",
"Solara570",
"Steve Huynh",
"Steve Sperandeo",
"Steven Braun",
"Steven Siddals",
"Stevie Metke",
"supershabam",
"Suteerth Vishnu",
"Suthen Thomas",
"Tal Einav",
"Taras Bobrovytsky",
"Tauba Auerbach",
"Ted Suzman",
"Thomas J Sargent",
"Thomas Tarler",
"Tianyu Ge",
"Tihan Seale",
"Tyler VanValkenburg",
"Vassili Philippov",
"Veritasium",
"Vignesh Ganapathi Subramanian",
"Vinicius Reis",
"Xuanji Li",
"Yana Chernobilsky",
"Yavor Ivanov",
"YinYangBalance.Asia",
"Yu Jun",
"Yurii Monastyrshyn",
],
}