File size: 16,092 Bytes
2771759
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
from typing import List

import random
import gradio as gr

SAMPLE_SPACE = [1, 2, 3, 4, 5, 6]


def random_event() -> List[int]:
    n = random.randint(1, len(SAMPLE_SPACE))
    return random.sample(SAMPLE_SPACE, n)

def convert_to_str(outcomes: List[int]):
    return ", ".join(map(str, outcomes))

def parse_str(outcomes: str):
    if not outcomes:
        return []
    try:
        return list(map(int, outcomes.split(",")))
    except ValueError:
        raise ValueError("Please enter a list of integers separated by commas.")

def compute_probability(favorable_outcomes: List[int], possible_outcomes: List[int]):
    assert all(outcome in possible_outcomes for outcome in favorable_outcomes), "Favorable outcomes must be a subset of possible outcomes."
    return len(favorable_outcomes) / len(possible_outcomes)


# Create a Gradio interface
css = """
.gradio-container {
    width: 40%!important;
    min-width: 800px;
}
details {
  border: 1px solid #aaa;
  border-radius: 4px;
  padding: 0.5em 0.5em 0;
}

summary {
  font-weight: bold;
  margin: -0.5em -0.5em 0;
  padding: 0.5em;
}

details[open] {
  padding: 0.5em;
}

details[open] summary {
  border-bottom: 1px solid #aaa;
  margin-bottom: 0.5em;
}
"""
with gr.Blocks(css=css) as demo:
    gr.Markdown(
        """
        # Probability Basics (Pt. 3)
        <div align="center">
        <br>
        <p>Let's learn discover more fundamental principles of Probability theory!</p>
        <br>
        </div>

        Welcome to this new segment of the ***Probability Basics series***! πŸŽ‰
        <br>So far, we have covered the following:
        - [Part 1](https://huggingface.co/spaces/helboukkouri/probability-basics-pt1): **preliminary concepts**β€”sample spaces, events and how to calculate the probability of simple events;
        - [Part 2](https://huggingface.co/spaces/helboukkouri/probability-basics-pt2): **the axioms of probability**β€”non-negativity, normalization, and additivity.
        
        In this part we will introduce the concept of `conditional probability`, along with the idea of event `independence`.
        <br>***NOTE**: It is easy to confuse `disjoint` and `independent` events, but they are not the same thing. So stay tuned to learn more!*
        """
    )

    gr.Markdown(
        r"""
        ## Conditional Probability
        Sometimes we are not interested in the probability of an event in isolation. Instead, we might want to know the probability of an event given that another event has already occurred. This is known as `conditional probability` and is denoted as:
        $$P(A|B)$$
        which reads as `the probability of event A given that event B has occurred`.

        When an event `B` has a **non-zero probability** of happening, the conditional probability of `A` given `B` is defined as:
        $$P(A|B) = \frac{P(A \cap B)}{P(B)}$$

        This also gives us a nice way of computing the probability of two events `A` and `B` occurring together:
        $$P(A \cap B) = P(A|B) \times P(B) = P(B|A) \times P(A)$$

        Intuitively, for `A` and `B` to occur together, first, either A or B has to happen, then, the other one occurs given the first πŸ€“.

        In the context of a six-sided die, we can easily compute the probabilities and check these formulas.
        """
    )
    with gr.Column():
        with gr.Row():
            randomize = gr.Button(value="Randomize Events")
            compute = gr.Button(value="Compute")
        with gr.Row():
            with gr.Column():
                outcomes_A = gr.Textbox(label="Favorable Outcomes: A", value="3, 4")
                outcomes_B = gr.Textbox(label="Favorable Outcomes: B", value="2, 4, 6")
                outcomes_AB = gr.Textbox(label="Favorable Outcomes: A ∩ B", value="4")
                all_outcomes = gr.Textbox(label="All Possible Outcomes", value="1, 2, 3, 4, 5, 6", interactive=False)
            with gr.Column():
                proba_A = gr.Textbox(label="Probability: A", value="", interactive=False)
                proba_B = gr.Textbox(label="Probability: B", value="", interactive=False)
                proba_AB = gr.Textbox(label="Probability: A ∩ B", value="", interactive=False)
                proba_AgB = gr.Textbox(label="Probability: A | B", value="", interactive=False)
    
    randomize.click(
        lambda: convert_to_str(random_event()),
        inputs=[],
        outputs=outcomes_A
    )
    outcomes_A.change(
        lambda a, b: convert_to_str(set(parse_str(a)).intersection(parse_str(b))),
        inputs=[outcomes_A, outcomes_B],
        outputs=outcomes_AB
    )
    randomize.click(
        lambda: convert_to_str(random_event()),
        inputs=[],
        outputs=outcomes_B
    )
    outcomes_B.change(
        lambda a, b: convert_to_str(set(parse_str(a)).intersection(parse_str(b))),
        inputs=[outcomes_A, outcomes_B],
        outputs=outcomes_AB
    )

    compute.click(
        lambda a, b: f"{compute_probability(parse_str(a), parse_str(b)):.2%}",
        inputs=[outcomes_A, all_outcomes],
        outputs=proba_A
    )
    compute.click(
        lambda a, b: f"{compute_probability(parse_str(a), parse_str(b)):.2%}",
        inputs=[outcomes_B, all_outcomes],
        outputs=proba_B
    )
    compute.click(
        lambda a, b: f"{compute_probability(parse_str(a), parse_str(b)):.2%}",
        inputs=[outcomes_AB, all_outcomes],
        outputs=proba_AB
    )
    compute.click(
        lambda a, b, c: f"{compute_probability(parse_str(a), parse_str(b)) / compute_probability(parse_str(c), parse_str(b)):.2%}",
        inputs=[outcomes_AB, all_outcomes, outcomes_B],
        outputs=proba_AgB
    )

    gr.Markdown(
        r"""
        ## Bayes' Rule
        `Bayes' Rule` is a fundamental theorem in probability theory that allows us switch the condition and the event in a conditional probability. For two events `A` and `B`, Bayes' Rule states that:
        $$P(A|B) = \frac{P(B|A) \times P(A)}{P(B)}$$

        This is easily derived from the definition of conditional probability. Specifically from:
        $$P(A|B) \times P(B) = P(A \cap B) = P(B \cap A) = P(B|A) \times P(A)$$
        
        ## Law of Total Probability
        The `law of total probability` allows us to break down an event `A` according to a set of events whose probabilities are easier to tackle.
        <br>
        Let's say we have some events:
        $$B_1, B_2, \ldots, B_n$$
        that form a `partition` of the sample spaceβ€”meaning that the events do not overlap and together they cover the entire sample space.
        <br>Then we can express the probability of event `A` as:
        $$P(A) = \sum_{i=1}^{n} P(A|B_i) \times P(B_i) = P(A|B_1) \times P(B_1) + \ldots + P(A|B_n) \times P(B_n)$$

        In many cases, we can use a single intermediate event to compute:

        $$P(A) = P(A|B) \times P(B) + P(A|\text{not }{B}) \times P(\text{not }{B})$$

        This is a very useful tool in probability theory and is used in many real-world applications.

        ## The Monty Hall problem
        he Monty Hall problem is a famous probability puzzle that shows how counterintuitive probability can be. It is also a good illustration of we can decompose a complex probability into simple ones using `conditional probability` and the `law of total probability`.
        
        The problem is as follows:
        - You are a contestant on the show and are presented with `three doors`.
        - Behind one of the doors is a `car`, and behind the other two are `goats`.
        - You choose a door to begin with.
        - The host, who knows what is behind each door, opens one of the other two doors to reveal a `goat`.
        - You are then given the option to change your choice to the other unopened door.

        The question is: `should you change your choice or stick with your initial choice? πŸ€”`
        <br><br>
        You can play around with the following simulation to see how the probabilities change as you make your choices.
        """
    )

    with gr.Column():
        with gr.Row():
            shuffle_doors = gr.Button(value="Shuffle Doors")
            reset_game = gr.Button(value="Reset Game", visible=False)
        with gr.Row():
            choose_1 = gr.Button(value="Choose Door nΒ°1", visible=False)
            choose_2 = gr.Button(value="Choose Door nΒ°2", visible=False)
            choose_3 = gr.Button(value="Choose Door nΒ°3", visible=False)
            reveal_first = gr.Button(value="Reveal First Goat", visible=False)
            reveal_all = gr.Button(value="Reveal Remaining Doors", visible=False)
        with gr.Row():
            door_1 = gr.Textbox(label="Door 1", value="", interactive=False)
            door_1_ = gr.Textbox(value="🐐", interactive=False, visible=False)
            choice_1 = gr.Textbox(value="", interactive=False, visible=False)
            door_2 = gr.Textbox(label="Door 2", value="", interactive=False)
            door_2_ = gr.Textbox(value="πŸš—", interactive=False, visible=False)
            choice_2 = gr.Textbox(value="", interactive=False, visible=False)
            door_3 = gr.Textbox(label="Door 3", value="", interactive=False)
            door_3_ = gr.Textbox(value="🐐", interactive=False, visible=False)
            choice_3 = gr.Textbox(value="", interactive=False, visible=False)

    def game_initial_state():
        return (
            gr.update(visible=True),
            gr.update(visible=False),
            gr.update(visible=False),
            gr.update(visible=False),
            gr.update(visible=False),
            gr.update(visible=False),
            gr.update(visible=False)
        )
    
    def after_shuffle_doors():
        return (
            gr.update(visible=False),
            gr.update(visible=True),
            gr.update(visible=True),
            gr.update(visible=True),
            gr.update(visible=False),
            gr.update(visible=False),
            gr.update(visible=False)
        )
    
    def after_door_choice():
        return (
            gr.update(visible=False),
            gr.update(visible=False),
            gr.update(visible=False),
            gr.update(visible=False),
            gr.update(visible=True),
            gr.update(visible=False),
            gr.update(visible=False)
        )
    
    def after_first_reveal():
        return (
            gr.update(visible=False),
            gr.update(visible=False),
            gr.update(visible=False),
            gr.update(visible=False),
            gr.update(visible=False),
            gr.update(visible=True),
            gr.update(visible=False)
        )
    
    def after_final_reveal():
        return (
            gr.update(visible=False),
            gr.update(visible=False),
            gr.update(visible=False),
            gr.update(visible=False),
            gr.update(visible=False),
            gr.update(visible=False),
            gr.update(visible=True)
        )
        
    def show_choice_buttons():
        gr.update(choose_1, visible=True)
        gr.update(choose_2, visible=True)
        gr.update(choose_3, visible=True)

    def hide_reveal_buttons():
        gr.update(reveal_first, visible=False)
        gr.update(reveal_all, visible=False)

    def show_first_reveal_button():
        return (
            gr.update(visible=True),
            gr.update(visible=False)
        )

    def show_final_reveal_button():
        gr.update(reveal_first, visible=True)
        gr.update(reveal_all, visible=True)

    shuffle_doors.click(
        lambda: list(random.sample(["πŸš—", "🐐", "🐐"], 3)) + [""] * 3,
        inputs=[],
        outputs=[door_1_, door_2_, door_3_, door_1, door_2, door_3]
    )
    shuffle_doors.click(
        after_shuffle_doors,
        inputs=[], outputs=[shuffle_doors, choose_1, choose_2, choose_3, reveal_first, reveal_all, reset_game]
    )

    choose_1.click(lambda: [1, 0, 0], inputs=[], outputs=[choice_1, choice_2, choice_3])
    choose_1.click(after_door_choice, inputs=[], outputs=[shuffle_doors, choose_1, choose_2, choose_3, reveal_first, reveal_all, reset_game])

    choose_2.click(lambda: [0, 1, 0], inputs=[], outputs=[choice_1, choice_2, choice_3])
    choose_2.click(after_door_choice, inputs=[], outputs=[shuffle_doors, choose_1, choose_2, choose_3, reveal_first, reveal_all, reset_game])

    choose_3.click(lambda: [0, 0, 1], inputs=[], outputs=[choice_1, choice_2, choice_3])
    choose_3.click(after_door_choice, inputs=[], outputs=[shuffle_doors, choose_1, choose_2, choose_3, reveal_first, reveal_all, reset_game])

    reveal_first.click(
        # Disgusting but fun one-liner to reveal the first goat
        lambda a, b, c, d, e, f: ["🐐" if i == [v if i != "1" else "" for i, v in zip([d, e, f], [a, b, c])].index("🐐") else "" for i in range(3)],
        inputs=[door_1_, door_2_, door_3_, choice_1, choice_2, choice_3],
        outputs=[door_1, door_2, door_3]
    )
    reveal_first.click(after_first_reveal, inputs=[], outputs=[shuffle_doors, choose_1, choose_2, choose_3, reveal_first, reveal_all, reset_game])

    reveal_all.click(
        lambda a, b, c: [a, b, c],
        inputs=[door_1_, door_2_, door_3_],
        outputs=[door_1, door_2, door_3]
    )
    reveal_all.click(
        after_final_reveal,
        inputs=[],
        outputs=[shuffle_doors, choose_1, choose_2, choose_3, reveal_first, reveal_all, reset_game]
    )

    reset_game.click(
        lambda: ["", "", ""],
        inputs=[],
        outputs=[door_1, door_2, door_3]
    )
    reset_game.click(
        game_initial_state,
        inputs=[],
        outputs=[shuffle_doors, choose_1, choose_2, choose_3, reveal_first, reveal_all, reset_game]
    )

    gr.Markdown(
        r"""
        <br>
        <details>
        <summary>Show Solution</summary>
        At first glance, the probability of winning the car seems to be 1/3 since there are three doors and only one of them hides the car.
        However, the probability of winning the car changes according to whether you change your choice or not. Let's see how!

        Let's call the event of winning the car after changing doors: `X`. Then:
        $$P(X) = P(X | \text{πŸš—}) \times P(\text{πŸš—}) + P(X | \text{🐐}) \times P(\text{🐐})$$

        When we change our door and we have initially chosen the car, we lose. So:
        $$P(X | \text{πŸš—}) = 0$$
        
        When we change our door and we have initially chosen a goat, we win. So:
        $$P(X | \text{🐐}) = 1$$
        
        There are two goats and one car, so:
        $$P(\text{πŸš—}) = \frac{1}{3}$$
        $$P(\text{🐐}) = \frac{2}{3}$$
        
        Therefore:
        $$P(X) = 0 \times \frac{1}{3} + 1 \times \frac{2}{3} = \frac{2}{3}$$
        
        As a result, you have twice the chances of winning the car if you change your choice! πŸŽ‰
        </details>

        ## Independence of Events
        Now that we know how to compute the probability of an event given that another more or less related event has already occurred; it is fair to wonder about events that are completely unrelated.
        
        Specifically, two events are said to be `independent` if the occurrence of one event does not affect the occurrence of the other. In other words, the probability of one event does not depend on the occurrence of the other. 
        
        Mathematically, two events `A` and `B` are `independent` if and only if:
        $$P(A \cap B) = P(A) \times P(B)$$

        One important thing to note is that `disjoint` events are not necessarily `independent`. In fact, most often than not (unless one event is impossible), when two events are disjoint they are actually dependent as:
        $$P(A \cap B) = 0 \neq P(A) \times P(B)$$

        Another interesting result is that the conditional probability of `A` given `B` is the same as the probability of `A` if `A` and `B` are independent:
        $$P(A|B) = \frac{P(A \cap B)}{P(B)} = \frac{P(A) \times P(B)}{P(B)} = P(A)$$

        The END.
        """
    )


if __name__ == "__main__":
    demo.launch()