import random
import gradio as gr
OUTCOMES = [1, 2, 3, 4, 5, 6]
# Function to simulate a simple die roll experiment
def roll_die():
result = random.choice(OUTCOMES)
return result
# Roll the die and check if the result is even
def test_event():
result = roll_die()
return result, "Yes!" if result % 2 == 0 else "No.."
# Make sure inputs are correct
def check_inputs(favorable_outcomes, possible_outcomes, test_subset=False):
if favorable_outcomes == "" or possible_outcomes == "":
raise Exception("Please input the set of favorable and possible outcomes")
favorable_outcomes = favorable_outcomes.split(",")
possible_outcomes = possible_outcomes.split(",")
try:
favorable_outcomes = list(map(int, favorable_outcomes))
except ValueError:
raise Exception("Please input a valid set of favorable outcomes")
try:
possible_outcomes = list(map(int, possible_outcomes))
except ValueError:
raise Exception("Please input a valid set of possible outcomes")
if test_subset:
if not set(favorable_outcomes).issubset(set(possible_outcomes)):
raise Exception("Favorable outcomes should be a subset of possible outcomes")
return favorable_outcomes, possible_outcomes
# Compute the probability of an event
def compute_event_probability(favorable_outcomes, possible_outcomes):
try:
favorable_outcomes, possible_outcomes = check_inputs(favorable_outcomes, possible_outcomes, test_subset=True)
except Exception as e:
return str(e)
probability = len(favorable_outcomes) / len(possible_outcomes)
return probability
# Produce randomized favorable and possible outcomes
def randomize_outcomes():
n_favorable_outcomes = random.randint(1, 6)
favorable_outcomes = random.sample(OUTCOMES, n_favorable_outcomes)
possible_outcomes = OUTCOMES
return ",".join(map(str, favorable_outcomes)), ",".join(map(str, possible_outcomes))
# Join two events
def join_events(event_a, event_b):
try:
event_a, event_b = check_inputs(event_a, event_b)
except Exception as e:
return str(e)
event_a_or_b = sorted(set(event_a + event_b))
return ",".join(map(str, event_a_or_b))
# Create a Gradio interface
css = """
.gradio-container {
width: 40%!important;
min-width: 800px;
}
"""
with gr.Blocks(css=css) as demo:
gr.Markdown(
"""
# Probability Basics (Pt. 2)
Let's learn about more fundamental principles of Probability theory!
Welcome to this new segment of the [Probability Basics series](https://huggingface.co/spaces/helboukkouri/probability-basics-pt1)! 🎉
In this part we will discuss the core principles, also known as `axioms`, that shape the mathematical foundation for probability calculations,
eventually allowing us to tackle more complex problems and understand the behavior of random events."""
)
gr.Markdown(
r"""
## Axioms of Probability
In order to start working with more serious probabilistic systems, we need to establish a set of `axioms` that will guide our calculations.
Just like in geometry, where we first accept general rules like `two parallel lines never meet` before starting to deal for larger problems; or in algebra, where we accept that `a number multiplied by zero is zero` before solving equations, we need to establish some general rules for probability theory as well.
### Axiom 1: Non-negativity
*The probability of an event is always a non-negative number. That is, it is always `greater than or equal to zero`.*
$$\text{For any Event:} \quad P(\text{Event}) \geq 0 $$
This is because the probability of an event is a [measure](https://en.wikipedia.org/wiki/Measure_(mathematics)) of how likely it is to occur, it can be seen as the ratio of favorable outcomes to possible outcomes, and that, for all intents and purposes, is assumed to always be a positive (or zero) number.
"""
)
with gr.Column():
with gr.Row():
randomize = gr.Button(value="Randomize favorable outcomes")
probability = gr.Button(value="Compute")
with gr.Row():
with gr.Column():
favorable_outcomes = gr.Textbox(label="Favorable Outcomes", value="2, 4, 6")
possible_outcomes = gr.Textbox(label="Possible Outcomes", value="1, 2, 3, 4, 5, 6")
with gr.Column():
probability_output = gr.Textbox(label="Probability", placeholder="", interactive=False)
static_probability_output = gr.Textbox(label="Is the probability positive?", placeholder="", interactive=False)
probability_output.change(lambda: "Yes!", [], [static_probability_output])
randomize.click(randomize_outcomes, [], [favorable_outcomes, possible_outcomes])
probability.click(
lambda a, b: f"The probability of the event is: {compute_event_probability(a, b):.0%}",
[favorable_outcomes, possible_outcomes], [probability_output]
)
gr.Markdown(
r"""
### Axiom 2: Total Probability
*The probability of the entire `sample space` is always equal to `1` (or in other words `100%`).*
$$\text{For any Sample Space:} \quad P(\text{Sample Space}) = 1 $$
If we look back at the definition of the `sample space`, we can see that it contains **all possible outcomes**. This means that the probability of the **entire sample space** is the same as the probability of **all possible outcomes combined**, which is the same as the probability of the event `something happens` which is always certain to happen.
When dealing with a six-sided die, the probability of getting any of the possible faces is always `1`.
(assuming it cannot land on its edge or something else 😅)
"""
)
with gr.Column():
with gr.Row():
randomize_total_probability = gr.Button(value="Randomize possible outcomes")
total_probability = gr.Button(value="Compute")
with gr.Row():
with gr.Column():
all_possible_outcomes = gr.Textbox(label="Favorable Outcomes", value="1, 2, 3, 4, 5, 6", interactive=False)
all_possible_outcomes_ = gr.Textbox(label="Possible Outcomes", value="1, 2, 3, 4, 5, 6")
with gr.Column():
total_probability_output = gr.Textbox(label="Probability of all possible outcomes", placeholder="", interactive=False)
randomize_total_probability.click(lambda: [randomize_outcomes()[0]]*2, [], [all_possible_outcomes, all_possible_outcomes_])
all_possible_outcomes_.change(lambda x: x, [all_possible_outcomes_], [all_possible_outcomes])
total_probability.click(
lambda a, b: f"The probability of the event is: {compute_event_probability(a, b):.0%}",
[all_possible_outcomes, all_possible_outcomes_], [total_probability_output]
)
gr.Markdown(
r"""
### Axiom 3: Additivity
*The probability of the `union` of two or more `disjoint events` is the sum of their individual probabilities.*
- The `union` of two events is the event that `either one or the other happens, or both`.
- When events are `disjoint`, it means that they `cannot happen at the same time`, so the probabilities sum up.
$$\text{For any disjoint Events A and B:} \quad P(\text{Event A} \cup \text{Event B}) = P(\text{Event A}) + P(\text{Event B}) $$
"""
)
with gr.Column():
with gr.Row():
randomize_disjoint = gr.Button(value="Randomize events A and B")
compute_disjoint = gr.Button(value="Compute")
with gr.Row():
with gr.Column():
favorable_a = gr.Textbox(label="Favorable Outcomes: A", value="1, 4, 6")
favorable_b = gr.Textbox(label="Favorable Outcomes: B", value="2, 4")
favorable_a_or_b = gr.Textbox(label="Favorable Outcomes: A U B", value="1, 2, 4, 6", interactive=False)
possible_outcomes_disjoint = gr.Textbox(label="Possible Outcomes", value="1, 2, 3, 4, 5, 6")
with gr.Column():
disjoint_probability_output_a = gr.Textbox(label="Probability: A", placeholder="", interactive=False)
disjoint_probability_output_b = gr.Textbox(label="Probability: B", placeholder="", interactive=False)
disjoint_probability_output_a_b = gr.Textbox(label="Probability: A U B", placeholder="", interactive=False)
disjoint_probability_output_a_p_b = gr.Textbox(label="(Probability A) + (Probability B)", placeholder="", interactive=False)
with gr.Row():
events_are_disjoint = gr.Textbox(label="Are the events disjoint?", placeholder="", interactive=False)
third_is_respected = gr.Textbox(label="Is the sum equal to the probability of the union?", placeholder="", interactive=False)
favorable_a.change(
lambda a, b: "No.." if (lambda x, y: len(set(x).intersection(set(y))))(*check_inputs(a, b)) else "Yes!",
[favorable_a, favorable_b], [events_are_disjoint]
)
favorable_b.change(
lambda a, b: "No.." if (lambda x, y: len(set(x).intersection(set(y))))(*check_inputs(a, b)) else "Yes!",
[favorable_a, favorable_b], [events_are_disjoint]
)
favorable_a.change(join_events, [favorable_a, favorable_b], [favorable_a_or_b])
favorable_b.change(join_events, [favorable_a, favorable_b], [favorable_a_or_b])
randomize_disjoint.click(lambda: (*randomize_outcomes(), randomize_outcomes()[0]), [], [favorable_a, possible_outcomes_disjoint, favorable_b])
compute_disjoint.click(
lambda a, b, c, d: (
f"The probability of the event is: {compute_event_probability(a, d):.0%}",
f"The probability of the event is: {compute_event_probability(b, d):.0%}",
f"The joint probability is: {compute_event_probability(c, d):.0%}",
f"The sum of probabilities is: {compute_event_probability(a, d) + compute_event_probability(b, d):.0%}",
"Yes!" if (compute_event_probability(a, d) + compute_event_probability(b, d) == compute_event_probability(c, d)) else "No.."
),
[favorable_a, favorable_b, favorable_a_or_b, possible_outcomes_disjoint],
[disjoint_probability_output_a, disjoint_probability_output_b, disjoint_probability_output_a_b, disjoint_probability_output_a_p_b, third_is_respected]
)
gr.Markdown(
r"""
If you are curious about how to compute the probability of event that at NOT disjoint, this can also be achieved with a simple calculation.
When events are `not disjoint`, there is, included in the probability of each event, the probability that both events happen at the same time.
To correct for this, and not count it twice, we need to subtract this probability from the total:
$$\text{For any Events A and B:} \quad P(A \cup B) = P(A) + P(B) - P(A \cap B) $$
Where the `intersection` of two events is the event that `both happen at the same time`, denoted as `A ∩ B`.
"""
)
# Launch the Blocks interface
demo.launch()