Poisson Process

Proceso Poisson
Proceso Poisson

Definición:

Sean \(T_1\), \(T_2\), …, \(T_n\) v.a.i.i.d con distribución exponencial de parámetro \(\lambda\), : \(\mathcal{E}xp(\lambda)\). : \(\mathcal{W}_0=0\), \(\mathcal{W}_n=T_1, T_2, ..., T_n\) para \(n\geq 1\). Definimos el proceso Poisson de parámetro o intensidad \(\lambda\) por

\[ \begin{equation} N(t) = máx \{n\geq 1, \ \ \mathcal{W}_n = T_1 + T_2 + ...+ T_n \leq t\} \end{equation} \] Las variables aleatorias \(T_n\) representan los intervalos de tiempo entre eventos sucesivos, y \(\mathcal{W}_n = T_1, T_2, ..., T_n\) representa el instante en el que ocurre el n-ésimo evento, y \(N(t)\) es el número de eventos que han ocurrido hasta el instante t.

Una definción más común, sea \(\{N(t), t \geq 0\}\) un proceso estocástico que cuenta el número de eventos hasta el tiempo \(t\). Se dice que \(\{N(t)\}\) es un proceso de Poisson de parámetro o intensidad \(\lambda > 0\) si cumple las siguientes propiedades:

  • \(N(0) = 0\)

  • Incrementos independientes: Para cualquier colección de intervalos disjuntos \([t_0, t_1], [t_2, t_3], \dots\), los incrementos \[ N(t_1)-N(t_0), \ N(t_3)-N(t_2), \dots \] son variables aleatorias independientes.

  • Incrementos estacionarios: La distribución de los incrementos depende solo de la longitud del intervalo: \[ N(t+s)-N(s) \sim \text{Poisson}(\lambda t), \quad \forall s,t \ge 0. \]

  • No ocurrencia simultánea de eventos: \[ \lim_{\Delta t \to 0} P(\text{más de un evento en } [t, t+\Delta t]) = 0. \]

Proposición:

La variable aleatoria \(N(t)\) tiene distribución Poisson con parámetro \((\lambda t)\), es decir, para cualquier \(t>0\), y para \(n=0, 1, ...\)

\[\begin{equation} P(N(t) = n) = e^{-\lambda t} \frac{(\lambda t)^n}{n!} \end{equation}\]

Su valor esperado, y varianza son

\[\mathbb{E}[N(t)] = \lambda t \]

\[\mathbb{V}ar(N(t)) = \lambda t \] Para un proceso Poisson con parámetro lambda \(\lambda\), y \(s<t\) la covarianza entre \(N(s)\) y \(N(t)\) es \[ \begin{equation} \mathbb{C}ov(N(s), N(t)) = \lambda s \end{equation} \] Distribución de los Tiempos de Llegada

El \(n\)-ésimo tiempo de llegada \(W_n\) tiene una distribución Gamma:

\[ W_n \sim \text{Gamma}(n, \lambda) \] con función de densidad de probabilidad: \[ f_{W_n}(t) = \frac{\lambda^n t^{\,n-1} e^{-\lambda t}}{(n-1)!}, \quad t > 0 \]

Aquí, \(W_n = T_1 + T_2 + \dots + T_n\) representa el instante en el que ocurre el \(n\)-ésimo evento, siendo \(T_1, T_2, \dots, T_n\) variables aleatorias independientes e idénticamente distribuidas con distribución exponencial de parámetro \(\lambda\), \(T_i \sim \mathcal{E}xp(\lambda)\).

Simulación

Este código define una clase \(StochasticSimulation()\) para simular procesos estocásticos en tiempo discreto. El método \(__init__\) inicializa el número de pasos, repeticiones (\(n\_times\)), el tiempo total \(T\) y genera un vector de tiempos \(t\) con incremento \(dt\). El método \(PoissonProcess(rate, max\_time)\) simula trayectorias. Se realizan \(n\_times\) simulaciones en paralelo, almacenando cada trayectoria en filas del arreglo. Finalmente, convierte el resultado en un DataFrame de pandas.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.cm as cm
import matplotlib.patches as patches
sns.set_theme()
class StochasticSimulation():

    def __init__(self, T=None, steps=100, n_times=1):
        self.steps = steps
        self.n_times = n_times
        # Si T no se proporciona, usar T = steps
        if T is None:
            self.T = steps
        else:
            self.T = T

        self.dt = self.T/ steps
        self.t = np.linspace(0, self.T, steps+1)

    # Procesos Poisson
    def PoissonProcess(self, rate, max_time=1000):
          """Event-based simulation"""
          time_array = np.linspace(0, max_time, self.steps + 1)
          trajectories = np.zeros((self.n_times, self.steps + 1))
          
          for i in range(self.n_times):
              events = []
              current_time = 0
              while current_time <= max_time:
                  inter_arrival = np.random.exponential(1/rate)
                  current_time += inter_arrival
                  if current_time <= max_time:
                      events.append(current_time) 
              # Contamos eventos
              event_count = 0
              for j, t in enumerate(time_array):
                  while event_count < len(events) and events[event_count] <= t:
                      event_count += 1
                  trajectories[i, j] = event_count
          # retornamos un dataframe
          return pd.DataFrame(trajectories)

En este ejemplo, primero se definen los parámetros de la simulación: se tomarán \(𝑁=10,000\) pasos, lo que permitirá generar una trayectoria muy detallada del proceso, y se simulará \(𝑀=1\), una única trayectoria.

A continuación, se crea una instancia de la clase \(StochasticSimulation()\), pasando como argumentos el número de pasos \(N\)y el número de trayectorias \(M\) a simular. Esta clase se encarga de manejar los cálculos necesarios para generar procesos estocásticos.

Finalmente, se llama al método \(PoissonProcess(rate, max\_time)\) de la instancia, recibe como argumento \(rate=0.01\) parámetro lambda, así como \(max\_time=N\), con los cuales nos genera la trayectoria simulada. El resultado se almacena en df.

Simulación de un proceso Poisson en una dimensión

Simulación de una trajectoria

# Parámetros
N = 10000        # número de pasos
M = 1            # número de trayectorias simuladas
rate= 0.01
max_time = N
sim = StochasticSimulation(steps=N, n_times=M)
df = sim.PoissonProcess(rate, max_time)

# Colormap
cmap = cm.get_cmap("Grays", M)
# lista de t
t_max = [*range(0, N, 1)]
T = sim.T
# sdt teórica : lambda*t
std_teorica = np.sqrt(np.array(t_max)*rate)
# Media teórica
mean_B = [t*rate for t in t_max]
# Intervalos de confianza 95% t(1−t/T)
conf_upper =  mean_B + 1.96 * std_teorica
conf_lower =  mean_B - 1.96 * std_teorica
# Gráfico
plt.figure(figsize=(14,6))
for i in range(M):
    plt.step( df.columns.to_list(), df.iloc[i,:].to_list(), color=cmap(i), linewidth=0.5)
plt.plot(t_max, conf_upper, 'blue', linewidth=0.3, linestyle='--')
plt.plot(t_max, conf_lower, 'blue', linewidth=0.3, linestyle='--')
plt.fill_between(t_max, conf_lower, conf_upper, color='lightblue', alpha=0.2, label='Intervalo de confianza 95% (teórico)')
plt.title(f"Proceso Poisson[{M} trayectoria]")
plt.xlabel("Tiempo t")
plt.ylabel("Events")
plt.grid(True)
plt.legend()
plt.show()

Simulación de múltiples trajectorias

# Parámetros
N = 10000        # número de pasos
M = 1000            # número de trayectorias simuladas
rate= 0.01
max_time = N
sim = StochasticSimulation(steps=N, n_times=M)
df = sim.PoissonProcess(rate, max_time)

# Colormap
cmap = cm.get_cmap("Grays", M*2)
# lista de t
t_max = [*range(0, N, 1)]
T = sim.T
# sdt teórica : lambda*t
std_teorica = np.sqrt(np.array(t_max)*rate)
# Media teórica
mean_B = [t*rate for t in t_max]
# Intervalos de confianza 95% t(1−t/T)
conf_upper =  mean_B + 1.96 * std_teorica
conf_lower =  mean_B - 1.96 * std_teorica
# Gráfico
plt.figure(figsize=(14,6))
for i in range(M):
    plt.step( df.columns.to_list(), df.iloc[i,:].to_list(), color=cmap(i), linewidth=0.5)
plt.plot(t_max, conf_upper, 'blue', linewidth=0.3, linestyle='--')
plt.plot(t_max, conf_lower, 'blue', linewidth=0.3, linestyle='--')
plt.fill_between(t_max, conf_lower, conf_upper, color='lightblue', alpha=0.2, label='Intervalo de confianza 95% (teórico)')
plt.title(f"Proceso Poisson[{M} trayectoria]")
plt.xlabel("Tiempo t")
plt.ylabel("Events")
plt.grid(True)
plt.legend()
plt.show()

Veamos las distribuciones a distintos tiempos

# Tiempos a graficar
steps = [10,100,300,500]
# Generamos gráfico
fig, axes = plt.subplots(2, 2, figsize=(10, 6))
axes = axes.flatten() 
for i, step in enumerate(steps):
    data = df[step]
    mean = data.mean()
    var = data.var()
    sns.histplot(data, bins=30, kde=True, ax=axes[i])
    axes[i].set_title(f'Distribución en paso {step}')
    axes[i].set_xlabel('Posición')
    axes[i].set_ylabel('Frecuencia')
    axes[i].legend([f'Media = {mean:.2f}, Varianza = {var:.2f}'])
plt.tight_layout()
plt.show()

POdemos observar que para cada valor de \(t\) los parámetros, media, y varianza, se aproximan con los valores teóricos.

Simulación de un Proceso Poisson en dos dimensiones

Finalmente, tomamos los datos de la simulación anterior y gráficamos pares de caminatas aleatorias para ver su evolución en dos dimensiones.

# Colormap (gradiente de gris)
cmap = cm.get_cmap("Greys", M*2)
centro = (50, 50)
circulo = patches.Circle(centro, 100, edgecolor='grey', facecolor='black', alpha=0.8, lw=1, ls='--')
# Crear la figura y los ejes
fig, ax = plt.subplots(figsize=(14, 10))
ax.add_patch(circulo)
# Proceso Poisson
for i in range(df.shape[0] - 1):
    if i < 500: 
        ax.plot(df.iloc[i][:], df.iloc[i + 1][:], linewidth=0.2, color=cmap(i))  # Normaliza el valor de i
    else:
        continue
plt.title('Simulación de un Proceso Poisson: {} steps and {} paths'.format(N, M))
plt.xlabel('N(t)')
plt.ylabel('N(t)')
plt.gca().set_xticklabels([])  
plt.gca().set_yticklabels([])  
plt.grid(True)
plt.show()

Referencias

  • Ubbo F Wiersema (2008). Brownian Motion Calculus. John Wiley & Sons Ltd.
  • Sheldon M. Ross(1995). Stochastic Processes. Wiley Series in Probability and Statistics: Probability and Statistics. John Wiley & Sons.
  • LefebvreMario(2007). Applied Stochastics Processes. Springer, New York.