|
import os |
|
import json |
|
import threading |
|
import gradio as gr |
|
import statistics |
|
from scipy import stats |
|
import numpy as np |
|
import altair as alt |
|
import pandas as pd |
|
|
|
DATA_DIR = './storage' |
|
DATA_FILE = os.path.join(DATA_DIR, 'guesses.json') |
|
lock = threading.Lock() |
|
|
|
def ensure_data_directory(): |
|
os.makedirs(DATA_DIR, exist_ok=True) |
|
|
|
def load_guesses(): |
|
ensure_data_directory() |
|
if not os.path.exists(DATA_FILE): |
|
with open(DATA_FILE, 'w') as f: |
|
json.dump([], f) |
|
with open(DATA_FILE, 'r') as f: |
|
return json.load(f) |
|
|
|
def save_guesses(guesses): |
|
with open(DATA_FILE, 'w') as f: |
|
json.dump(guesses, f) |
|
|
|
def add_guess(guess): |
|
with lock: |
|
guesses = load_guesses() |
|
guesses.append(guess) |
|
save_guesses(guesses) |
|
message = compute_statistics(guesses, include_message=True) |
|
chart = generate_plot(guesses) |
|
return message, None, chart |
|
|
|
def get_current_results(): |
|
with lock: |
|
guesses = load_guesses() |
|
message = compute_statistics(guesses, include_message=False) |
|
chart = generate_plot(guesses) |
|
return message, chart |
|
|
|
def compute_statistics(guesses, include_message): |
|
n = len(guesses) |
|
if n == 0: |
|
return "No guesses have been made yet." |
|
|
|
average = sum(guesses) / n |
|
median = statistics.median(guesses) |
|
|
|
Q1 = np.percentile(guesses, 25) |
|
Q3 = np.percentile(guesses, 75) |
|
IQR = Q3 - Q1 |
|
|
|
|
|
message = ( |
|
(f"Your guess has been recorded.\n" if include_message else "") + |
|
f"Current average of all {n} guesses: {average:.2f}\n" + |
|
f"Median of all guesses: {median:.2f}\n" + |
|
f"Interquartile Range (IQR): {IQR:.2f}\n" + |
|
"(The IQR is the range between the 25th percentile and 75th percentile of the data.)\n" |
|
) |
|
|
|
if n >= 2: |
|
|
|
s = statistics.stdev(guesses) |
|
|
|
SE = s / (n ** 0.5) |
|
|
|
df = n - 1 |
|
|
|
confidence_level = 0.95 |
|
alpha = 1 - confidence_level |
|
|
|
t_value = stats.t.ppf(1 - alpha/2, df) |
|
|
|
ME = t_value * SE |
|
|
|
ci_lower = average - ME |
|
ci_upper = average + ME |
|
|
|
message += f"95% confidence interval for the average: ({ci_lower:.2f}, {ci_upper:.2f})" |
|
else: |
|
|
|
message += "Not enough data to compute confidence interval for the average." |
|
|
|
return message |
|
|
|
def generate_plot(guesses): |
|
if len(guesses) < 2: |
|
return None |
|
|
|
|
|
df = pd.DataFrame({'Guess': guesses}) |
|
|
|
|
|
histogram = alt.Chart(df).mark_bar().encode( |
|
alt.X('Guess', bin=alt.Bin(maxbins=20), title='Guess Value'), |
|
alt.Y('count()', title='Frequency'), |
|
tooltip=[alt.Tooltip('count()', title='Frequency')] |
|
).properties( |
|
title='Histogram of Guesses', |
|
width=500, |
|
height=300 |
|
) |
|
|
|
|
|
df_sorted = df.sort_values('Guess').reset_index(drop=True) |
|
df_sorted['ECDF'] = (df_sorted.index + 1) / len(df_sorted) |
|
|
|
cdf = alt.Chart(df_sorted).mark_line(interpolate='step-after').encode( |
|
x=alt.X('Guess', title='Guess Value'), |
|
y=alt.Y('ECDF', title='Cumulative Probability'), |
|
tooltip=[ |
|
alt.Tooltip('Guess', title='Guess Value'), |
|
alt.Tooltip('ECDF', title='Cumulative Probability', format='.2f') |
|
] |
|
).properties( |
|
title='Cumulative Distribution Function (CDF)', |
|
width=500, |
|
height=300 |
|
) |
|
|
|
|
|
combined_chart = alt.vconcat(histogram, cdf).configure_title( |
|
fontSize=16, |
|
anchor='middle' |
|
).interactive() |
|
|
|
return combined_chart |
|
|
|
with gr.Blocks() as demo: |
|
gr.Markdown("# Collective Guessing Game") |
|
gr.Markdown("Submit your guess and contribute to the global statistics!") |
|
guess_input = gr.Number(label="Enter your guess") |
|
submit_button = gr.Button("Submit Guess") |
|
with gr.Accordion("View Current Results!", open=False): |
|
output_text = gr.Textbox(label="Results", lines=10) |
|
output_plot = gr.Plot(label="Data Visualization") |
|
refresh_button = gr.Button("Refresh Results") |
|
submit_button.click( |
|
fn=add_guess, |
|
inputs=guess_input, |
|
outputs=[output_text, guess_input, output_plot] |
|
) |
|
refresh_button.click( |
|
fn=get_current_results, |
|
outputs=[output_text, output_plot] |
|
) |
|
demo.load(fn=get_current_results, outputs=[output_text, output_plot]) |
|
|
|
demo.launch() |