helboukkouri commited on
Commit
2771759
1 Parent(s): 64a3484

initial commit

Browse files
Files changed (3) hide show
  1. README.md +3 -3
  2. app.py +383 -0
  3. requirements.txt +198 -0
README.md CHANGED
@@ -1,6 +1,6 @@
1
  ---
2
- title: Probability Basics Pt3
3
- emoji: 🏃
4
  colorFrom: indigo
5
  colorTo: red
6
  sdk: gradio
@@ -10,4 +10,4 @@ pinned: false
10
  license: apache-2.0
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: Probability Basics (Pt. 3)
3
+ emoji: 🎲
4
  colorFrom: indigo
5
  colorTo: red
6
  sdk: gradio
 
10
  license: apache-2.0
11
  ---
12
 
13
+ Continue building towards a better understanding of Probability Theory with the introduction of `Conditional Probability`, `Bayes' Theorem`, the `Law of Total Probability`, and the notion `Independence`.
app.py ADDED
@@ -0,0 +1,383 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List
2
+
3
+ import random
4
+ import gradio as gr
5
+
6
+ SAMPLE_SPACE = [1, 2, 3, 4, 5, 6]
7
+
8
+
9
+ def random_event() -> List[int]:
10
+ n = random.randint(1, len(SAMPLE_SPACE))
11
+ return random.sample(SAMPLE_SPACE, n)
12
+
13
+ def convert_to_str(outcomes: List[int]):
14
+ return ", ".join(map(str, outcomes))
15
+
16
+ def parse_str(outcomes: str):
17
+ if not outcomes:
18
+ return []
19
+ try:
20
+ return list(map(int, outcomes.split(",")))
21
+ except ValueError:
22
+ raise ValueError("Please enter a list of integers separated by commas.")
23
+
24
+ def compute_probability(favorable_outcomes: List[int], possible_outcomes: List[int]):
25
+ assert all(outcome in possible_outcomes for outcome in favorable_outcomes), "Favorable outcomes must be a subset of possible outcomes."
26
+ return len(favorable_outcomes) / len(possible_outcomes)
27
+
28
+
29
+ # Create a Gradio interface
30
+ css = """
31
+ .gradio-container {
32
+ width: 40%!important;
33
+ min-width: 800px;
34
+ }
35
+ details {
36
+ border: 1px solid #aaa;
37
+ border-radius: 4px;
38
+ padding: 0.5em 0.5em 0;
39
+ }
40
+
41
+ summary {
42
+ font-weight: bold;
43
+ margin: -0.5em -0.5em 0;
44
+ padding: 0.5em;
45
+ }
46
+
47
+ details[open] {
48
+ padding: 0.5em;
49
+ }
50
+
51
+ details[open] summary {
52
+ border-bottom: 1px solid #aaa;
53
+ margin-bottom: 0.5em;
54
+ }
55
+ """
56
+ with gr.Blocks(css=css) as demo:
57
+ gr.Markdown(
58
+ """
59
+ # Probability Basics (Pt. 3)
60
+ <div align="center">
61
+ <br>
62
+ <p>Let's learn discover more fundamental principles of Probability theory!</p>
63
+ <br>
64
+ </div>
65
+
66
+ Welcome to this new segment of the ***Probability Basics series***! 🎉
67
+ <br>So far, we have covered the following:
68
+ - [Part 1](https://huggingface.co/spaces/helboukkouri/probability-basics-pt1): **preliminary concepts**—sample spaces, events and how to calculate the probability of simple events;
69
+ - [Part 2](https://huggingface.co/spaces/helboukkouri/probability-basics-pt2): **the axioms of probability**—non-negativity, normalization, and additivity.
70
+
71
+ In this part we will introduce the concept of `conditional probability`, along with the idea of event `independence`.
72
+ <br>***NOTE**: It is easy to confuse `disjoint` and `independent` events, but they are not the same thing. So stay tuned to learn more!*
73
+ """
74
+ )
75
+
76
+ gr.Markdown(
77
+ r"""
78
+ ## Conditional Probability
79
+ 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:
80
+ $$P(A|B)$$
81
+ which reads as `the probability of event A given that event B has occurred`.
82
+
83
+ When an event `B` has a **non-zero probability** of happening, the conditional probability of `A` given `B` is defined as:
84
+ $$P(A|B) = \frac{P(A \cap B)}{P(B)}$$
85
+
86
+ This also gives us a nice way of computing the probability of two events `A` and `B` occurring together:
87
+ $$P(A \cap B) = P(A|B) \times P(B) = P(B|A) \times P(A)$$
88
+
89
+ Intuitively, for `A` and `B` to occur together, first, either A or B has to happen, then, the other one occurs given the first 🤓.
90
+
91
+ In the context of a six-sided die, we can easily compute the probabilities and check these formulas.
92
+ """
93
+ )
94
+ with gr.Column():
95
+ with gr.Row():
96
+ randomize = gr.Button(value="Randomize Events")
97
+ compute = gr.Button(value="Compute")
98
+ with gr.Row():
99
+ with gr.Column():
100
+ outcomes_A = gr.Textbox(label="Favorable Outcomes: A", value="3, 4")
101
+ outcomes_B = gr.Textbox(label="Favorable Outcomes: B", value="2, 4, 6")
102
+ outcomes_AB = gr.Textbox(label="Favorable Outcomes: A ∩ B", value="4")
103
+ all_outcomes = gr.Textbox(label="All Possible Outcomes", value="1, 2, 3, 4, 5, 6", interactive=False)
104
+ with gr.Column():
105
+ proba_A = gr.Textbox(label="Probability: A", value="", interactive=False)
106
+ proba_B = gr.Textbox(label="Probability: B", value="", interactive=False)
107
+ proba_AB = gr.Textbox(label="Probability: A ∩ B", value="", interactive=False)
108
+ proba_AgB = gr.Textbox(label="Probability: A | B", value="", interactive=False)
109
+
110
+ randomize.click(
111
+ lambda: convert_to_str(random_event()),
112
+ inputs=[],
113
+ outputs=outcomes_A
114
+ )
115
+ outcomes_A.change(
116
+ lambda a, b: convert_to_str(set(parse_str(a)).intersection(parse_str(b))),
117
+ inputs=[outcomes_A, outcomes_B],
118
+ outputs=outcomes_AB
119
+ )
120
+ randomize.click(
121
+ lambda: convert_to_str(random_event()),
122
+ inputs=[],
123
+ outputs=outcomes_B
124
+ )
125
+ outcomes_B.change(
126
+ lambda a, b: convert_to_str(set(parse_str(a)).intersection(parse_str(b))),
127
+ inputs=[outcomes_A, outcomes_B],
128
+ outputs=outcomes_AB
129
+ )
130
+
131
+ compute.click(
132
+ lambda a, b: f"{compute_probability(parse_str(a), parse_str(b)):.2%}",
133
+ inputs=[outcomes_A, all_outcomes],
134
+ outputs=proba_A
135
+ )
136
+ compute.click(
137
+ lambda a, b: f"{compute_probability(parse_str(a), parse_str(b)):.2%}",
138
+ inputs=[outcomes_B, all_outcomes],
139
+ outputs=proba_B
140
+ )
141
+ compute.click(
142
+ lambda a, b: f"{compute_probability(parse_str(a), parse_str(b)):.2%}",
143
+ inputs=[outcomes_AB, all_outcomes],
144
+ outputs=proba_AB
145
+ )
146
+ compute.click(
147
+ lambda a, b, c: f"{compute_probability(parse_str(a), parse_str(b)) / compute_probability(parse_str(c), parse_str(b)):.2%}",
148
+ inputs=[outcomes_AB, all_outcomes, outcomes_B],
149
+ outputs=proba_AgB
150
+ )
151
+
152
+ gr.Markdown(
153
+ r"""
154
+ ## Bayes' Rule
155
+ `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:
156
+ $$P(A|B) = \frac{P(B|A) \times P(A)}{P(B)}$$
157
+
158
+ This is easily derived from the definition of conditional probability. Specifically from:
159
+ $$P(A|B) \times P(B) = P(A \cap B) = P(B \cap A) = P(B|A) \times P(A)$$
160
+
161
+ ## Law of Total Probability
162
+ 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.
163
+ <br>
164
+ Let's say we have some events:
165
+ $$B_1, B_2, \ldots, B_n$$
166
+ that form a `partition` of the sample space—meaning that the events do not overlap and together they cover the entire sample space.
167
+ <br>Then we can express the probability of event `A` as:
168
+ $$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)$$
169
+
170
+ In many cases, we can use a single intermediate event to compute:
171
+
172
+ $$P(A) = P(A|B) \times P(B) + P(A|\text{not }{B}) \times P(\text{not }{B})$$
173
+
174
+ This is a very useful tool in probability theory and is used in many real-world applications.
175
+
176
+ ## The Monty Hall problem
177
+ 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`.
178
+
179
+ The problem is as follows:
180
+ - You are a contestant on the show and are presented with `three doors`.
181
+ - Behind one of the doors is a `car`, and behind the other two are `goats`.
182
+ - You choose a door to begin with.
183
+ - The host, who knows what is behind each door, opens one of the other two doors to reveal a `goat`.
184
+ - You are then given the option to change your choice to the other unopened door.
185
+
186
+ The question is: `should you change your choice or stick with your initial choice? 🤔`
187
+ <br><br>
188
+ You can play around with the following simulation to see how the probabilities change as you make your choices.
189
+ """
190
+ )
191
+
192
+ with gr.Column():
193
+ with gr.Row():
194
+ shuffle_doors = gr.Button(value="Shuffle Doors")
195
+ reset_game = gr.Button(value="Reset Game", visible=False)
196
+ with gr.Row():
197
+ choose_1 = gr.Button(value="Choose Door n°1", visible=False)
198
+ choose_2 = gr.Button(value="Choose Door n°2", visible=False)
199
+ choose_3 = gr.Button(value="Choose Door n°3", visible=False)
200
+ reveal_first = gr.Button(value="Reveal First Goat", visible=False)
201
+ reveal_all = gr.Button(value="Reveal Remaining Doors", visible=False)
202
+ with gr.Row():
203
+ door_1 = gr.Textbox(label="Door 1", value="", interactive=False)
204
+ door_1_ = gr.Textbox(value="🐐", interactive=False, visible=False)
205
+ choice_1 = gr.Textbox(value="", interactive=False, visible=False)
206
+ door_2 = gr.Textbox(label="Door 2", value="", interactive=False)
207
+ door_2_ = gr.Textbox(value="🚗", interactive=False, visible=False)
208
+ choice_2 = gr.Textbox(value="", interactive=False, visible=False)
209
+ door_3 = gr.Textbox(label="Door 3", value="", interactive=False)
210
+ door_3_ = gr.Textbox(value="🐐", interactive=False, visible=False)
211
+ choice_3 = gr.Textbox(value="", interactive=False, visible=False)
212
+
213
+ def game_initial_state():
214
+ return (
215
+ gr.update(visible=True),
216
+ gr.update(visible=False),
217
+ gr.update(visible=False),
218
+ gr.update(visible=False),
219
+ gr.update(visible=False),
220
+ gr.update(visible=False),
221
+ gr.update(visible=False)
222
+ )
223
+
224
+ def after_shuffle_doors():
225
+ return (
226
+ gr.update(visible=False),
227
+ gr.update(visible=True),
228
+ gr.update(visible=True),
229
+ gr.update(visible=True),
230
+ gr.update(visible=False),
231
+ gr.update(visible=False),
232
+ gr.update(visible=False)
233
+ )
234
+
235
+ def after_door_choice():
236
+ return (
237
+ gr.update(visible=False),
238
+ gr.update(visible=False),
239
+ gr.update(visible=False),
240
+ gr.update(visible=False),
241
+ gr.update(visible=True),
242
+ gr.update(visible=False),
243
+ gr.update(visible=False)
244
+ )
245
+
246
+ def after_first_reveal():
247
+ return (
248
+ gr.update(visible=False),
249
+ gr.update(visible=False),
250
+ gr.update(visible=False),
251
+ gr.update(visible=False),
252
+ gr.update(visible=False),
253
+ gr.update(visible=True),
254
+ gr.update(visible=False)
255
+ )
256
+
257
+ def after_final_reveal():
258
+ return (
259
+ gr.update(visible=False),
260
+ gr.update(visible=False),
261
+ gr.update(visible=False),
262
+ gr.update(visible=False),
263
+ gr.update(visible=False),
264
+ gr.update(visible=False),
265
+ gr.update(visible=True)
266
+ )
267
+
268
+ def show_choice_buttons():
269
+ gr.update(choose_1, visible=True)
270
+ gr.update(choose_2, visible=True)
271
+ gr.update(choose_3, visible=True)
272
+
273
+ def hide_reveal_buttons():
274
+ gr.update(reveal_first, visible=False)
275
+ gr.update(reveal_all, visible=False)
276
+
277
+ def show_first_reveal_button():
278
+ return (
279
+ gr.update(visible=True),
280
+ gr.update(visible=False)
281
+ )
282
+
283
+ def show_final_reveal_button():
284
+ gr.update(reveal_first, visible=True)
285
+ gr.update(reveal_all, visible=True)
286
+
287
+ shuffle_doors.click(
288
+ lambda: list(random.sample(["🚗", "🐐", "🐐"], 3)) + [""] * 3,
289
+ inputs=[],
290
+ outputs=[door_1_, door_2_, door_3_, door_1, door_2, door_3]
291
+ )
292
+ shuffle_doors.click(
293
+ after_shuffle_doors,
294
+ inputs=[], outputs=[shuffle_doors, choose_1, choose_2, choose_3, reveal_first, reveal_all, reset_game]
295
+ )
296
+
297
+ choose_1.click(lambda: [1, 0, 0], inputs=[], outputs=[choice_1, choice_2, choice_3])
298
+ choose_1.click(after_door_choice, inputs=[], outputs=[shuffle_doors, choose_1, choose_2, choose_3, reveal_first, reveal_all, reset_game])
299
+
300
+ choose_2.click(lambda: [0, 1, 0], inputs=[], outputs=[choice_1, choice_2, choice_3])
301
+ choose_2.click(after_door_choice, inputs=[], outputs=[shuffle_doors, choose_1, choose_2, choose_3, reveal_first, reveal_all, reset_game])
302
+
303
+ choose_3.click(lambda: [0, 0, 1], inputs=[], outputs=[choice_1, choice_2, choice_3])
304
+ choose_3.click(after_door_choice, inputs=[], outputs=[shuffle_doors, choose_1, choose_2, choose_3, reveal_first, reveal_all, reset_game])
305
+
306
+ reveal_first.click(
307
+ # Disgusting but fun one-liner to reveal the first goat
308
+ 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)],
309
+ inputs=[door_1_, door_2_, door_3_, choice_1, choice_2, choice_3],
310
+ outputs=[door_1, door_2, door_3]
311
+ )
312
+ reveal_first.click(after_first_reveal, inputs=[], outputs=[shuffle_doors, choose_1, choose_2, choose_3, reveal_first, reveal_all, reset_game])
313
+
314
+ reveal_all.click(
315
+ lambda a, b, c: [a, b, c],
316
+ inputs=[door_1_, door_2_, door_3_],
317
+ outputs=[door_1, door_2, door_3]
318
+ )
319
+ reveal_all.click(
320
+ after_final_reveal,
321
+ inputs=[],
322
+ outputs=[shuffle_doors, choose_1, choose_2, choose_3, reveal_first, reveal_all, reset_game]
323
+ )
324
+
325
+ reset_game.click(
326
+ lambda: ["", "", ""],
327
+ inputs=[],
328
+ outputs=[door_1, door_2, door_3]
329
+ )
330
+ reset_game.click(
331
+ game_initial_state,
332
+ inputs=[],
333
+ outputs=[shuffle_doors, choose_1, choose_2, choose_3, reveal_first, reveal_all, reset_game]
334
+ )
335
+
336
+ gr.Markdown(
337
+ r"""
338
+ <br>
339
+ <details>
340
+ <summary>Show Solution</summary>
341
+ 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.
342
+ However, the probability of winning the car changes according to whether you change your choice or not. Let's see how!
343
+
344
+ Let's call the event of winning the car after changing doors: `X`. Then:
345
+ $$P(X) = P(X | \text{🚗}) \times P(\text{🚗}) + P(X | \text{🐐}) \times P(\text{🐐})$$
346
+
347
+ When we change our door and we have initially chosen the car, we lose. So:
348
+ $$P(X | \text{🚗}) = 0$$
349
+
350
+ When we change our door and we have initially chosen a goat, we win. So:
351
+ $$P(X | \text{🐐}) = 1$$
352
+
353
+ There are two goats and one car, so:
354
+ $$P(\text{🚗}) = \frac{1}{3}$$
355
+ $$P(\text{🐐}) = \frac{2}{3}$$
356
+
357
+ Therefore:
358
+ $$P(X) = 0 \times \frac{1}{3} + 1 \times \frac{2}{3} = \frac{2}{3}$$
359
+
360
+ As a result, you have twice the chances of winning the car if you change your choice! 🎉
361
+ </details>
362
+
363
+ ## Independence of Events
364
+ 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.
365
+
366
+ 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.
367
+
368
+ Mathematically, two events `A` and `B` are `independent` if and only if:
369
+ $$P(A \cap B) = P(A) \times P(B)$$
370
+
371
+ 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:
372
+ $$P(A \cap B) = 0 \neq P(A) \times P(B)$$
373
+
374
+ 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:
375
+ $$P(A|B) = \frac{P(A \cap B)}{P(B)} = \frac{P(A) \times P(B)}{P(B)} = P(A)$$
376
+
377
+ The END.
378
+ """
379
+ )
380
+
381
+
382
+ if __name__ == "__main__":
383
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ # This file is autogenerated by pip-compile with Python 3.10
3
+ # by the following command:
4
+ #
5
+ # pip-compile requirements.in
6
+ #
7
+ aiofiles==23.2.1
8
+ # via gradio
9
+ altair==5.2.0
10
+ # via gradio
11
+ annotated-types==0.6.0
12
+ # via pydantic
13
+ anyio==4.3.0
14
+ # via
15
+ # httpx
16
+ # starlette
17
+ attrs==23.2.0
18
+ # via
19
+ # jsonschema
20
+ # referencing
21
+ certifi==2024.2.2
22
+ # via
23
+ # httpcore
24
+ # httpx
25
+ # requests
26
+ charset-normalizer==3.3.2
27
+ # via requests
28
+ click==8.1.7
29
+ # via
30
+ # typer
31
+ # uvicorn
32
+ colorama==0.4.6
33
+ # via typer
34
+ contourpy==1.2.0
35
+ # via matplotlib
36
+ cycler==0.12.1
37
+ # via matplotlib
38
+ exceptiongroup==1.2.0
39
+ # via anyio
40
+ fastapi==0.110.0
41
+ # via gradio
42
+ ffmpy==0.3.2
43
+ # via gradio
44
+ filelock==3.13.1
45
+ # via huggingface-hub
46
+ fonttools==4.49.0
47
+ # via matplotlib
48
+ fsspec==2024.2.0
49
+ # via
50
+ # gradio-client
51
+ # huggingface-hub
52
+ gradio==4.19.2
53
+ # via -r requirements.in
54
+ gradio-client==0.10.1
55
+ # via gradio
56
+ h11==0.14.0
57
+ # via
58
+ # httpcore
59
+ # uvicorn
60
+ httpcore==1.0.4
61
+ # via httpx
62
+ httpx==0.27.0
63
+ # via
64
+ # gradio
65
+ # gradio-client
66
+ huggingface-hub==0.21.4
67
+ # via
68
+ # gradio
69
+ # gradio-client
70
+ idna==3.6
71
+ # via
72
+ # anyio
73
+ # httpx
74
+ # requests
75
+ importlib-resources==6.1.3
76
+ # via gradio
77
+ jinja2==3.1.3
78
+ # via
79
+ # altair
80
+ # gradio
81
+ jsonschema==4.21.1
82
+ # via altair
83
+ jsonschema-specifications==2023.12.1
84
+ # via jsonschema
85
+ kiwisolver==1.4.5
86
+ # via matplotlib
87
+ markdown-it-py==3.0.0
88
+ # via rich
89
+ markupsafe==2.1.5
90
+ # via
91
+ # gradio
92
+ # jinja2
93
+ matplotlib==3.8.3
94
+ # via gradio
95
+ mdurl==0.1.2
96
+ # via markdown-it-py
97
+ numpy==1.26.4
98
+ # via
99
+ # altair
100
+ # contourpy
101
+ # gradio
102
+ # matplotlib
103
+ # pandas
104
+ orjson==3.9.15
105
+ # via gradio
106
+ packaging==23.2
107
+ # via
108
+ # altair
109
+ # gradio
110
+ # gradio-client
111
+ # huggingface-hub
112
+ # matplotlib
113
+ pandas==2.2.1
114
+ # via
115
+ # altair
116
+ # gradio
117
+ pillow==10.2.0
118
+ # via
119
+ # gradio
120
+ # matplotlib
121
+ pydantic==2.6.3
122
+ # via
123
+ # fastapi
124
+ # gradio
125
+ pydantic-core==2.16.3
126
+ # via pydantic
127
+ pydub==0.25.1
128
+ # via gradio
129
+ pygments==2.17.2
130
+ # via rich
131
+ pyparsing==3.1.2
132
+ # via matplotlib
133
+ python-dateutil==2.9.0.post0
134
+ # via
135
+ # matplotlib
136
+ # pandas
137
+ python-multipart==0.0.9
138
+ # via gradio
139
+ pytz==2024.1
140
+ # via pandas
141
+ pyyaml==6.0.1
142
+ # via
143
+ # gradio
144
+ # huggingface-hub
145
+ referencing==0.33.0
146
+ # via
147
+ # jsonschema
148
+ # jsonschema-specifications
149
+ requests==2.31.0
150
+ # via huggingface-hub
151
+ rich==13.7.1
152
+ # via typer
153
+ rpds-py==0.18.0
154
+ # via
155
+ # jsonschema
156
+ # referencing
157
+ ruff==0.3.1
158
+ # via gradio
159
+ semantic-version==2.10.0
160
+ # via gradio
161
+ shellingham==1.5.4
162
+ # via typer
163
+ six==1.16.0
164
+ # via python-dateutil
165
+ sniffio==1.3.1
166
+ # via
167
+ # anyio
168
+ # httpx
169
+ starlette==0.36.3
170
+ # via fastapi
171
+ tomlkit==0.12.0
172
+ # via gradio
173
+ toolz==0.12.1
174
+ # via altair
175
+ tqdm==4.66.2
176
+ # via huggingface-hub
177
+ typer[all]==0.9.0
178
+ # via gradio
179
+ typing-extensions==4.10.0
180
+ # via
181
+ # altair
182
+ # anyio
183
+ # fastapi
184
+ # gradio
185
+ # gradio-client
186
+ # huggingface-hub
187
+ # pydantic
188
+ # pydantic-core
189
+ # typer
190
+ # uvicorn
191
+ tzdata==2024.1
192
+ # via pandas
193
+ urllib3==2.2.1
194
+ # via requests
195
+ uvicorn==0.27.1
196
+ # via gradio
197
+ websockets==11.0.3
198
+ # via gradio-client