import os import json import threading import gradio as gr import statistics from scipy import stats import numpy as np # Added import for numpy to compute percentiles 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) # Generate the Altair chart return message, None, chart # Return message, cleared input, and 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) # Calculate IQR Q1 = np.percentile(guesses, 25) Q3 = np.percentile(guesses, 75) IQR = Q3 - Q1 # Initialize the message 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: # Calculate sample standard deviation s = statistics.stdev(guesses) # Calculate standard error SE = s / (n ** 0.5) # Degrees of freedom df = n - 1 # Confidence level (e.g., 95%) confidence_level = 0.95 alpha = 1 - confidence_level # Calculate critical t-value t_value = stats.t.ppf(1 - alpha/2, df) # Calculate margin of error ME = t_value * SE # Calculate confidence interval ci_lower = average - ME ci_upper = average + ME # Append confidence interval to the message message += f"95% confidence interval for the average: ({ci_lower:.2f}, {ci_upper:.2f})" else: # Not enough data to compute confidence interval message += "Not enough data to compute confidence interval for the average." return message def generate_plot(guesses): if len(guesses) < 2: return None # Not enough data to plot # Convert the list of guesses into a Pandas DataFrame df = pd.DataFrame({'Guess': guesses}) # Histogram 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 ) # CDF 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 ) # Combine the two charts vertically 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") # Plot component for Altair charts 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()