GADE 24/25 Daniel Miles-Touya.
Utilizar Python para analizar un problema cuya resolución se apoya en conceptos vistos en cursos anteriores.
Esto implica que las preguntas que debemos hacernos para desestructurar el problema se fundamentan en conceptos (que ya conocemos/nos deberían sonar de cursos anteriores).
Enfatizar en la importancia de los conceptos, más que en el saber hacer.
ADIF van a cancelar el bono transporte (link) de viajes frecuentes. ADIF pretende volver a la política de cobrar a los viajeros según el trayecto.
En este nuevo escenario, ADIF desea diseñar una política de venta anticipada de billetes que le permita completar la capacidad del tren en cada uno de los trayectos.
De Santiago de Compostela a Vigo se prevee dotar de un tren los Viernes a las 20 horas con una capacidad para 300 viajeros.
Del análisis de datos históricos de RENFE-ADIF se sabe que sólo el 90 por ciento de los viajeros que adquiere un billete anticipadamente se presenta al tren para viajar.
Dada esta información: ¿Cuantos billetes debe poner a la venta ADIF para completar el tren del Viernes a las 20 horas de Santiago a Vigo?
En función de la información planteada arriba (y de sus conocimientos de estadística) proponga una política de venta de billetes que optimice la utilización del tren (en térmimos probabilísticos).
Ayuda: debe pensar cómo modelizaría, en términos estadísticos, el comportamiento de un viajero. Luego, agregar todos los viajeros y deducir la ley que asigna probabilidades a este agregado.
Idea: Sobrevender billetes hasta el punto en que la probabilidad que venga un viajero más sea muy pequeña.
Recordar las probabilidades que están más allá de \(\mu + 3 \times \sigma\) en una distribución de probabilidades Normal con media \(\mu\) y varianza \(\sigma^2\).
Desestructurar conceptos para encontrar solución:
1.-Vamos a asumir que \(X\) es la variable aleatoria que recoge si el pasajero se presenta o no se presenta al tren. Esta es una variable aleatoria Bernoulli:
\[ X = \begin{cases} 1 & p \\ 0 & 1-p \end{cases} \]
donde \(X\) toma el valor 1 si el pasajero se presenta y 0 si no se presenta. donde \(p=0.9\), la probabilidad que se presente al tren.
2.-El total de viajeros que se presenta al tren es \(T_B = \sum_i^B X_i\), donde \(B\) es el número de billetes vendidos. Esta es una variable aleatoria con probabilidades dadas por una \(Binomial(B,p)\).
3.-Dado que \(B\) es grande (al menos, 300 asientos), las probabilidades generadas por la \(Binomial(B,p)\) son practicamente las mismas que las generadas por una distribución normal con media \(\mu = Bp\) y con varianza \(\sigma^2 = Bp(1-p)\), i.e., \(N(Bp,Bp(1-p))\).
Entonces \(Pr(T_B \ge \mu + 3 \times \sigma) = Pr(T_B \ge Bp + 3 \times \sqrt{Bp(1-p)}) \approx 0.001\)
4.-Por tanto, vamos a vender billetes hasta que \(\mu + 3 \times \sigma \approx 300\), ya que por encima de 300 habrá muy poca probabilidad que venga gente.
Vendemos tantos billetes, B, tal que \[ B \times 0.9 + 3 \times \sqrt{B \times 0.9 \times (1-0.9)} \approx 300 \]
Lo resolveremos en Python.
Un posible sript en Python para resolver la ecuación
\[ B \times 0.9 + 3 \times \sqrt{B \times 0.9 \times (1-0.9)} \approx 300 \]
sería
# Importar librerías necesarias
from scipy.optimize import fsolve
import numpy as np
# Definamos una funcion para resolver B: número de billetes a vender
def overbooking_equation(B,p,Capacidad):
mean_shows = B * p
std_shows = np.sqrt(B * p * (1 - p))
return mean_shows + 3 * std_shows - Capacidad # Target capacity is 300
# Resolver usando la función fsolve
B_valor_inicial = 350
B_optimal = fsolve(overbooking_equation, B_valor_inicial, (0.9, 300)) # Initial guess of 300
#Valor óptimo
B_optimal[0]
¿Cómo podemos testear la validez de esta política de venta de billetes?.
Vamos a simular en Python esta política de venta de billetes:
¿Qué queremos simular?
Los individuos compran anticipadamente el billetes, de forma independiente unos de otros, y hay una probabilidad de que no se presenten al tren.
Si sobrevendo billetes (overbooking) de manera óptima entonces será poco probable que aparezca un viajero por encima de la capacidad al momento que salga el tren.
El ordenador se puede utilizar para simular modelos para estudiar los resultados o las conclusiones que se derivan del modelo: simulaciones de Monte Carlo.
Aquí queremos simular el total de viajeros que se presentan al tren.
1.- Cada viajero compra el billete pero tiene una probabilidad \(1-p\) de no viajar.
2.- El comportamiento de cada viajero es independiente.
3.- La suma de cada viajero es el total de billetes, i.e., la capacidad.
Simulación:
El componente 1 es una Bernulli con \(p=0.9\).
El componente 3 es una Binomial cuyos parámetros son: el total de billetes vendidos donde los parámetors dependent de la política de ventas de billetes adoptada, i.e., \(B= 316\) , y \(p=0.9\).
La simulación consiste en impaginarse, por ejemplo, que observamos 10.000 viajes de Santiago de Compostela a Vigo y somos capaces de contar en cuantos viajes se excedió la capacidad de 300 asientos.
Esto es: la probabilidad es una proporción. Por tanto, calculamos la proporción de viajes (en 10.000 viajes) en los que hubo overbooking.
Vamos a hacer lectura comprensiva del script que da lugar a este gráfico:
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from scipy.stats import gaussian_kde
# Parámetros
n_tickets_sold = 316 # Número óptimo de boletos a la venta según la regla de los tres sigmas
p_show_up = 0.9 # Probabilidad de que un pasajero se presente
capacity = 300 # Capacidad del tren
n_simulations = 10000 # Número de simulaciones de Monte Carlo
# Paso 1: Simular una variable de Bernoulli
uniform_random = np.random.uniform(0, 1, size=(n_simulations, n_tickets_sold))
bernoulli_trials = (uniform_random < p_show_up).astype(int)
# Paso 2: Construir la distribución binomial
show_up_counts = np.sum(bernoulli_trials, axis=1)
# Paso 3: Calcular la probabilidad de sobreventa
overbooking_probability = np.mean(show_up_counts > capacity)
# Calcular la densidad usando gaussian_kde
kde = gaussian_kde(show_up_counts, bw_method=1)
# Crear el gráfico
plt.figure(figsize=(12, 6))
sns.histplot(show_up_counts, bins=30, kde=True, color="skyblue", stat="density", label="Distribución del conteo de pasajeros")
plt.axvline(x=capacity, color="red", linestyle="--", label="Límite de capacidad (300 asientos)")
plt.xlabel("Número de pasajeros que se presentan")
plt.ylabel("Densidad")
plt.title("Densidad de pasajeros que se presentan")
# Añadir una flecha que apunte a la línea de capacidad con el texto de probabilidad de sobreventa
plt.annotate(f"Prob. de sobreventa: {overbooking_probability:.4f}",
xy=(capacity + 0.1, 0), xytext=(capacity + .5, 0.05),
arrowprops=dict(facecolor="black", edgecolor="black", arrowstyle="->"), color="black")
plt.legend()
plt.show()
Conclusión: si los supuestos en los que se basa el modelo son correctos, entonces la política de venta de billetes sería válida.