# To bar, or not to bar - what is the intention?¶

post @ endlesspint.com

In [1]:
import numpy as np
import pandas as pd
from scipy import stats

import matplotlib.pyplot as plt
%matplotlib inline

In [2]:
# comfort level
L = 60.

# wishing to attend
N = 100.

# previous 'm' Friday nights
m = 2.

In [3]:
N = 100

def preferences(N):
return np.random.uniform(.0001,1,N)

np.random.seed(8)
patron_p = preferences(N)
patron_p[:10]

Out[3]:
array([0.87344206, 0.96854381, 0.86920762, 0.53090261, 0.23280506,
0.01149766, 0.43052577, 0.40241112, 0.5227224 , 0.47844396])
In [4]:
# https://stackoverflow.com/questions/22744577/plotting-basic-uniform-distribution-on-python
count, bins, ignored = plt.hist(patron_p, 25, facecolor='green')

In [5]:
def any_given_friday_night(patron_p, N=100, L=60, ind_friday=True):

tally = np.zeros(N)
# either one rand number or one for each, we'll see...
if ind_friday == True:
friday = np.random.uniform(.0001,1, N)
else:
friday = np.random.uniform(.0001, 1)

going_out = friday <= patron_p
staying_in = np.invert(going_out)

yes_cnt = np.sum(going_out)
nos_cnt = N - yes_cnt

if yes_cnt == L:    # bar at optimal capacity everyone choose right/wins
tally += going_out
tally += staying_in
#         print(tally)
elif yes_cnt < L:   # extra space at bar, should've gone out
tally += going_out
tally -= staying_in
#         print(tally)
else:               # overcrowded bar, should've stayed in
tally -= going_out
tally += staying_in
#         print(tally)

return yes_cnt, nos_cnt, tally, going_out

np.random.seed(9)
bar_y, bar_n, tally_run, _ = any_given_friday_night(patron_p, ind_friday=True)
print("the count that went out & stayed in, respectively: ", bar_y, bar_n)
print("the tally for the 'correct' choice: ", np.sum(tally_run==1))

the count that went out & stayed in, respectively:  57 43
the tally for the 'correct' choice:  57

In [6]:
yes = []
nos = []
tally_run = np.zeros(N)
# m = []  # historical winning strategy

# np.random.seed(10)
for i in range(100):
bar_y, bar_n, tally_run, going_out = any_given_friday_night(patron_p, ind_friday=False)
yes.append(bar_y)
nos.append(bar_n)

print(yes[-10:])
print(nos[-10:])
print(np.sum(tally_run==1))

m = [True if y<=60 else False for y in yes]
print(tally_run[-10:])
print(m[-10:])
np.zeros(10) + m[-10:] - np.invert(m[-10:])

[30, 30, 29, 24, 28, 87, 30, 9, 95, 85]
[70, 70, 71, 76, 72, 13, 70, 91, 5, 15]
15
[ 1. -1. -1. -1. -1. -1.  1. -1.  1. -1.]
[True, True, True, True, True, False, True, True, False, False]

Out[6]:
array([ 1.,  1.,  1.,  1.,  1., -1.,  1.,  1., -1., -1.])
In [7]:
# https://matplotlib.org/gallery/lines_bars_and_markers/bar_stacked.html
plt.figure(figsize=(16,8))
ind = np.arange(100)
p1 = plt.bar(ind, yes)
p2 = plt.bar(ind, nos, bottom=yes)
plt.axhline(y=60, linewidth=5, color='red')

Out[7]:
<matplotlib.lines.Line2D at 0x2524912ef28>

individual cut offs for going out/staying in result in too wild of swings, above just one example; later on, even with updating preferences, it remains eratic

## use 'm' to determine whether to up or lower the ps¶

In [8]:
# check tally, is it -3?

# review recent history, m[-2:], to determine go out more/less
# if 50/50 choose randomly

# determine by how much to update (+/-) ps

# plot ps hist over time

In [9]:
np.random.seed(8)
patron_p = preferences(N)
patron_p[8]

Out[9]:
0.5227224038854343
In [10]:
temp_pref = patron_p.copy()

yes = []
nos = []
tally_run = np.zeros(N)
m = []  # historical winning strategy
pref_08 = []
tally08_run = []
tally08 = []
bar_08 = []
m_rate = []

print("Sanity check, preferences for ten persons  (pre-run):\n")
print(patron_p[-10:])

bar_runs = 1000
d = -3  # losing threshold
last_m_nights = 4
change_rate = .1

for i in range(bar_runs):
bar_y, bar_n, tally, going_out = any_given_friday_night(temp_pref)
yes.append(bar_y)
nos.append(bar_n)
tally_run += tally
tally08_run.append(tally[8])

# historical winning strategy
if bar_y <= 60:
m.append(True)
else:
m.append(False)

pref_08.append(patron_p[8])
tally08.append(tally_run[8])

# check tally, is it -3?
# update directionally to recent past
m_recent = np.sum(m[-last_m_nights:])/np.min([last_m_nights, len(m)])
m_rate.append(m_recent)
#     print(m_recent)
temp_pref[tally_run <= d] += (temp_pref[tally_run <= d] - m_recent) * -change_rate
tally_run[tally_run <= d] = tally[tally_run <= d] * 0.

bar_08.append(going_out[8])

print("\nSanity check, preferences for ten persons (post-run):")
print(temp_pref[-10:])
print("\nLast ten 'yes's':")
print(yes[-10:])
print("\nLast ten 'no's':")
print(nos[-10:])
print("\nPublic memory ('shoulda gone out'):")
print(m[-10:])
print("\nEnding score for ten persons:")
print(tally_run[-10:])

# stats.describe(tally)

Sanity check, preferences for ten persons  (pre-run):

[0.17883914 0.61976344 0.56232716 0.76342827 0.80666567 0.29869534
0.13847087 0.63543437 0.00840754 0.78860441]

Sanity check, preferences for ten persons (post-run):
[0.45363968 0.69200839 0.56136785 0.76342827 0.80666567 0.49585112
0.51139845 0.63543437 0.5059887  0.78860441]

Last ten 'yes's':
[61, 57, 63, 61, 62, 54, 64, 60, 68, 55]

Last ten 'no's':
[39, 43, 37, 39, 38, 46, 36, 40, 32, 45]

Public memory ('shoulda gone out'):
[False, True, False, False, False, True, False, True, False, True]

Ending score for ten persons:
[42. 38. 30. 58. 56. 10. 12. 12.  4. 24.]

In [11]:
# a look at one 'customer' over time
# sanity check

pd.DataFrame(
{'bar': yes,
'home': nos,
'go_out': m,
'ps08': pref_08,
'+/-': tally08_run,
'ty08': tally08,
'bar08': bar_08,
'm_rate': m_rate
}, columns=['bar', 'home', 'go_out', 'bar08', 'ps08', '+/-', 'ty08', 'm_rate']).tail(10)

Out[11]:
bar home go_out bar08 ps08 +/- ty08 m_rate
990 61 39 False False 0.522722 1.0 1.0 0.25
991 57 43 True False 0.522722 -1.0 0.0 0.50
992 63 37 False True 0.522722 -1.0 -1.0 0.50
993 61 39 False False 0.522722 1.0 0.0 0.25
994 62 38 False False 0.522722 1.0 1.0 0.25
995 54 46 True False 0.522722 -1.0 0.0 0.25
996 64 36 False True 0.522722 -1.0 -1.0 0.25
997 60 40 True True 0.522722 1.0 0.0 0.50
998 68 32 False True 0.522722 -1.0 -1.0 0.50
999 55 45 True False 0.522722 -1.0 -2.0 0.50
In [12]:
# https://stackoverflow.com/questions/22744577/plotting-basic-uniform-distribution-on-python
count, bins, ignored = plt.hist(temp_pref, 25, facecolor='green')

# https://matplotlib.org/gallery/lines_bars_and_markers/bar_stacked.html
plt.figure(figsize=(16,8))
ind = np.arange(bar_runs)
p1 = plt.bar(ind, yes)
p2 = plt.bar(ind, nos, bottom=yes)
plt.axhline(y=60, linewidth=5, color='red')

Out[12]:
<matplotlib.lines.Line2D at 0x252493caf98>
• timeseries: capture runnnig tally for all N (100)
In [13]:
# what if they weren't describing the threshold, e.g., -3, as an absolute tally
# but rather a short term one, say -3 in last dozen, ten, or five nights out?
# or even anytime it tallys -3 regardless window (too extreme?)

def going_out_and_evaluate(patron_p, N=len(patron_p), L=int(N*.6), d=-3, m_nights=2, change_rate=.1, bar_runs=1000,
ind_friday=True,
re_eval=True, re_eval_binary=False, re_eval_w_contrarian=False):
'''
Parameters:
-----------

patron_p    : ndarray of patron preferences
N           : number of patrons
L           : max comfort level of venue
d           : losing threshold
m_nights    : number of most recent nights' best policy
bar_runs    :
change_rate :
re_eval     : whether to reset patron preferences
'''

temp_pref = patron_p
yes = []
nos = []

# historical winning strategy
m = []

# historical v contrarian preference
if re_eval_w_contrarian==True:
hist_contra = np.random.randint(2, size=N)
hist_contra += (hist_contra-1)
else:
hist_contra = np.ones(N)

tally_run = np.zeros(N)
M = bar_runs+1
tally_matrix = np.zeros(N*M).reshape(N, M)

for i in range(bar_runs):
bar_y, bar_n, tally, going_out = any_given_friday_night(temp_pref, ind_friday=ind_friday)
yes.append(bar_y)
nos.append(bar_n)
tally_run += tally
tally_matrix[:, i+1] = tally_matrix[:, i] + tally

# historical winning strategy
if bar_y <= L:
m.append(True)
else:
m.append(False)

if re_eval==True:

# check tally, update directionally to recent past
m_recent = np.sum(m[-m_nights:])/np.min([m_nights, len(m)])

if re_eval_binary==True:
# only care if should've gone out more/less & update
discrete_target = (m_recent >= 0.5) * 1.0
temp_pref[tally_run <= d] += (temp_pref[tally_run <= d] - discrete_target) * (-change_rate * hist_contra[tally_run <= d])
tally_run[tally_run <= d] = tally[tally_run <= d] * 0.
else:
# by what ratio should've gone out more/less & update
temp_pref[tally_run <= d] += (temp_pref[tally_run <= d] - m_recent) * (-change_rate * hist_contra[tally_run <= d])
tally_run[tally_run <= d] = tally[tally_run <= d] * 0.

return yes, nos, bar_runs, hist_contra, tally_run, tally_matrix, temp_pref

## run function

np.random.seed(8)
patron_p = preferences(N)
print("pre preferences: ", patron_p[:10])
yes, nos, bar_runs, _, final_tally, bar_matrix, new_pref = going_out_and_evaluate(patron_p, ind_friday=True,
re_eval=True, re_eval_binary=True, re_eval_w_contrarian=True)
print("\npost preferences: ", new_pref[:10])
print("\ninterquartile (cumulative): ", np.percentile(bar_matrix[:,-1], 25), np.percentile(bar_matrix[:,-1], 50), np.percentile(bar_matrix[:,-1], 75))
print("\ninterquartile (final tally): ", np.percentile(final_tally, 25), np.percentile(final_tally, 50), np.percentile(final_tally, 75))
print("\nfinal tally:\n", final_tally)

pre preferences:  [0.87344206 0.96854381 0.86920762 0.53090261 0.23280506 0.01149766
0.43052577 0.40241112 0.5227224  0.47844396]

post preferences:  [ 8.73442060e-01  9.68543809e-01  8.69207621e-01 -4.23812220e+12
-4.23913459e+13 -5.46196826e+13  5.38725875e-01 -1.15732262e+13
5.22722404e-01 -6.89893697e+12]

interquartile (cumulative):  -938.5 140.0 472.0

interquartile (final tally):  -0.0 146.5 472.0

final tally:
[748. 918. 736.  -1.  -2.  -2.  80.  -1.  72.  -1. 298. 195. 512. 470.
246.  -0.  -0. 944.  -0.  -1. 198. 968.  -0. 143.  -2.  75.  42. 790.
-2. 974.  81.  -0. 138. 508. 860.  -2.  90.  -2. 654. 296.  -2.  -0.
628. 245.  -2. 554. 970. 144.  -0.  47. 158. 216. 221. 282. 106.  -0.
478. 898. 876.  -2. 202. 290.  -2.  -1. 251.  -2.  74.  -2.  77. 181.
220. 385.  -1. 149. 638.  99. 412. 123. 690. 448. 690.  -0.  -2.  -1.
48. 210. 604.  70. 884. 450. 122. 177. 152. 494. 610. 103.  -0. 246.
49. 588.]

In [14]:
## plots

f, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(18, 8))

ax1.hist(patron_p, 25, facecolor='green')
ax1.set_title("Final 'Going Out' Preferences")

# https://matplotlib.org/gallery/lines_bars_and_markers/bar_stacked.html
ind = np.arange(bar_runs)
p1 = ax2.bar(ind, yes)
p2 = ax2.bar(ind, nos, bottom=yes)
ax2.axhline(y=60, linewidth=5, color='red')
ax2.set_title("Simulated Nights Out")

for row in range(bar_matrix.shape[0]):
ax3.plot(bar_matrix[row])
ax3.set_title("Cumulative Benefit")

ax4.scatter(new_pref, bar_matrix[:,-1])
ax4.set_xlim(0.,1.)
ax4.axhline(y=np.percentile(bar_matrix[:,-1], 25), linewidth=2, color='green', ls='--')
ax4.axhline(y=np.percentile(bar_matrix[:,-1], 75), linewidth=2, color='green', ls='--')
ax4.set_title("Final Pref x Cumulative Scores")

plt.tight_layout()

In [15]:
# ds = [-10, -5, -3, -2, -1]
# ms = [10, 5, 3, 2, 1]
# cr = [.9, .75, .5, .2, .1]

ds = [-3, -2, -1]
ms = [10, 5, 3]
cr = [.9, .5, .2]
bn = [True, False]
ct = [True, False]

param_stats = []

for i in range(len(ds)):
for j in range(len(ms)):
for k in range(len(cr)):
for l in range(len(bn)):
for m in range(len(ct)):
for n in range(2):

temp_dict = {}
temp_dict['d_threshold'] = ds[i]
temp_dict['m_nights'] = ms[j]
temp_dict['change_rate'] = cr[k]
temp_dict['binary_eval'] = bn[l]
temp_dict['incl_contra'] = ct[m]
temp_dict['setting_run'] = n

yes, nos, bar_runs, _, final_tally, bar_matrix, new_pref = going_out_and_evaluate(patron_p, d=ds[i], m_nights=ms[j],
change_rate=cr[k], re_eval_binary=bn[l],
re_eval_w_contrarian=ct[m])

#                         mean, minmax = stats.bayes_mvs(bar_matrix[:,-1], .95)[0]
#                         temp_dict['min_ci_95'] = minmax[0]
#                         temp_dict['max_ci_95'] = minmax[1]

temp_dict['median_tally'] = np.median(final_tally)
temp_dict['mean_tally'] = np.mean(final_tally)
temp_dict['mean_cuml'] = np.mean(bar_matrix[:,-1])

param_stats.append(temp_dict)

df_param_stats = pd.DataFrame(param_stats)
df_param_stats = df_param_stats[['d_threshold', 'm_nights', 'change_rate',
'binary_eval', 'incl_contra', 'setting_run',
'median_tally', 'mean_tally', 'mean_cuml']]

C:\Users\rstancut\AppData\Local\Continuum\anaconda3\lib\site-packages\ipykernel_launcher.py:63: RuntimeWarning: overflow encountered in add
C:\Users\rstancut\AppData\Local\Continuum\anaconda3\lib\site-packages\ipykernel_launcher.py:63: RuntimeWarning: invalid value encountered in add
C:\Users\rstancut\AppData\Local\Continuum\anaconda3\lib\site-packages\ipykernel_launcher.py:10: RuntimeWarning: invalid value encountered in less_equal
# Remove the CWD from sys.path while we load stuff.
C:\Users\rstancut\AppData\Local\Continuum\anaconda3\lib\site-packages\ipykernel_launcher.py:67: RuntimeWarning: overflow encountered in add
C:\Users\rstancut\AppData\Local\Continuum\anaconda3\lib\site-packages\ipykernel_launcher.py:67: RuntimeWarning: invalid value encountered in add

Out[15]:
d_threshold m_nights change_rate binary_eval incl_contra setting_run median_tally mean_tally mean_cuml
0 -3 10 0.9 True True 0 80.0 98.22 79.20
1 -3 10 0.9 True True 1 285.0 257.27 245.36
2 -3 10 0.9 True False 0 274.0 316.18 316.12
3 -3 10 0.9 True False 1 360.0 303.69 302.10
4 -3 10 0.9 False True 0 300.0 311.06 310.82
In [16]:
# df_param_stats.to_excel("param_stats.xlsx")

In [19]:
# just the high performing settings

ds = [-3, -2]
ms = [10, 5, 3]
cr = [.9, .5, .2]
bn = [True, False]
ct = [True]

param_stats = []

for i in range(len(ds)):
for j in range(len(ms)):
for k in range(len(cr)):
for l in range(len(bn)):
for m in range(len(ct)):
for n in range(5):    # set to 5 for example purpose only, actual run at 100

temp_dict = {}
temp_dict['d_threshold'] = ds[i]
temp_dict['m_nights'] = ms[j]
temp_dict['change_rate'] = cr[k]
temp_dict['binary_eval'] = bn[l]
temp_dict['incl_contra'] = ct[m]
temp_dict['setting_run'] = n

yes, nos, bar_runs, hist_contra, final_tally, bar_matrix, new_pref = going_out_and_evaluate(patron_p, d=ds[i], m_nights=ms[j],
change_rate=cr[k], re_eval_binary=bn[l],
re_eval_w_contrarian=ct[m])

#                         mean, minmax = stats.bayes_mvs(bar_matrix[:,-1], .95)[0]
#                         temp_dict['min_ci_95'] = minmax[0]
#                         temp_dict['max_ci_95'] = minmax[1]

#                         temp_dict['median_tally'] = np.median(final_tally)
#                         temp_dict['mean_tally'] = np.mean(final_tally)
temp_dict['mean_cuml'] = np.mean(bar_matrix[:,-1])
temp_dict['hist_cnt'] = np.sum([hist_contra==1])
temp_dict['mean_cuml_hist'] = np.mean(bar_matrix[:,-1][hist_contra==1])
temp_dict['contr_cnt'] = np.sum([hist_contra==-1])
temp_dict['mean_cuml_contr'] = np.mean(bar_matrix[:,-1][hist_contra==-1])

param_stats.append(temp_dict)

df_param_stats = pd.DataFrame(param_stats)
df_param_stats = df_param_stats[['d_threshold', 'm_nights', 'change_rate',
'binary_eval', 'incl_contra', 'setting_run',
'mean_cuml', 'hist_cnt', 'mean_cuml_hist', 'contr_cnt', 'mean_cuml_contr']]

C:\Users\rstancut\AppData\Local\Continuum\anaconda3\lib\site-packages\ipykernel_launcher.py:10: RuntimeWarning: invalid value encountered in less_equal
# Remove the CWD from sys.path while we load stuff.

Out[19]:
d_threshold m_nights change_rate binary_eval incl_contra setting_run mean_cuml hist_cnt mean_cuml_hist contr_cnt mean_cuml_contr
0 -3 10 0.9 True True 0 103.14 46 -101.695652 54 277.629630
1 -3 10 0.9 True True 1 102.66 53 75.396226 47 133.404255
2 -3 10 0.9 True True 2 103.66 48 107.750000 52 99.884615
3 -3 10 0.9 True True 3 103.34 52 24.269231 48 189.000000
4 -3 10 0.9 True True 4 104.40 55 73.381818 45 142.311111
In [20]:
# df_param_stats.to_excel("param_stats_high_perf.xlsx")


## random notes below¶

In [21]:
# y = np.arange(df_param_stats.shape[0])
x = df_param_stats.mean_cuml
y = np.arange(df_param_stats.shape[0])
plt.plot(x, y, 'o')
# plt.plot(*z, '-')
plt.xlabel("simulation mean")
plt.ylabel("settings")

Out[21]:
Text(0,0.5,'settings')
In [22]:
# use decision tree to identify parameters/thresholds to get high score (25+)
# i.e., change_rate == 0.2, m_nights == 10, etc.

# dot plot with ci lines for settings
# or: https://seaborn.pydata.org/generated/seaborn.violinplot.html

# also, CLT: run above N times and grab mean

In [23]:
#, new_pref
plt.scatter(new_pref, bar_matrix[:,-1])

Out[23]:
<matplotlib.collections.PathCollection at 0x2524cf20cf8>
In [24]:
plt.hist(bar_matrix[:,-1])

Out[24]:
(array([44.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0., 56.]),
array([-1000.,  -800.,  -600.,  -400.,  -200.,     0.,   200.,   400.,
600.,   800.,  1000.]),
<a list of 10 Patch objects>)
In [ ]:


In [ ]: