aboutsummaryrefslogtreecommitdiffstats
path: root/scenes/v16_bayes.py
blob: be6ffdce020a60f604451e13530cd7ed4fda285e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# -*- coding: utf-8 -*-
import sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from manim import *
from tiktok import *

COLS, ROWS = 25, 40
PX, PY = 0.27, 0.175
GY = 1.65                       # vertical centre of the grid
RAD = 0.062
DIMMED = "#2B3A5C"
SICK = 437                   # row 17, column 12 (ends up centred)
FALSE_POS = [63, 147, 232, 318, 389, 476, 551, 668, 742, 861]


def pos(i):
    r, c = divmod(i, COLS)
    return np.array([(c - (COLS - 1) / 2) * PX,
                     GY + ((ROWS - 1) / 2 - r) * PY, 0.0])


class Bayes(TikTok):
    NAME = "bayes"

    def construct(self):
        self.prepare()
        chip = self.chip("Probabilidad", C_SKY)

        # --- the test card ---------------------------------------------------
        frame_box = RoundedRectangle(corner_radius=0.32, width=6.4, height=3.4, stroke_width=5,
                                 stroke_color=C_SKY, fill_color="#121C33",
                                 fill_opacity=1).move_to(UP * 3.5)
        et = Text("TEST", font=FONT, font_size=40, weight=BOLD, color=C_GRAY)
        num = Text("99%", font=FONT, font_size=118, weight=BOLD, color=AMBER)
        footer = Text("de precisión", font=FONT, font_size=38, weight=BOLD, color=WHITE)
        VGroup(et, num, footer).arrange(DOWN, buff=0.18).move_to(frame_box)
        card = VGroup(frame_box, et, num, footer)

        with self.beat(0) as b:
            b.play(FadeIn(chip, shift=DOWN * 0.3), run_time=0.4)
            b.play(GrowFromCenter(card), run_time=0.8)
            b.play(Flash(num, color=AMBER, line_length=0.35, num_lines=14,
                         flash_radius=1.7), run_time=0.9)

        stamp = VGroup(
            RoundedRectangle(corner_radius=0.18, width=4.7, height=1.25, stroke_width=8,
                             stroke_color=C_PINK, fill_color="#2A0E18", fill_opacity=0.94),
            Text("POSITIVO", font=FONT, font_size=60, weight=BOLD, color=C_PINK))
        stamp.rotate(-0.09).move_to(frame_box.get_bottom() + DOWN * 0.05)

        with self.beat(1) as b:
            b.play(FadeIn(stamp, scale=2.4), run_time=0.55)
            b.play(Wiggle(stamp, scale_value=1.07), run_time=1.0)

        question = Text("La mayoría contesta:", font=FONT, font_size=40,
                    weight=BOLD, color=C_GRAY).move_to(UP * 0.45)
        wrong = Text("99%", font=FONT, font_size=106, weight=BOLD, color=C_GRAY)
        wrong.next_to(question, DOWN, buff=0.28)

        with self.beat(2) as b:
            b.play(FadeIn(question, shift=UP * 0.2), run_time=0.5)
            b.play(FadeIn(wrong, scale=0.7), run_time=0.7)

        strike = Line(wrong.get_left() + LEFT * 0.28, wrong.get_right() + RIGHT * 0.28,
                     stroke_width=11, color=C_PINK)
        right_ok = Text("9%", font=FONT, font_size=150, weight=BOLD,
                    color=C_GREEN).move_to(DOWN * 2.05)

        with self.beat(3) as b:
            b.play(Create(strike), run_time=0.45)
            b.play(FadeIn(right_ok, scale=0.6), run_time=0.7)
            b.play(Flash(right_ok, color=C_GREEN, flash_radius=1.7, num_lines=16), run_time=0.9)

        # --- las mil personas -------------------------------------------------
        point_list = VGroup(*[Dot(radius=RAD, color=DIMMED, fill_opacity=1).move_to(pos(i))
                          for i in range(COLS * ROWS)])
        caption = Text("1.000 personas", font=FONT, font_size=40, weight=BOLD,
                      color=WHITE).move_to(UP * 5.55)

        with self.beat(4) as b:
            b.play(FadeOut(VGroup(card, stamp, question, wrong, strike, right_ok)), run_time=0.5)
            b.play(FadeIn(point_list, lag_ratio=0), FadeIn(caption, shift=DOWN * 0.2),
                   run_time=1.1)

        sick = point_list[SICK]
        ring = Circle(radius=0.30, stroke_width=6, color=C_PINK).move_to(pos(SICK))
        small_box = RoundedRectangle(corner_radius=0.14, width=2.45, height=0.7, stroke_width=0,
                                  fill_color="#0B0F1A", fill_opacity=0.92)
        txt = Text("1 enferma", font=FONT, font_size=34, weight=BOLD, color=C_PINK)
        etiq = VGroup(small_box, txt).move_to(pos(SICK) + RIGHT * 1.95)
        guide = Line(ring.get_right(), etiq.get_left(), stroke_width=4, color=C_PINK)

        with self.beat(5) as b:
            b.play(sick.animate.set_color(C_PINK).scale(2.2), run_time=0.5)
            b.play(Create(ring), run_time=0.4)
            b.play(Create(guide), FadeIn(etiq), run_time=0.5)

        # --- the test sweep ---------------------------------------------------
        sweep = Line(LEFT * 3.6, RIGHT * 3.6, stroke_width=6, color=C_SKY)
        sweep.move_to([0, pos(0)[1] + 0.22, 0])
        ley1 = VGroup(Dot(radius=0.12, color=C_PINK),
                      Text("1 positivo verdadero", font=FONT, font_size=36,
                           weight=BOLD, color=WHITE)).arrange(RIGHT, buff=0.26)
        ley1.move_to(DOWN * 2.15)

        with self.beat(6) as b:
            b.play(FadeIn(sweep), run_time=0.25)
            b.play(sweep.animate.move_to([0, pos(COLS * ROWS - 1)[1] - 0.22, 0]),
                   run_time=max(0.9, b.floor - 1.5), rate_func=linear)
            b.play(FadeOut(sweep),
                   Flash(ring, color=C_GREEN, flash_radius=0.65, num_lines=12),
                   run_time=0.5)
            b.add(ley1)

        # --- the false positives ----------------------------------------------
        false_pos = VGroup(*[point_list[i] for i in FALSE_POS])

        with self.beat(7) as b:
            b.play(LaggedStart(*[p.animate.set_color(AMBER).scale(2.2) for p in false_pos],
                               lag_ratio=0.2), run_time=max(1.3, b.floor - 0.7))

        ley2 = VGroup(Dot(radius=0.12, color=AMBER),
                      Text("10 falsos positivos", font=FONT, font_size=36,
                           weight=BOLD, color=WHITE)).arrange(RIGHT, buff=0.26)
        ley2.move_to(DOWN * 2.78)

        with self.beat(8) as b:
            b.play(FadeIn(ley2, shift=UP * 0.2), run_time=0.45)
            b.play(*[Indicate(p, color=AMBER, scale_factor=1.7) for p in false_pos],
                   run_time=1.1)

        # --- the eleven positives, in a row -----------------------------------
        marked = set(FALSE_POS) | {SICK}
        remainder = VGroup(*[p for j, p in enumerate(point_list) if j not in marked])
        chosen = [point_list[SICK]] + [point_list[i] for i in FALSE_POS]
        count = Text("11 positivos", font=FONT, font_size=52, weight=BOLD,
                      color=WHITE).move_to(UP * 2.2)
        frac = Text("1 de 11 = 9%", font=FONT, font_size=90, weight=BOLD,
                    color=C_GREEN).move_to(UP * 0.7)

        with self.beat(9) as b:
            b.play(remainder.animate.set_opacity(0.12),
                   FadeOut(VGroup(ring, guide, etiq, caption, ley1, ley2)), run_time=0.5)
            b.play(*[p.animate.move_to([(k - 5) * 0.62, 3.35, 0]).scale(2.4)
                     for k, p in enumerate(chosen)], run_time=0.9)
            b.play(FadeIn(count, shift=UP * 0.2), run_time=0.4)
            b.play(FadeIn(frac, scale=0.7), run_time=0.6)

        closing = VGroup(
            Text("Si la enfermedad es rara,", font=FONT, font_size=42, weight=BOLD, color=C_GRAY),
            Text("un positivo suelto", font=FONT, font_size=50, weight=BOLD, color=WHITE),
            Text("casi siempre es falso", font=FONT, font_size=50, weight=BOLD, color=AMBER),
        ).arrange(DOWN, buff=0.32).move_to(DOWN * 1.75)

        with self.beat(10) as b:
            b.play(FadeOut(remainder), run_time=0.4)
            b.play(FadeIn(closing, shift=UP * 0.25), run_time=0.8)
            b.play(Flash(frac, color=C_GREEN, flash_radius=2.2, num_lines=18), run_time=0.9)

        self.finish()