mirror of
https://github.com/3b1b/manim.git
synced 2025-07-31 22:13:30 +08:00
1303 lines
38 KiB
Python
1303 lines
38 KiB
Python
from big_ol_pile_of_manim_imports import *
|
|
from old_projects.eoc.chapter8 import *
|
|
from active_projects.eop.histograms import *
|
|
|
|
import scipy.special
|
|
|
|
COIN_RADIUS = 0.3
|
|
COIN_THICKNESS = 0.4 * COIN_RADIUS
|
|
COIN_FORESHORTENING = 0.3
|
|
COIN_NB_RIDGES = 20
|
|
COIN_STROKE_WIDTH = 2
|
|
|
|
COIN_SEQUENCE_SPACING = 0.1
|
|
|
|
GRADE_COLOR_1 = COLOR_HEADS = RED
|
|
GRADE_COLOR_2 = COLOR_TAILS = BLUE
|
|
|
|
|
|
def binary(i):
|
|
# returns an array of 0s and 1s
|
|
if i == 0:
|
|
return []
|
|
j = i
|
|
binary_array = []
|
|
while j > 0:
|
|
jj = j/2
|
|
if jj > 0:
|
|
binary_array.append(j % 2)
|
|
else:
|
|
binary_array.append(1)
|
|
j = jj
|
|
return binary_array[::-1]
|
|
|
|
def nb_of_ones(i):
|
|
return binary(i).count(1)
|
|
|
|
class PiCreatureCoin(VMobject):
|
|
CONFIG = {
|
|
"diameter": 0.8,
|
|
"thickness": 0.2,
|
|
"nb_ridges" : 7,
|
|
"stroke_color": YELLOW,
|
|
"stroke_width": 3,
|
|
"fill_color": YELLOW,
|
|
"fill_opacity": 0.7,
|
|
}
|
|
|
|
def generate_points(self):
|
|
outer_rect = Rectangle(
|
|
width = self.diameter,
|
|
height = self.thickness,
|
|
fill_color = self.fill_color,
|
|
fill_opacity = self.fill_opacity,
|
|
stroke_color = self.stroke_color,
|
|
stroke_width = 0, #self.stroke_width
|
|
)
|
|
self.add(outer_rect)
|
|
PI = TAU/2
|
|
ridge_angles = np.arange(PI/self.nb_ridges,PI,PI/self.nb_ridges)
|
|
ridge_positions = 0.5 * self.diameter * np.array([
|
|
np.cos(theta) for theta in ridge_angles
|
|
])
|
|
ridge_color = interpolate_color(BLACK,self.stroke_color,0.5)
|
|
for x in ridge_positions:
|
|
ridge = Line(
|
|
x * RIGHT + 0.5 * self.thickness * DOWN,
|
|
x * RIGHT + 0.5 * self.thickness * UP,
|
|
stroke_color = ridge_color,
|
|
stroke_width = self.stroke_width
|
|
)
|
|
self.add(ridge)
|
|
|
|
class CoinFlippingPiCreature(PiCreature):
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
coin = PiCreatureCoin()
|
|
PiCreature.__init__(self,**kwargs)
|
|
self.coin = coin
|
|
self.add(coin)
|
|
right_arm = self.get_arm_copies()[1]
|
|
coin.next_to(right_arm, RIGHT+UP, buff = 0)
|
|
coin.shift(0.15 * self.get_width() * LEFT)
|
|
|
|
def flip_coin_up(self):
|
|
self.change("raise_right_hand")
|
|
|
|
class FlipUpAndDown(Animation):
|
|
CONFIG = {
|
|
"vector" : UP,
|
|
"nb_turns" : 1
|
|
}
|
|
|
|
def update(self,t):
|
|
self.mobject.shift(4 * t * (1 - t) * self.vector)
|
|
self.mobject.rotate(t * self.nb_turns * TAU)
|
|
|
|
class FlipCoin(AnimationGroup):
|
|
CONFIG = {
|
|
"rate_func" : there_and_back
|
|
}
|
|
def __init__(self, pi_creature, **kwargs):
|
|
digest_config(self, kwargs)
|
|
pi_creature_motion = ApplyMethod(
|
|
pi_creature.flip_coin_up,
|
|
rate_func = self.rate_func,
|
|
**kwargs
|
|
)
|
|
coin_motion = Succession(
|
|
EmptyAnimation(run_time = 1.0),
|
|
FlipUpAndDown(
|
|
pi_creature.coin,
|
|
vector = UP,
|
|
nb_turns = 5,
|
|
rate_func = self.rate_func,
|
|
**kwargs
|
|
)
|
|
)
|
|
AnimationGroup.__init__(self,pi_creature_motion, coin_motion)
|
|
|
|
class CoinFlippingPiCreatureScene(Scene):
|
|
|
|
def construct(self):
|
|
|
|
randy = CoinFlippingPiCreature()
|
|
self.add(randy)
|
|
self.play(FlipCoin(randy, run_time = 3))
|
|
|
|
|
|
class UprightCoin(Circle):
|
|
# For use in coin sequences
|
|
CONFIG = {
|
|
"radius": COIN_RADIUS,
|
|
"stroke_width": COIN_STROKE_WIDTH,
|
|
"stroke_color": WHITE,
|
|
"fill_opacity": 1,
|
|
"symbol": "\euro"
|
|
}
|
|
|
|
def __init__(self, **kwargs):
|
|
Circle.__init__(self,**kwargs)
|
|
self.symbol_mob = TextMobject(self.symbol, stroke_color = self.stroke_color)
|
|
self.symbol_mob.scale_to_fit_height(0.5*self.get_height()).move_to(self)
|
|
self.add(self.symbol_mob)
|
|
|
|
class UprightHeads(UprightCoin):
|
|
CONFIG = {
|
|
"fill_color": COLOR_HEADS,
|
|
"symbol": "H",
|
|
}
|
|
|
|
class UprightTails(UprightCoin):
|
|
CONFIG = {
|
|
"fill_color": COLOR_TAILS,
|
|
"symbol": "T",
|
|
}
|
|
|
|
class CoinSequence(VGroup):
|
|
CONFIG = {
|
|
"sequence": [],
|
|
"spacing": COIN_SEQUENCE_SPACING
|
|
}
|
|
|
|
def __init__(self, sequence, **kwargs):
|
|
VGroup.__init__(self, **kwargs)
|
|
self.sequence = sequence
|
|
offset = 0
|
|
for symbol in self.sequence:
|
|
if symbol == "H":
|
|
new_coin = UprightHeads()
|
|
elif symbol == "T":
|
|
new_coin = UprightTails()
|
|
else:
|
|
new_coin = UprightCoin(symbol = symbol)
|
|
new_coin.shift(offset * RIGHT)
|
|
self.add(new_coin)
|
|
offset += self.spacing
|
|
|
|
class FlatCoin(UprightCoin):
|
|
# For use in coin stacks
|
|
CONFIG = {
|
|
"thickness": COIN_THICKNESS,
|
|
"foreshortening": COIN_FORESHORTENING,
|
|
"nb_ridges": COIN_NB_RIDGES
|
|
}
|
|
|
|
def __init__(self, **kwargs):
|
|
UprightCoin.__init__(self, **kwargs)
|
|
self.symbol_mob.rotate(TAU/8)
|
|
self.stretch_in_place(self.foreshortening, 1)
|
|
|
|
# draw the edge
|
|
control_points1 = self.points[12:25].tolist()
|
|
control_points2 = self.copy().shift(self.thickness * DOWN).points[12:25].tolist()
|
|
edge_anchors_and_handles = control_points1
|
|
edge_anchors_and_handles.append(edge_anchors_and_handles[-1] + self.thickness * DOWN)
|
|
edge_anchors_and_handles.append(edge_anchors_and_handles[-1] + self.thickness * UP)
|
|
edge_anchors_and_handles += control_points2[::-1] # list concatenation
|
|
edge_anchors_and_handles.append(edge_anchors_and_handles[-1] + self.thickness * UP)
|
|
edge_anchors_and_handles.append(edge_anchors_and_handles[-1] + self.thickness * DOWN)
|
|
edge_anchors_and_handles.append(control_points1[0])
|
|
#edge_anchors_and_handles = edge_anchors_and_handles[::-1]
|
|
edge = VMobject()
|
|
edge.set_points(edge_anchors_and_handles)
|
|
edge.set_fill(
|
|
color = self.fill_color,
|
|
opacity = self.fill_opacity
|
|
)
|
|
edge.set_stroke(width = self.stroke_width)
|
|
self.add(edge)
|
|
|
|
# draw the ridges
|
|
PI = TAU/2
|
|
dtheta = PI/self.nb_ridges
|
|
ridge_angles = np.arange(dtheta,PI,dtheta)
|
|
# add a twist onto each coin
|
|
ridge_angles += np.random.rand(1) * dtheta
|
|
# crop the angles that overshoot on either side
|
|
ridge_angles = ridge_angles[(ridge_angles > 0) * (ridge_angles < PI)]
|
|
ridge_positions = 0.5 * 2 * self.radius * np.array([
|
|
np.cos(theta) for theta in ridge_angles
|
|
])
|
|
ridge_color = interpolate_color(self.stroke_color, self.fill_color, 0.7)
|
|
for x in ridge_positions:
|
|
y = -(1 - (x/self.radius)**2)**0.5 * self.foreshortening * self.radius
|
|
ridge = Line(
|
|
x * RIGHT + y * UP,
|
|
x * RIGHT + y * UP + self.thickness * DOWN,
|
|
stroke_color = ridge_color,
|
|
stroke_width = self.stroke_width
|
|
)
|
|
self.add(ridge)
|
|
|
|
# redraw the unfilled edge to cover the ridge ends
|
|
empty_edge = edge.copy()
|
|
empty_edge.set_fill(opacity = 0)
|
|
self.add(empty_edge)
|
|
|
|
class FlatHeads(FlatCoin):
|
|
CONFIG = {
|
|
"fill_color": COLOR_HEADS,
|
|
"symbol": "H",
|
|
}
|
|
|
|
class FlatTails(FlatCoin):
|
|
CONFIG = {
|
|
"fill_color": COLOR_TAILS,
|
|
"symbol": "T",
|
|
}
|
|
|
|
class CoinStack(VGroup):
|
|
CONFIG = {
|
|
"coin_thickness": COIN_THICKNESS,
|
|
"size": 5,
|
|
"face": FlatCoin,
|
|
}
|
|
|
|
def generate_points(self):
|
|
for n in range(self.size):
|
|
coin = self.face(thickness = self.coin_thickness)
|
|
coin.shift(n * self.coin_thickness * UP)
|
|
self.add(coin)
|
|
|
|
class HeadsStack(CoinStack):
|
|
CONFIG = {
|
|
"face": FlatHeads
|
|
}
|
|
|
|
class TailsStack(CoinStack):
|
|
CONFIG = {
|
|
"face": FlatTails
|
|
}
|
|
|
|
class TallyStack(VGroup):
|
|
CONFIG = {
|
|
"coin_thickness": COIN_THICKNESS
|
|
}
|
|
|
|
def __init__(self,h,t,anchor = ORIGIN, **kwargs):
|
|
self.nb_heads = h
|
|
self.nb_tails = t
|
|
self.anchor = anchor
|
|
VGroup.__init__(self,**kwargs)
|
|
|
|
def generate_points(self):
|
|
stack1 = HeadsStack(size = self.nb_heads, coin_thickness = self.coin_thickness)
|
|
stack2 = TailsStack(size = self.nb_tails, coin_thickness = self.coin_thickness)
|
|
stack1.next_to(self.anchor, LEFT, buff = SMALL_BUFF)
|
|
stack2.next_to(self.anchor, RIGHT, buff = SMALL_BUFF)
|
|
stack1.align_to(self.anchor, DOWN)
|
|
stack2.align_to(self.anchor, DOWN)
|
|
self.heads_stack = stack1
|
|
self.tails_stack = stack2
|
|
self.add(stack1, stack2)
|
|
|
|
def move_anchor_to(self, new_anchor):
|
|
for submob in self.submobjects:
|
|
submob.shift(new_anchor - self.anchor)
|
|
self.anchor = new_anchor
|
|
return self
|
|
|
|
class CoinFlipTree(VGroup):
|
|
CONFIG = {
|
|
"total_width": 12,
|
|
"level_height": 0.8,
|
|
"nb_levels": 4,
|
|
"sort_until_level": 3
|
|
}
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
VGroup.__init__(self, **kwargs)
|
|
|
|
self.rows = []
|
|
for n in range(self.nb_levels + 1):
|
|
if n <= self.sort_until_level:
|
|
self.create_row(n, sorted = True)
|
|
else:
|
|
self.create_row(n, sorted = False)
|
|
|
|
|
|
for row in self.rows:
|
|
for leaf in row:
|
|
dot = Dot()
|
|
dot.move_to(leaf[0])
|
|
line = Line(leaf[2], leaf[0])
|
|
if leaf[2][0] > leaf[0][0]:
|
|
line_color = COLOR_HEADS
|
|
else:
|
|
line_color = COLOR_TAILS
|
|
line.set_stroke(color = line_color)
|
|
group = VGroup()
|
|
group.add(dot)
|
|
group.add_to_back(line)
|
|
self.add(group)
|
|
|
|
|
|
|
|
|
|
def create_row(self, level, sorted = True):
|
|
|
|
if level == 0:
|
|
new_row = [[ORIGIN,0,ORIGIN]] # is its own parent
|
|
self.rows.append(new_row)
|
|
return
|
|
|
|
previous_row = self.rows[level - 1]
|
|
new_row = []
|
|
dx = float(self.total_width) / (2 ** level)
|
|
x = - 0.5 * self.total_width + 0.5 * dx
|
|
y = - self.level_height * level
|
|
for root in previous_row:
|
|
root_point = root[0]
|
|
root_tally = root[1]
|
|
for i in range(2): # 0 = heads = left, 1 = tails = right
|
|
leaf = x * RIGHT + y * UP
|
|
new_row.append([leaf, root_tally + i, root_point]) # leaf and its parent
|
|
x += dx
|
|
|
|
#print "tallies for row", level, ":", [new_row[i][1] for i in range(2**level)]
|
|
|
|
if sorted:
|
|
# sort the new_row by its tallies
|
|
sorted_row = []
|
|
x = - 0.5 * self.total_width + 0.5 * dx
|
|
for i in range(level + 1):
|
|
for leaf in new_row:
|
|
if leaf[1] == i:
|
|
sorted_leaf = leaf
|
|
sorted_leaf[0][0] = x
|
|
x += dx
|
|
sorted_row.append(leaf)
|
|
print "sorted roots:", [sorted_row[i][2][0] for i in range(2**level)]
|
|
self.rows.append(sorted_row)
|
|
else:
|
|
self.rows.append(new_row)
|
|
|
|
|
|
|
|
|
|
|
|
class Chapter1OpeningQuote(OpeningQuote):
|
|
CONFIG = {
|
|
"fade_in_kwargs": {
|
|
"submobject_mode": "lagged_start",
|
|
"rate_func": None,
|
|
"lag_factor": 9,
|
|
"run_time": 10,
|
|
},
|
|
"text_size" : "\\normalsize",
|
|
"use_quotation_marks": False,
|
|
"quote" : [
|
|
"To see a world in a grain of sand\\\\",
|
|
"And a heaven in a wild flower,\\\\",
|
|
"Hold infinity in the palm of your hand\\\\",
|
|
"\phantom{r}And eternity in an hour.\\\\"
|
|
],
|
|
"quote_arg_separator" : " ",
|
|
"highlighted_quote_terms" : {},
|
|
"author" : "William Blake: \\\\ \emph{Auguries of Innocence}",
|
|
}
|
|
|
|
class Introduction(TeacherStudentsScene):
|
|
|
|
CONFIG = {
|
|
"default_pi_creature_kwargs": {
|
|
"color": MAROON_E,
|
|
"flip_at_start": True,
|
|
},
|
|
}
|
|
|
|
def construct(self):
|
|
self.show_series()
|
|
self.show_area_model1()
|
|
|
|
def show_series(self):
|
|
series = VideoSeries()
|
|
series.to_edge(UP)
|
|
this_video = series[0]
|
|
this_video.set_color(YELLOW)
|
|
this_video.save_state()
|
|
this_video.set_fill(opacity = 0)
|
|
this_video.center()
|
|
this_video.scale_to_fit_height(FRAME_HEIGHT)
|
|
self.this_video = this_video
|
|
|
|
|
|
words = TextMobject(
|
|
"Welcome to \\\\",
|
|
"Essence of Probability"
|
|
)
|
|
words.set_color_by_tex("Essence of Probability", YELLOW)
|
|
|
|
self.teacher.change_mode("happy")
|
|
self.play(
|
|
FadeIn(
|
|
series,
|
|
submobject_mode = "lagged_start",
|
|
run_time = 2
|
|
),
|
|
Blink(self.get_teacher())
|
|
)
|
|
self.teacher_says(words, target_mode = "hooray")
|
|
self.change_student_modes(
|
|
*["hooray"]*3,
|
|
look_at_arg = series[1].get_left(),
|
|
added_anims = [
|
|
ApplyMethod(this_video.restore, run_time = 3),
|
|
]
|
|
)
|
|
self.play(*[
|
|
ApplyMethod(
|
|
video.shift, 0.5*video.get_height()*DOWN,
|
|
run_time = 3,
|
|
rate_func = squish_rate_func(
|
|
there_and_back, alpha, alpha+0.3
|
|
)
|
|
)
|
|
for video, alpha in zip(series, np.linspace(0, 0.7, len(series)))
|
|
]+[
|
|
Animation(self.teacher.bubble),
|
|
Animation(self.teacher.bubble.content),
|
|
])
|
|
|
|
self.play(
|
|
FadeOut(self.teacher.bubble),
|
|
FadeOut(self.teacher.bubble.content),
|
|
*[
|
|
ApplyMethod(pi.change_mode, "pondering")
|
|
for pi in self.get_pi_creatures()
|
|
]
|
|
)
|
|
self.wait()
|
|
|
|
self.series = series
|
|
|
|
# # # # # # # # # # # # # # # # # #
|
|
# show examples of the area model #
|
|
# # # # # # # # # # # # # # # # # #
|
|
|
|
|
|
def show_area_model1(self):
|
|
|
|
# show independent events
|
|
|
|
sample_space_width = sample_space_height = 2.5
|
|
p_of_A = 0.7
|
|
p_of_not_A = 1 - p_of_A
|
|
p_of_B = 0.8
|
|
p_of_not_B = 1 - p_of_B
|
|
|
|
|
|
rect_A = Rectangle(
|
|
width = p_of_A * sample_space_width,
|
|
height = 1 * sample_space_height,
|
|
stroke_width = 0,
|
|
fill_color = BLUE,
|
|
fill_opacity = 1.0
|
|
).move_to(2 * RIGHT + 1.5 * UP)
|
|
|
|
rect_not_A = Rectangle(
|
|
width = p_of_not_A * sample_space_width,
|
|
height = 1 * sample_space_height,
|
|
stroke_width = 0,
|
|
fill_color = BLUE_E,
|
|
fill_opacity = 1.0
|
|
).next_to(rect_A, RIGHT, buff = 0)
|
|
|
|
brace_A = Brace(rect_A, DOWN)
|
|
label_A = TexMobject("P(A)").next_to(brace_A, DOWN).scale(0.7)
|
|
brace_not_A = Brace(rect_not_A, DOWN)
|
|
label_not_A = TexMobject("P(\\text{not }A)").next_to(brace_not_A, DOWN).scale(0.7)
|
|
|
|
# self.play(
|
|
# LaggedStart(FadeIn, VGroup(rect_A, rect_not_A), lag_factor = 0.5)
|
|
# )
|
|
# self.play(
|
|
# ShowCreation(brace_A),
|
|
# Write(label_A),
|
|
# )
|
|
|
|
|
|
|
|
rect_B = Rectangle(
|
|
width = 1 * sample_space_width,
|
|
height = p_of_B * sample_space_height,
|
|
stroke_width = 0,
|
|
fill_color = GREEN,
|
|
fill_opacity = 0.5
|
|
)
|
|
rect_not_B = Rectangle(
|
|
width = 1 * sample_space_width,
|
|
height = p_of_not_B * sample_space_height,
|
|
stroke_width = 0,
|
|
fill_color = GREEN_E,
|
|
fill_opacity = 0.5
|
|
).next_to(rect_B, UP, buff = 0)
|
|
|
|
VGroup(rect_B, rect_not_B).move_to(VGroup(rect_A, rect_not_A))
|
|
|
|
brace_B = Brace(rect_B, LEFT)
|
|
label_B = TexMobject("P(B)").next_to(brace_B, LEFT).scale(0.7)
|
|
brace_not_B = Brace(rect_not_B, LEFT)
|
|
label_not_B = TexMobject("P(\\text{not }B)").next_to(brace_not_B, LEFT).scale(0.7)
|
|
|
|
# self.play(
|
|
# LaggedStart(FadeIn, VGroup(rect_B, rect_not_B), lag_factor = 0.5)
|
|
# )
|
|
# self.play(
|
|
# ShowCreation(brace_B),
|
|
# Write(label_B),
|
|
# )
|
|
|
|
rect_A_and_B = Rectangle(
|
|
width = p_of_A * sample_space_width,
|
|
height = p_of_B * sample_space_height,
|
|
stroke_width = 3,
|
|
fill_opacity = 0.0
|
|
).align_to(rect_A, DOWN).align_to(rect_A,LEFT)
|
|
label_A_and_B = TexMobject("P(A\\text{ and }B)").scale(0.7)
|
|
label_A_and_B.move_to(rect_A_and_B)
|
|
|
|
# self.play(
|
|
# ShowCreation(rect_A_and_B)
|
|
# )
|
|
|
|
indep_formula = TexMobject("P(A\\text{ and }B)", "=", "P(A)", "\cdot", "P(B)")
|
|
indep_formula = indep_formula.scale(0.7).next_to(rect_not_B, UP, buff = MED_LARGE_BUFF)
|
|
|
|
label_A_and_B_copy = label_A_and_B.copy()
|
|
label_A_copy = label_A.copy()
|
|
label_B_copy = label_B.copy()
|
|
# self.add(label_A_and_B_copy, label_A_copy, label_B_copy)
|
|
|
|
# self.play(Transform(label_A_and_B_copy, indep_formula[0]))
|
|
# self.play(FadeIn(indep_formula[1]))
|
|
# self.play(Transform(label_A_copy, indep_formula[2]))
|
|
# self.play(FadeIn(indep_formula[3]))
|
|
# self.play(Transform(label_B_copy, indep_formula[4]))
|
|
|
|
#self.wait()
|
|
|
|
label_A_and_B_copy = indep_formula[0]
|
|
label_A_copy = indep_formula[2]
|
|
label_B_copy = indep_formula[4]
|
|
|
|
# show conditional prob
|
|
|
|
rect_A_and_B.set_fill(color = GREEN, opacity = 0.5)
|
|
rect_A_and_not_B = Rectangle(
|
|
width = p_of_A * sample_space_width,
|
|
height = p_of_not_B * sample_space_height,
|
|
stroke_width = 0,
|
|
fill_color = GREEN_E,
|
|
fill_opacity = 0.5
|
|
).next_to(rect_A_and_B, UP, buff = 0)
|
|
|
|
rect_not_A_and_B = Rectangle(
|
|
width = p_of_not_A * sample_space_width,
|
|
height = p_of_B * sample_space_height,
|
|
stroke_width = 0,
|
|
fill_color = GREEN,
|
|
fill_opacity = 0.5
|
|
).next_to(rect_A_and_B, RIGHT, buff = 0)
|
|
|
|
rect_not_A_and_not_B = Rectangle(
|
|
width = p_of_not_A * sample_space_width,
|
|
height = p_of_not_B * sample_space_height,
|
|
stroke_width = 0,
|
|
fill_color = GREEN_E,
|
|
fill_opacity = 0.5
|
|
).next_to(rect_not_A_and_B, UP, buff = 0)
|
|
|
|
|
|
indep_formula.next_to(rect_not_A, LEFT, buff = 4)
|
|
indep_formula.shift(UP)
|
|
|
|
self.play(Write(indep_formula))
|
|
|
|
self.play(
|
|
FadeIn(VGroup(
|
|
rect_A, rect_not_A, brace_A, label_A, brace_B, label_B,
|
|
rect_A_and_not_B, rect_not_A_and_B, rect_not_A_and_not_B,
|
|
rect_A_and_B,
|
|
label_A_and_B,
|
|
))
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
p_of_B_knowing_A = 0.6
|
|
rect_A_and_B.target = Rectangle(
|
|
width = p_of_A * sample_space_width,
|
|
height = p_of_B_knowing_A * sample_space_height,
|
|
stroke_width = 3,
|
|
fill_color = GREEN,
|
|
fill_opacity = 0.5
|
|
).align_to(rect_A_and_B, DOWN).align_to(rect_A_and_B, LEFT)
|
|
|
|
rect_A_and_not_B.target = Rectangle(
|
|
width = p_of_A * sample_space_width,
|
|
height = (1 - p_of_B_knowing_A) * sample_space_height,
|
|
stroke_width = 0,
|
|
fill_color = GREEN_E,
|
|
fill_opacity = 0.5
|
|
).next_to(rect_A_and_B.target, UP, buff = 0)
|
|
|
|
brace_B.target = Brace(rect_A_and_B.target, LEFT)
|
|
label_B.target = TexMobject("P(B\mid A)").scale(0.7).next_to(brace_B.target, LEFT)
|
|
|
|
|
|
self.play(
|
|
MoveToTarget(rect_A_and_B),
|
|
MoveToTarget(rect_A_and_not_B),
|
|
MoveToTarget(brace_B),
|
|
MoveToTarget(label_B),
|
|
label_A_and_B.move_to,rect_A_and_B.target
|
|
)
|
|
label_B_knowing_A = label_B
|
|
|
|
#self.play(FadeOut(label_B_copy))
|
|
self.remove(indep_formula.get_part_by_tex("P(B)"))
|
|
label_B_knowing_A_copy = label_B_knowing_A.copy()
|
|
self.add(label_B_knowing_A_copy)
|
|
|
|
self.play(
|
|
label_B_knowing_A_copy.next_to, indep_formula.get_part_by_tex("\cdot"), RIGHT,
|
|
)
|
|
|
|
# solve formula for P(B|A)
|
|
|
|
rearranged_formula = TexMobject(["P(B\mid A)", "=", "{P(A\\text{ and }B) \over P(A)}"])
|
|
rearranged_formula.move_to(indep_formula)
|
|
|
|
self.wait()
|
|
|
|
|
|
self.play(
|
|
# in some places get_part_by_tex does not find the correct part
|
|
# so I picked out fitting indices
|
|
label_B_knowing_A_copy.move_to, rearranged_formula.get_part_by_tex("P(B\mid A)"),
|
|
label_A_copy.move_to, rearranged_formula[-1][10],
|
|
label_A_and_B_copy.move_to, rearranged_formula[-1][3],
|
|
indep_formula.get_part_by_tex("=").move_to, rearranged_formula.get_part_by_tex("="),
|
|
Transform(indep_formula.get_part_by_tex("\cdot"), rearranged_formula[2][6]),
|
|
)
|
|
|
|
|
|
self.play(
|
|
FadeOut(VGroup(
|
|
indep_formula, rect_A, rect_B, rect_not_A, rect_not_B,
|
|
rect_A_and_B, rect_A_and_not_B, rect_not_A_and_B, rect_not_A_and_not_B,
|
|
brace_A, brace_B, label_A, label_B_knowing_A, label_A_and_B,
|
|
label_B_knowing_A_copy
|
|
))
|
|
)
|
|
|
|
|
|
# # # # # # # # # # # # # # # # #
|
|
# Old version with SampleSpace #
|
|
# # # # # # # # # # # # # # # # #
|
|
|
|
# def show_independent_events(self):
|
|
# sample_space = SampleSpace(
|
|
# full_space_config = {
|
|
# "height" : 3,
|
|
# "width" : 3,
|
|
# "fill_opacity" : 0
|
|
# }
|
|
# )
|
|
# sample_space.divide_horizontally(0.4)
|
|
# sample_space.horizontal_parts.set_fill(opacity = 0)
|
|
# h_labels = [
|
|
# TexMobject("P(", "A", ")"),
|
|
# TexMobject("P(\\text{not }", "A", ")"),
|
|
# ]
|
|
# for label in h_labels:
|
|
# label.scale(0.7)
|
|
# #self.color_label(label)
|
|
# sample_space.get_side_braces_and_labels(h_labels)
|
|
# sample_space.add_braces_and_labels()
|
|
# h_parts = sample_space.horizontal_parts
|
|
# for (label, part) in zip(h_labels, h_parts):
|
|
# label.next_to(part, 2 * LEFT)
|
|
# sample_space.add(label)
|
|
|
|
# values = [0.2, 0.2]
|
|
# color_pairs = [(GREEN, BLUE), (GREEN_E, BLUE_E)]
|
|
# v_parts = VGroup()
|
|
# for tup in zip(h_parts, values, color_pairs):
|
|
# part, value, colors = tup
|
|
# part.divide_vertically(value, colors = colors)
|
|
# part.vertical_parts.set_fill(opacity = 0.8)
|
|
# #label = TexMobject(
|
|
# # "P(", "B", "|", given_str, "A", ")"
|
|
# #)
|
|
# #label.scale(0.7)
|
|
# #self.color_label(label)
|
|
# if part == h_parts[0]:
|
|
# part.get_subdivision_braces_and_labels(
|
|
# part.vertical_parts, [label], DOWN
|
|
# )
|
|
# sample_space.add(
|
|
# part.vertical_parts.braces,
|
|
# # part.vertical_parts.labels,
|
|
# )
|
|
# v_parts.add(part.vertical_parts.submobjects)
|
|
|
|
|
|
# v_labels = [
|
|
# TexMobject("P(", "B", ")"),
|
|
# TexMobject("P(\\text{not }", "B", ")"),
|
|
# ]
|
|
# for (label, part) in zip(v_labels, v_parts[1::2]):
|
|
# label.scale(0.7)
|
|
# label.next_to(part, DOWN)
|
|
# sample_space.add(label)
|
|
|
|
|
|
# sample_space.to_edge(LEFT)
|
|
|
|
# self.add(sample_space)
|
|
# self.sample_space = sample_space
|
|
|
|
# self.wait()
|
|
|
|
|
|
|
|
|
|
|
|
# def color_label(self, label):
|
|
# label.set_color_by_tex("B", RED)
|
|
# label.set_color_by_tex("I", GREEN)
|
|
|
|
|
|
|
|
|
|
class IllustrateAreaModel2(GraphScene):
|
|
CONFIG = {
|
|
"x_min" : -3.5,
|
|
"x_max" : 3.5,
|
|
"y_min" : -0,
|
|
"y_max" : 0.6,
|
|
"graph_origin": 3*DOWN,
|
|
"num_rects": 20,
|
|
"y_axis_label" : "",
|
|
"x_axis_label" : "",
|
|
"variable_point_label" : "x",
|
|
"y_axis_height" : 4,
|
|
"graph_origin": 2.5 * DOWN + 3 * LEFT,
|
|
"x_axis_width": 5,
|
|
"y_axis_height": 3
|
|
}
|
|
|
|
def construct(self):
|
|
|
|
x_max_1 = 0
|
|
x_min_1 = -x_max_1
|
|
|
|
x_max_2 = 3.5
|
|
x_min_2 = -x_max_2
|
|
|
|
|
|
self.setup_axes()
|
|
graph = self.get_graph(lambda x: np.exp(-x**2) / ((0.5 * TAU) ** 0.5))
|
|
|
|
self.add(graph)
|
|
|
|
cdf_formula = TexMobject("P(|X-\mu| < x) = \int_{-x}^x {\exp(-{1\over 2}({t\over \sigma})^2) \over \sigma\sqrt{2\pi}} dt")
|
|
|
|
cdf_formula.set_color_by_tex("x", YELLOW)
|
|
cdf_formula.next_to(graph, RIGHT, buff = -1)
|
|
self.add(cdf_formula)
|
|
|
|
|
|
self.v_graph = graph
|
|
self.add_T_label(x_min_1, color = YELLOW, animated = False)
|
|
|
|
self.remove(self.T_label_group, self.right_v_line)
|
|
#self.T_label_group[0].set_fill(opacity = 0).set_stroke(width = 0)
|
|
#self.T_label_group[1].set_fill(opacity = 0).set_stroke(width = 0)
|
|
#self.right_v_line.set_fill(opacity = 0).set_stroke(width = 0)
|
|
|
|
#self.add(self.T_label_group)
|
|
area = self.area = self.get_area(graph, x_min_1, x_max_1)
|
|
|
|
right_bound_label = TexMobject("x", color = YELLOW)
|
|
right_bound_label.next_to(self.coords_to_point(0,0), DOWN)
|
|
right_bound_label.target = right_bound_label.copy().next_to(self.coords_to_point(self.x_max,0), DOWN)
|
|
right_bound_label.set_fill(opacity = 0).set_stroke(width = 0)
|
|
|
|
left_bound_label = TexMobject("-x", color = YELLOW)
|
|
left_bound_label.next_to(self.coords_to_point(0,0), DOWN)
|
|
left_bound_label.target = right_bound_label.copy().next_to(self.coords_to_point(self.x_min,0), DOWN)
|
|
left_bound_label.set_fill(opacity = 0).set_stroke(width = 0)
|
|
|
|
#integral = self.get_riemann_rectangles(
|
|
#graph,x_min = self.x_min, x_max = x_max_1)
|
|
self.add(area)
|
|
|
|
def integral_update_func(t):
|
|
return 100 * scipy.special.erf(
|
|
self.point_to_coords(self.right_v_line.get_center())[0]
|
|
)
|
|
|
|
cdf_value = DecimalNumber(0, unit = "\%")
|
|
cdf_value.move_to(self.coords_to_point(0,0.2))
|
|
self.add_foreground_mobject(cdf_value)
|
|
|
|
self.add(ContinualChangingDecimal(
|
|
decimal_number_mobject = cdf_value,
|
|
number_update_func = integral_update_func,
|
|
num_decimal_points = 1
|
|
))
|
|
|
|
anim = self.get_animation_integral_bounds_change(
|
|
graph, x_min_2, x_max_2, run_time = 3)
|
|
|
|
# changing_cdf_value = ChangingDecimal(
|
|
# decimal_number_mobject = cdf_value,
|
|
# number_update_func = integral_update_func,
|
|
# num_decimal_points = 1
|
|
# )
|
|
|
|
self.play(
|
|
anim
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class IllustrateAreaModel3(Scene):
|
|
|
|
def construct(self):
|
|
|
|
formula = TexMobject("E[X] = \sum_{i=1}^N p_i x_i").move_to(3 * LEFT + UP)
|
|
self.add(formula)
|
|
|
|
|
|
x_scale = 5.0
|
|
y_scale = 1.0
|
|
|
|
probabilities = np.array([1./8, 3./8, 3./8, 1./8])
|
|
prob_strings = ["{1\over 8}","{3\over 8}","{3\over 8}","{1\over 8}"]
|
|
cumulative_probabilities = np.cumsum(probabilities)
|
|
cumulative_probabilities = np.insert(cumulative_probabilities, 0, 0)
|
|
print cumulative_probabilities
|
|
y_values = np.array([0, 1, 2, 3])
|
|
|
|
hist = Histogram(probabilities, y_values,
|
|
mode = "widths",
|
|
x_scale = x_scale,
|
|
y_scale = y_scale,
|
|
x_labels = "none"
|
|
)
|
|
|
|
flat_hist = Histogram(probabilities, 0 * y_values,
|
|
mode = "widths",
|
|
x_scale = x_scale,
|
|
y_scale = y_scale,
|
|
x_labels = "none"
|
|
)
|
|
|
|
self.play(FadeIn(flat_hist))
|
|
self.play(
|
|
ReplacementTransform(flat_hist, hist)
|
|
)
|
|
|
|
braces = VGroup()
|
|
p_labels = VGroup()
|
|
# add x labels (braces)
|
|
for (p,string,bar) in zip(probabilities, prob_strings,hist.bars):
|
|
brace = Brace(bar, DOWN, buff = 0.1)
|
|
p_label = TexMobject(string).next_to(brace, DOWN, buff = SMALL_BUFF).scale(0.7)
|
|
group = VGroup(brace, p_label)
|
|
braces.add(brace)
|
|
p_labels.add(p_label)
|
|
self.play(
|
|
Write(group)
|
|
)
|
|
|
|
|
|
|
|
labels = VGroup()
|
|
for (y, bar) in zip(y_values, hist.bars):
|
|
label = TexMobject(str(int(y))).scale(0.7).next_to(bar, UP, buff = SMALL_BUFF)
|
|
self.play(FadeIn(label))
|
|
labels.add(label)
|
|
|
|
y_average = np.mean(y_values)
|
|
averaged_y_values = y_average * np.ones(np.shape(y_values))
|
|
|
|
averaged_hist = flat_hist = Histogram(probabilities, averaged_y_values,
|
|
mode = "widths",
|
|
x_scale = x_scale,
|
|
y_scale = y_scale,
|
|
x_labels = "none"
|
|
).fade(0.2)
|
|
|
|
ghost_hist = hist.copy().fade(0.8)
|
|
labels.fade(0.8)
|
|
self.bring_to_back(ghost_hist)
|
|
|
|
self.play(Transform(hist, averaged_hist))
|
|
|
|
average_label = TexMobject(str(y_average)).scale(0.7).next_to(averaged_hist, UP, SMALL_BUFF)
|
|
|
|
one_brace = Brace(averaged_hist, DOWN, buff = 0.1)
|
|
one_p_label = TexMobject(str(1)).next_to(one_brace, DOWN, buff = SMALL_BUFF).scale(0.7)
|
|
one_group = VGroup(one_brace, one_p_label)
|
|
|
|
self.play(
|
|
FadeIn(average_label),
|
|
Transform(braces, one_brace),
|
|
Transform(p_labels, one_p_label),
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AreaSplitting(Scene):
|
|
|
|
def create_rect_row(self,n):
|
|
rects_group = VGroup()
|
|
for k in range(n+1):
|
|
proportion = float(choose(n,k)) / 2**n
|
|
new_rect = Rectangle(
|
|
width = proportion * WIDTH,
|
|
height = HEIGHT,
|
|
fill_color = graded_color(n,k),
|
|
fill_opacity = 1
|
|
)
|
|
new_rect.next_to(rects_group,RIGHT,buff = 0)
|
|
rects_group.add(new_rect)
|
|
return rects_group
|
|
|
|
def split_rect_row(self,rect_row):
|
|
|
|
split_row = VGroup()
|
|
for rect in rect_row.submobjects:
|
|
half = rect.copy().stretch_in_place(0.5,0)
|
|
left_half = half.next_to(rect.get_center(),LEFT,buff = 0)
|
|
right_half = half.copy().next_to(rect.get_center(),RIGHT,buff = 0)
|
|
split_row.add(left_half, right_half)
|
|
return split_row
|
|
|
|
|
|
def rect_center(self,n,i,j):
|
|
if n < 0:
|
|
raise Exception("wrong indices " + str(n) + ", " + str(i) + ", " + str(j))
|
|
if i < 0 or i > n:
|
|
raise Exception("wrong indices " + str(n) + ", " + str(i) + ", " + str(j))
|
|
if j > choose(n,i) or j < 0:
|
|
raise Exception("wrong indices " + str(n) + ", " + str(i) + ", " + str(j))
|
|
|
|
rect = self.brick_array[n][i]
|
|
width = rect.get_width()
|
|
left_x = rect.get_center()[0] - width/2
|
|
spacing = width / choose(n,i)
|
|
x = left_x + (j+0.5) * spacing
|
|
return np.array([x,rect.get_center()[1], rect.get_center()[2]])
|
|
|
|
def construct(self):
|
|
|
|
# Draw the bricks
|
|
|
|
brick_wall = VGroup()
|
|
rect_row = self.create_rect_row(0)
|
|
rect_row.move_to(3.5*UP + 0*HEIGHT*DOWN)
|
|
self.add(rect_row)
|
|
brick_wall.add(rect_row)
|
|
self.brick_array = [[rect_row.submobjects[0]]]
|
|
|
|
for n in range(NB_ROWS):
|
|
# copy and shift
|
|
new_rect_row = rect_row.copy()
|
|
self.add(new_rect_row)
|
|
self.play(new_rect_row.shift,HEIGHT * DOWN)
|
|
self.wait()
|
|
|
|
#split
|
|
split_row = self.split_rect_row(new_rect_row)
|
|
self.play(FadeIn(split_row))
|
|
self.remove(new_rect_row)
|
|
self.wait()
|
|
|
|
# merge
|
|
rect_row = self.create_rect_row(n+1)
|
|
rect_row.move_to(3.5*UP + (n+1)*HEIGHT*DOWN)
|
|
self.play(FadeIn(rect_row))
|
|
brick_wall.add(rect_row)
|
|
self.remove(split_row)
|
|
self.wait()
|
|
|
|
# add to brick dict
|
|
rect_array = []
|
|
for rect in rect_row.submobjects:
|
|
rect_array.append(rect)
|
|
|
|
self.brick_array.append(rect_array)
|
|
|
|
|
|
self.play(
|
|
brick_wall.set_fill, {"opacity" : 0.2}
|
|
)
|
|
|
|
|
|
# Draw the branches
|
|
|
|
for (n, rect_row_array) in enumerate(self.brick_array):
|
|
for (i, rect) in enumerate(rect_row_array):
|
|
pos = rect.get_center()
|
|
tally = TallyStack(n - i, i)
|
|
tally.move_to(pos)
|
|
|
|
|
|
# from the left
|
|
lines = VGroup()
|
|
|
|
if i > 0:
|
|
for j in range(choose(n-1,i-1)):
|
|
start_pos = self.rect_center(n-1,i-1,j)
|
|
end_pos = self.rect_center(n,i,j)
|
|
lines.add(Line(start_pos,end_pos, stroke_color = GRADE_COLOR_2))
|
|
self.play(
|
|
LaggedStart(ShowCreation, lines))
|
|
|
|
# from the right
|
|
lines = VGroup()
|
|
|
|
if i < n:
|
|
for j in range(choose(n-1,i)):
|
|
start_pos = self.rect_center(n-1,i,j)
|
|
if i != 0:
|
|
end_pos = self.rect_center(n,i,choose(n-1,i-1) + j)
|
|
else:
|
|
end_pos = self.rect_center(n,i,j)
|
|
|
|
lines.add(Line(start_pos,end_pos, stroke_color = GRADE_COLOR_1))
|
|
self.play(
|
|
LaggedStart(ShowCreation, lines))
|
|
|
|
|
|
|
|
#self.play(FadeIn(tally))
|
|
|
|
|
|
class DieFace(SVGMobject):
|
|
|
|
def __init__(self, value, **kwargs):
|
|
|
|
self.value = value
|
|
self.file_name = "Dice-" + str(value)
|
|
self.ensure_valid_file()
|
|
SVGMobject.__init__(self, file_name = self.file_name)
|
|
|
|
class RowOfDice(VGroup):
|
|
CONFIG = {
|
|
"values" : range(1,7)
|
|
}
|
|
|
|
def generate_points(self):
|
|
for value in self.values:
|
|
new_die = DieFace(value)
|
|
new_die.submobjects[0].set_fill(opacity = 0)
|
|
new_die.submobjects[0].set_stroke(width = 7)
|
|
new_die.next_to(self, RIGHT)
|
|
self.add(new_die)
|
|
|
|
|
|
|
|
class ShowUncertainty1(Scene):
|
|
|
|
def throw_a_die(self):
|
|
|
|
eye = np.random.randint(1,7)
|
|
face = self.row_of_dice.submobjects[eye - 1]
|
|
self.tallies[eye - 1] += 1
|
|
|
|
new_hist = self.hist_from_tallies()
|
|
|
|
self.play(
|
|
ApplyMethod(face.submobjects[0].set_fill, {"opacity": 1},
|
|
rate_func = there_and_back,
|
|
run_time = 0.3,
|
|
),
|
|
)
|
|
self.play(
|
|
Transform(self.dice_histogram, new_hist,
|
|
run_time = 0.5)
|
|
)
|
|
|
|
def hist_from_tallies(self):
|
|
x_scale = self.row_of_dice.get_width() / np.size(self.tallies)
|
|
hist = Histogram(np.ones(6), self.tallies,
|
|
mode = "widths",
|
|
x_labels = "none",
|
|
y_scale = 0.5,
|
|
x_scale = x_scale
|
|
)
|
|
|
|
hist.next_to(self.row_of_dice, UP)
|
|
return hist
|
|
|
|
def construct(self):
|
|
|
|
self.row_of_dice = RowOfDice().scale(0.5).move_to(2 * DOWN)
|
|
self.add(self.row_of_dice)
|
|
|
|
self.tallies = np.zeros(6)
|
|
self.dice_histogram = self.hist_from_tallies()
|
|
|
|
self.add(self.dice_histogram)
|
|
|
|
for i in range(30):
|
|
self.throw_a_die()
|
|
self.wait()
|
|
|
|
|
|
|
|
class IdealizedDieHistogram(Scene):
|
|
|
|
def construct(self):
|
|
|
|
self.row_of_dice = RowOfDice().scale(0.5).move_to(2 * DOWN)
|
|
self.add(self.row_of_dice)
|
|
self.probs = 1.0/6 * np.ones(6)
|
|
x_scale = self.row_of_dice.get_width() / np.size(self.probs)
|
|
|
|
y_labels = ["${1\over 6}$"] * 6
|
|
|
|
hist = Histogram(np.ones(6), self.probs,
|
|
mode = "widths",
|
|
x_labels = "none",
|
|
y_labels = y_labels,
|
|
y_label_position = "center",
|
|
y_scale = 20,
|
|
x_scale = x_scale,
|
|
)
|
|
hist.next_to(self.row_of_dice, UP)
|
|
|
|
self.add(hist)
|
|
|
|
|
|
|
|
class ShowUncertainty2(PiCreatureScene):
|
|
|
|
|
|
def throw_darts(self, n, run_time = 1):
|
|
|
|
points = np.random.normal(
|
|
loc = self.dartboard.get_center(),
|
|
scale = 0.6 * np.ones(3),
|
|
size = (n,3)
|
|
)
|
|
points[:,2] = 0
|
|
dots = VGroup()
|
|
for point in points:
|
|
dot = Dot(point, radius = 0.04, fill_opacity = 0.7)
|
|
dots.add(dot)
|
|
self.add(dot)
|
|
|
|
self.play(
|
|
LaggedStart(FadeIn, dots, lag_ratio = 0.01, run_time = run_time)
|
|
)
|
|
|
|
|
|
def construct(self):
|
|
|
|
self.dartboard = ImageMobject("dartboard").scale(2)
|
|
dartboard_circle = Circle(
|
|
radius = self.dartboard.get_width() / 2,
|
|
fill_color = BLACK,
|
|
fill_opacity = 0.5,
|
|
stroke_color = WHITE,
|
|
stroke_width = 5
|
|
)
|
|
self.dartboard.add(dartboard_circle)
|
|
|
|
self.add(self.dartboard)
|
|
|
|
self.throw_darts(5,5)
|
|
self.throw_darts(20,5)
|
|
self.throw_darts(100,5)
|
|
self.throw_darts(1000,5)
|
|
|
|
|
|
|
|
|
|
|
|
class ShowUncertainty3(Scene):
|
|
|
|
def construct(self):
|
|
|
|
randy = CoinFlippingPiCreature(color = MAROON_E)
|
|
randy.scale(0.5).to_edge(LEFT + DOWN)
|
|
|
|
heads = tails = 0
|
|
tally = TallyStack(heads, tails, anchor = ORIGIN)
|
|
|
|
nb_flips = 10
|
|
|
|
flips = np.random.randint(2, size = nb_flips)
|
|
|
|
for i in range(nb_flips):
|
|
|
|
self.play(FlipCoin(randy))
|
|
self.wait(0.5)
|
|
|
|
flip = flips[i]
|
|
if flip == 0:
|
|
heads += 1
|
|
elif flip == 1:
|
|
tails += 1
|
|
else:
|
|
raise Exception("That side does not exist on this coin")
|
|
|
|
new_tally = TallyStack(heads, tails, anchor = ORIGIN)
|
|
|
|
if tally.nb_heads == 0 and new_tally.nb_heads == 1:
|
|
self.play(FadeIn(new_tally.heads_stack))
|
|
elif tally.nb_tails == 0 and new_tally.nb_tails == 1:
|
|
self.play(FadeIn(new_tally.tails_stack))
|
|
else:
|
|
self.play(Transform(tally, new_tally))
|
|
|
|
tally = new_tally
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|