-
Notifications
You must be signed in to change notification settings - Fork 0
/
modelo.py
201 lines (153 loc) · 7.88 KB
/
modelo.py
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
import numpy as np
from scipy.stats import binom, norm, lognorm, truncnorm, exponpow, mielke, genhyperbolic, johnsonsb, gennorm, powerlaw, uniform, genpareto
import math
class Modelo:
def __init__(self, b, m, loops, horas_semanales = 40, aleatoriedad = None, crit = "lineal"):
self.b = b
self.m = m
self.horas_semanales = horas_semanales
self.loops = loops
self.tiempo_actual = 0
self.sumatoria_ganancias = 0
self.pacientes_actuales = np.zeros(horas_semanales)
self.honorarios_pacientes = np.zeros(horas_semanales)
self.sumatoria_horas_ocupadas = 0
self.contador_personas_rechazadas_cupo = 0
self.contador_personas_rechazdas_monto = 0
self.contador_llegadas_totales = 0
if aleatoriedad:
self.aleatorias = aleatoriedad
else:
self.aleatorias = DistribucionesAleatorias()
self.crit = crit
def init_random(self):
self.pacientes_actuales = np.random.randint(0, 40, self.horas_semanales)
self.honorarios_pacientes = np.random.randint(0, 25, self.horas_semanales)
def horas_ocupadas(self):
return np.count_nonzero(self.pacientes_actuales)
def generar_proxima_llegada(self):
return self.aleatorias.generar_proxima_llegada()
def generar_monto_honorarios(self):
return self.aleatorias.generar_monto_honorarios()
def generar_duracion_consulta(self):
return self.aleatorias.generar_duracion_consulta()
def encontrar_posicion_libre(self):
horas_libres = np.where(self.pacientes_actuales == 0)[0]
if len(horas_libres) > 0:
return horas_libres[0]
else:
return None
def evaluar_monto(self, monto):
if self.crit == "log":
return self.evaluar_monto_log(monto)
elif self.crit == "lineal":
return self.evaluar_monto_lineal(monto)
def evaluar_monto_log(self, monto):
return monto > self.b + self.m*math.log(self.horas_ocupadas() + 1)
def evaluar_monto_lineal(self, monto):
return monto > self.b + self.m*self.horas_ocupadas()
def loop(self):
dt = self.generar_proxima_llegada()
self.contador_llegadas_totales += 1
self.tiempo_actual += dt
self.sumatoria_ganancias += np.dot(np.minimum(self.pacientes_actuales, dt), self.honorarios_pacientes)
self.pacientes_actuales = np.maximum(self.pacientes_actuales - dt, 0)
horas_ocupadas = self.horas_ocupadas()
self.sumatoria_horas_ocupadas += horas_ocupadas * dt
monto = self.generar_monto_honorarios()
if horas_ocupadas == self.horas_semanales:
self.contador_personas_rechazadas_cupo += 1
elif not self.evaluar_monto(monto):
self.contador_personas_rechazdas_monto += 1
else:
duracion = self.generar_duracion_consulta()
i = self.encontrar_posicion_libre()
self.pacientes_actuales[i] = duracion
self.honorarios_pacientes[i] = monto
def simular(self):
for i in range(self.loops):
self.loop()
return self.calcular_resultados()
def calcular_resultados(self):
ganancia_semanal = self.sumatoria_ganancias / self.tiempo_actual
promedio_ocupacion = self.sumatoria_horas_ocupadas / (self.tiempo_actual*self.horas_semanales)
promedio_rechazo_cupo = (self.contador_personas_rechazadas_cupo / self.contador_llegadas_totales) * 100
promedio_rechazo_monto = (self.contador_personas_rechazdas_monto / (self.contador_llegadas_totales)) * 100
print(f"Ganancia semanal: {ganancia_semanal}")
print(f"Promedio ocupación: {promedio_ocupacion}")
print(f"Promedio rechazo cupo: {promedio_rechazo_cupo}")
print(f"Promedio rechazo monto: {promedio_rechazo_monto}")
return ganancia_semanal, promedio_ocupacion, promedio_rechazo_cupo, promedio_rechazo_monto
def reset(self):
self.tiempo_actual = 0
self.sumatoria_ganancias = 0
self.pacientes_actuales = np.zeros(self.horas_semanales)
self.honorarios_pacientes = np.zeros(self.horas_semanales)
self.sumatoria_horas_ocupadas = 0
self.contador_personas_rechazadas_cupo = 0
self.contador_personas_rechazdas_monto = 0
self.contador_llegadas_totales = 0
class DistribucionesAleatorias:
def generar_proxima_llegada(self):
mean_stay = 0.5 # Mean stay in weeks
std_stay = 0.2 # Standard deviation of stay in weeks
norm_dist = norm(mean_stay, std_stay)
while True:
r = norm_dist.rvs()
if r > 0:
return r
def generar_monto_honorarios(self):
# Define the bounds
lower_bound = 8
# Adjust parameters to achieve the desired CDF at 20
mu = np.log(10) # Mean of the underlying normal distribution (to achieve CDF(20) = 50%)
sigma = 0.5 # Standard deviation of the underlying normal distribution
# Calculate parameters of the lognormal distribution
s = sigma
scale = np.exp(mu)
# Generate random values from lognormal distribution
return int(lognorm.rvs(s=s, scale=scale, loc=lower_bound))
def generar_duracion_consulta(self):
# Parameters for binomial distribution
n = 25 # Number of weeks
p = 0.3 # Probability of leaving within 25 weeks
binom_dist = binom(n, p)
# Parameters for normal distribution (long-term stay)
mean_stay = 208 # Mean stay in weeks
std_stay = 100
lower_bound = 0
upper_bound = float("inf") # Standard deviation of stay in weeks
norm_dist = truncnorm(loc = mean_stay, scale = std_stay, a=(lower_bound - mean_stay) / std_stay, b=(upper_bound - mean_stay) / std_stay)
if np.random.random() > 0.3:
return int(norm_dist.rvs())
else:
return int(binom_dist.rvs())
class DistribucionesViejas:
def generar_proxima_llegada(self):
params = {'b': 0.2530355326169663, 'loc': 1.9999999999999998, 'scale': 2.220442218350816}
return exponpow.rvs(b=params['b'], loc=params['loc'], scale=params['scale'])/7
def generar_monto_honorarios(self):
params = {'k': 0.6537121713667068, 's': 4.535972358049413, 'loc': 8.999999793193584, 'scale': 21.772360198928865}
return mielke.rvs(k=params['k'], s=params['s'], loc=params['loc'], scale=params['scale'])
def generar_duracion_consulta(self):
params = {'p': 0.24829868410300127, 'a': 0.9027799441751452, 'b': 0.9027799436159013, 'loc': 1.9999999434593596, 'scale': 7.062554221953598e-08}
return genhyperbolic.rvs(p=params['p'], a=params['a'], b=params['b'], loc=params['loc'], scale=params['scale'])*4
class DistribucionesNuevas:
def generar_proxima_llegada(self):
while True:
r = int(gennorm.rvs(beta= 0.09988947689055722, loc=3.998961573541849, scale=1.6248371198774156e-11))
if r >= 0:
return r/7
def generar_monto_honorarios(self):
params={'a': 0.14349017715809642, 'loc': 8.999999999999998, 'scale': 24.000000000000004}
return powerlaw.rvs(a=params['a'], loc=params['loc'], scale=params['scale'])
def generar_duracion_consulta(self):
params={'a': 0.42361987798205225, 'b': 0.4946976129904108, 'loc': 0.8047291306002482, 'scale': 97.27293102030796}
return johnsonsb.rvs(a=params['a'],b=params['b'], loc=params['loc'], scale=params['scale'])*4
class DistribucionesEasyFit:
def generar_proxima_llegada(self):
return uniform.rvs(loc=1.3675, scale=5.3525)/7
def generar_monto_honorarios(self):
return genpareto.rvs(c=0.03876, scale=6.5469, loc=20.189)
def generar_duracion_consulta(self):
return johnsonsb.rvs(a=0.35316, b=0.47835, scale=92.976, loc= -0.88488)*4