Spaces:
Runtime error
Runtime error
File size: 10,277 Bytes
1539f5b 2f02b79 1539f5b d8658ea 2f02b79 4cadf36 ca42357 4cadf36 1539f5b d8658ea 2fbd96d 1539f5b ca42357 1539f5b | 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 | from matplotlib import pyplot as plt
import numpy as np
from collections import Counter,deque
import time
import panel as pn
import holoviews as hv
import random
class CustomDeque(deque):
def __init__(self, maxlen=None):
super().__init__(maxlen=maxlen)
def appendleft(self, x):
# Close the oldest figure before removing it
if len(self) == self.maxlen:
plt.close(self[-1])
super().appendleft(x)
def append(self, x):
# Close the oldest figure before removing it
if len(self) == self.maxlen:
plt.close(self[0])
super().append(x)
global_prob = []
chart_history = CustomDeque(maxlen=4)
def get_dice_prob_bargraph(events_num,equal,color='#7a5af5'):
global global_prob
probabilities = create_distribution(events_num = events_num, equal=equal)
global_prob = probabilities
if not np.isclose(sum(probabilities), 1):
raise ValueError("Probabilities must sum to 1.")
outcomes = np.arange(1,len(probabilities)+1,dtype=int)# the number of probabilities will tell us how many outcomes or sides of dice we have
np.arange(1,len(probabilities)+1,dtype=int)
fig, ax = setup_darkmode_axes(1.20,1.2)
ax.bar(outcomes, probabilities, tick_label=outcomes, color=color, alpha=0.7)
ax.set_xticks(np.linspace(0, len(probabilities), len(probabilities)+1))
ax.set_yticks(np.linspace(0, 1, 11))
ax.tick_params(axis='x', colors='white',labelsize=4,pad=1)
ax.tick_params(axis='y', colors='white',labelsize=4,pad=1)
ax.set_title("Distribution of Sides",color="w",fontsize=5)
ax.set_xlabel("Number of Sides",color="w",fontsize=6,labelpad=2)
ax.set_ylabel("Probability",color="w",fontsize=6,labelpad=2)
return fig
def roll_weighted_die(probabilities):
if not np.isclose(sum(probabilities), 1):
raise ValueError("Probabilities must sum to 1.")
# Define the possible outcomes of the die roll
outcomes = np.arange(1,len(probabilities)+1,dtype=int)# the number of probabilities will tell us how many outcomes or sides of dice we have
# Sample from the distribution
roll = np.random.choice(outcomes,p=probabilities)
return roll
def make_dice_hist(bins,dice_num=50,events_num=6, fig=None, ax=None,color='y'):
min_val = min(bins)
max_val = max(bins)
first_time = False
if fig is None:
fig, ax = setup_darkmode_axes(9, 2)
ax.set_xlabel("Dice Sum",color="w",fontsize=6)
ax.set_ylabel("Occured How Many Times",color="w",fontsize=6)
ax.clear()
ax.set_title(f"Dice Sum",color="w")
#bin_edges = np.arange(min_val, max_val + 1.5) - 0.5 # Create bin edges the 1.5 will get the last bin in (it needs both left and right edges, and the -.5 will shift them all so they are
#bin_edges = np.arange(min_val, max_val + 2) - 0.5
#centered around each bin starting
#ax.clear()
#counts, bins, patches = ax.hist(bins,bins=bin_edges,density=False, alpha=0.8,rwidth=1, color='y', histtype='bar', orientation = 'vertical', edgecolor='w') # counts are the y or height of each bin,
counts, bins, patches = ax.hist(bins,density=False, alpha=0.6,rwidth=1, color=color, histtype='bar', orientation = 'vertical', edgecolor='w') # counts are the y or height of each bin,
# Set x-ticks to match the bin edges
#bin_midpoints = bins[:-1] + np.diff(bins)/2 # for each bin left side except the last one (because that is the right side) add to it the difference of each pair of bins /2 (midpoint) thus storing
# the midpoints of the bins
# ax.tick_params(axis='x', labelsize=6, colors="white") # Adjust the font size here
#ax.tick_params(axis='y', labelsize=6, colors="white") # Adjust the font size here
#ax.set_xticks(bin_midpoints[::2]) # put a tick at each mid point of each 10th' bin
#ax.set_yticks(np.linspace(0, max(counts)*1.25, 3))
return fig,ax,counts,bins
def create_distribution(events_num = 6, equal=True):
# Generate random numbers
if not equal:
random_numbers = np.random.random(events_num) # random numbers between 0-1 of length num_events to make a weighted distribution (not equal for all)
# Normalize to get probabilities that add up to 1
probabilities = random_numbers / np.sum(random_numbers)
else:
probabilities = [1/events_num] * events_num #
return probabilities
def create_sum(dice_num: int = 20,probabilities=[1/6]*6) -> list :
return sum(roll_weighted_die(probabilities) for _ in range(dice_num)) # roll the dice the number of times needed and sum it them up
def run_trials(trials_num = 10, dice_num: int =20,probabilities=[1/6]*6):
sum_list= []
fig = None
ax = None
for i in range(trials_num):
sum_list.append(create_sum(dice_num,probabilities))
# create/update our histogram
(fig,ax,counts,bins) = make_dice_hist(sum_list,i+1,dice_num,len(probabilities),fig,ax)
plt.show()
time.sleep(.25)
return sum_list
def setup_darkmode_axes(fig_width = 6, fig_height= 2, grid_on = True):
fig, ax = plt.subplots(nrows = 1,figsize=(fig_width,fig_height),dpi=400,layout='compressed')
fig.set_facecolor("#212529")
ax.set_facecolor("black")
if grid_on:
ax.grid(color='white', linewidth=.6, alpha=0.5, zorder=0)
else:
ax.set_axis_off()
return fig,ax
def update_hex_value(event):
print('updating color picker hex value')
color_picker_hex.value = event.new
def show_chart_history(running,local_chart_history = None):
print(f'show_chart_history called chart history {len(chart_history)}\n')
if not running:
return
col_layout = pn.Column()
# Add each figure as a separate panel to the row layout
for fig in local_chart_history:
panel = pn.pane.Matplotlib(fig, dpi=400, format="svg",sizing_mode="stretch_width")
col_layout.append(panel)
return col_layout
def run_simulation(running,num_sums=50,num_dice=30,color='yellow'):
run_input.disabled = True # Disable the button
global global_prob,chart_history
fig = None
ax = None
try:
num_sides = len(global_prob)
if not running:
return # if the panel isn't active just return
sum_list= []
start_time = time.time()
for i in range(num_sums):
loop_start = time.time()
start_time = time.time()
sum_list.append(create_sum(num_dice,global_prob))
end_time = time.time()
elapsed_time = end_time - start_time
# print(f"sum list took {elapsed_time:.4f}")
start_time = time.time()
if not (i+1) % 50: # only update every 10th sum
start_time = time.time() # Record the start time
(fig,ax,counts,bins) = make_dice_hist(sum_list,20,len(global_prob),fig,ax,color)
ax.set_title(f"Dice Sum #{i+1}/{num_sums} using {num_dice} {num_sides}-sided dice",color="w")
ax.set_xlabel("Dice Sum",color="w",fontsize=6)
ax.set_ylabel("Occured How Many Times",color="w")
ax.tick_params(axis='x', labelsize=5, colors="w",pad=1) # Adjust the font size here
ax.tick_params(axis='y', labelsize=5, colors="w",pad=1) # Adjust the font size here
ax.grid(color='white', linewidth=.6, alpha=0.2, zorder=0)
end_time = time.time()
elapsed_time = end_time - start_time
# print(f"Make dice hist took {elapsed_time:.4f}")
start_time = time.time() # Record the start time
layout=pn.pane.Matplotlib(fig, dpi=400, format="svg",sizing_mode="stretch_width")
end_time = time.time()
elapsed_time = end_time - start_time
# print(f"Setting the layout to fig took {elapsed_time:.4f}")
yield layout
time.sleep(.2)
#time.sleep(.15)
end_time = time.time()
elapsed_time = end_time - start_time
# print(f"Yield took {elapsed_time:.4f}")
loop_end = time.time()
elapsed_time = loop_end - loop_start
yield layout
# print(f"Loop took {elapsed_time:.4f}\n\n")
finally:
run_input.disabled = False # Re-enable the button after the simulation
if fig:
chart_history.appendleft(fig)
color_picker = pn.widgets.ColorPicker(name='Bar Color', value='#5e43f3')
color_picker_hex = pn.widgets.StaticText(name='Hex Value', value=color_picker.value)
str_pane1 = pn.pane.Str('Sides Probability Equal?', width=110)
random_switch = pn.widgets.Switch(name='Switch1', value=True, width=25)
num_events = pn.widgets.IntSlider(name='Number of Sides',start=2, end=12,step=1,value=6,width=150)
num_sums = pn.widgets.IntSlider(name='Run For How Long',start=50, end=20000,step=10,value=50,width=150)
num_dice = pn.widgets.IntSlider(name='Number of Dice (size of sum)',start=1, end=100,step=1,value=2,width=150)
#bind a plot to different params
bplot = pn.bind(get_dice_prob_bargraph, events_num=num_events, equal=random_switch,color=color_picker)
layout = pn.pane
# Attach the callback function to the color_picker's value parameter
color_picker.param.watch(update_hex_value, 'value') # Define callback function to update plots
pn.extension(design='material',theme='dark',sizing_mode="stretch_width")
run_input = pn.widgets.Button(name="Run Simulation",icon="hand-click")
pn.template.MaterialTemplate(
title=f'Visualizing The Central Limit Theorm',
site="Statistics Playground",
header_background="#1a1625",
sidebar=['dice.png',num_events,str_pane1,random_switch,bplot,num_dice,num_sums,color_picker,run_input,
pn.Row('gaussian_dist.png')],
main=[pn.Card(pn.bind(run_simulation,run_input,num_sums=num_sums,num_dice=num_dice,color=color_picker),
title="Current Sim", sizing_mode='stretch_width',styles={'background': '#5e5a66'}),
pn.Card(pn.bind(show_chart_history,run_input,local_chart_history=chart_history),title="History (newest first)",sizing_mode='stretch_width',styles={'background': '#5e5a66'})
],
sidebar_width=210,
).servable()
|