walopy
Teoría de colas, análisis de operaciones, modelos de inventario, árboles de KPI y más para Python.
pip install walopy
Tabla de contenidos
- Modelos de colas clásicos
- Modelos avanzados y de capacidad finita
- Colas con prioridad
- Simulación Monte Carlo G/G/1
- Ajuste de parámetros desde datos reales
- Redes de Jackson
- Inventarios
- Análisis de operaciones
- Análisis de cuellos de botella
- Balance de línea y tiempo takt
- Análisis de punto de equilibrio
- Programación de producción (scheduling)
- Confiabilidad
- Árboles de KPI
- Solvers y optimización
- Análisis de sensibilidad
- Escenarios en lote (batch_model)
- Comparar múltiples resultados
- Gráficas
- CLI
- Referencia de módulos
- Requisitos
1. Modelos de colas clásicos
Todos los modelos devuelven un QueueResult con los indicadores estándar de teoría de colas.
QueueResult — atributos principales
| Atributo | Descripción |
|---|---|
model |
Nombre del modelo (ej. "M/M/1") |
lam |
Tasa de llegada λ |
mu |
Tasa de servicio μ por servidor |
servers |
Número de servidores c |
rho |
Utilización por servidor ρ = λ/(c·μ) |
L |
Número promedio de unidades en el sistema |
Lq |
Número promedio de unidades en la cola |
W |
Tiempo promedio en el sistema |
Wq |
Tiempo promedio de espera en cola |
params |
Parámetros adicionales específicos del modelo |
Métodos disponibles en todos los resultados:
.summary()/print(r)— tabla de indicadores.to_frame()— exporta apd.DataFramede una fila.plot()— gráfica interactiva de sensibilidad
mm1(lam, mu) — M/M/1
Un servidor, llegadas Poisson, servicio exponencial. El modelo más simple de cola.
Fórmulas clave:
- ρ = λ/μ (debe ser < 1 para estabilidad)
- Lq = ρ² / (1 − ρ)
- L = ρ / (1 − ρ)
- Wq = Lq / λ, W = L / λ
import walopy as wl
r = wl.mm1(lam=3.0, mu=5.0)
print(r)
# Model : M/M/1
# λ : 3.0 (arrival rate)
# μ : 5.0 (service rate per server)
# ρ : 0.6 (utilization)
# L : 1.5 (avg units in system)
# Lq : 0.9 (avg units in queue)
# W : 0.5 (avg time in system)
# Wq : 0.3 (avg wait time in queue)
df = r.to_frame() # exportar a DataFrame
r.plot() # gráfica Wq y Lq vs ρ
mmc(lam, mu, c) — M/M/c
Multi-servidor con llegadas Poisson y servicio exponencial. Usa la fórmula de Erlang-C para calcular la probabilidad de espera C(c, a).
Fórmulas:
- ρ = λ/(c·μ) (utilización por servidor, debe ser < 1)
- a = λ/μ (carga ofrecida)
- C(c,a) = P(esperar) — fórmula de Erlang-C
- Lq = C(c,a) · ρ / (1 − ρ)
r = wl.mmc(lam=8.0, mu=5.0, c=2)
print(r.params["C(c,a) Erlang-C"]) # probabilidad de espera
r_1 = wl.mmc(lam=4.0, mu=5.0, c=1) # equivalente a mm1
r_3 = wl.mmc(lam=4.0, mu=5.0, c=3)
print(r_3.Wq < r_1.Wq) # True: más servidores → menor espera
md1(lam, mu) — M/D/1
Llegadas Poisson, servicio determinístico (tiempo de servicio constante = 1/μ). Produce exactamente la mitad de cola que M/M/1 bajo la misma carga.
Fórmulas:
- Lq = ρ² / (2·(1 − ρ)) ← mitad que M/M/1
- L = ρ + Lq
r_mm1 = wl.mm1(lam=3.0, mu=5.0)
r_md1 = wl.md1(lam=3.0, mu=5.0)
print(r_md1.Lq / r_mm1.Lq) # ≈ 0.5 — M/D/1 tiene la mitad de cola
kingman(lam, mu, ca2, cs2) — G/G/1 (aproximación VUT)
Modelo general G/G/1 mediante la aproximación de Kingman (también llamada VUT o fórmula de Pollaczek). Acepta cualquier distribución de llegadas y servicio a través de sus coeficientes de variación al cuadrado.
Fórmula:
Wq ≈ (ρ / (1 − ρ)) · ((ca² + cs²) / 2) · (1/μ)
| Parámetro | Descripción |
|---|---|
ca2 |
CV² de los tiempos entre llegadas (1.0 para Poisson) |
cs2 |
CV² de los tiempos de servicio (1.0 exponencial, 0.0 determinístico) |
# Proceso de llegada más regular que Poisson (ca2 < 1)
r = wl.kingman(lam=3.0, mu=5.0, ca2=0.5, cs2=1.0)
# Servicio muy variable (ca2 >> 1)
r = wl.kingman(lam=3.0, mu=5.0, ca2=1.0, cs2=3.0)
# Recuperar parámetros ajustados desde datos reales y usarlos directamente
fit = wl.fit_from_data(inter_arrivals=..., service_times=...)
r = wl.kingman(**fit.to_model_kwargs())
Equivalencias:
ca2=1, cs2=1→ M/M/1
ca2=1, cs2=0→ M/D/1 (aproximado)
mg1(lam, mu, cs2) — M/G/1 (Pollaczek-Khinchine exacto)
Llegadas Poisson, servicio de distribución arbitraria parametrizado por su media (1/μ) y su variabilidad (cs²). La fórmula P-K es exacta, no una aproximación.
Fórmula P-K:
Wq = λ · E[S²] / (2 · (1 − ρ)), donde E[S²] = (1 + cs²) / μ²
# Servicio con distribución triangular: calcular cs² primero
cs2 = wl.cv2_triangular(a=2, m=5, b=10) # a=mín, m=moda, b=máx
r = wl.mg1(lam=0.15, mu=1/5, cs2=cs2)
# Equivalencia con M/M/1 (cs²=1) y M/D/1 (cs²=0)
r_mm1 = wl.mg1(lam=3.0, mu=5.0, cs2=1.0) # idéntico a mm1(3, 5)
r_md1 = wl.mg1(lam=3.0, mu=5.0, cs2=0.0) # idéntico a md1(3, 5)
Helpers cv2_* — CV² por distribución
El CV² (varianza / media²) es el único parámetro de distribución que M/G/1 necesita:
| Función | Distribución | CV² |
|---|---|---|
cv2_normal(mean, std) |
Normal | (σ/μ)² |
cv2_triangular(a, m, b) |
Triangular | fórmula exacta |
cv2_uniform(a, b) |
Uniforme | (b−a)² / (12·μ²) |
cv2_erlang(k) |
Erlang-k | 1/k |
cv2_gamma(shape) |
Gamma | 1/shape |
cv2_lognormal(mean, std) |
Lognormal | (σ/μ)² |
cv2_weibull(shape) |
Weibull | Γ(1+2/k)/Γ(1+1/k)²−1 |
# Servicio con distribución Weibull de forma 2 (rayleigh)
cs2 = wl.cv2_weibull(shape=2.0) # ≈ 0.273
r = wl.mg1(lam=1.0, mu=2.0, cs2=cs2)
print(r.Wq) # tiempo medio de espera en cola
# Servicio Erlang-3 (menos variable que exponencial)
r = wl.mg1(lam=3.0, mu=5.0, cs2=wl.cv2_erlang(3))
littles_law(*, L, lam, W) — Ley de Little
Resuelve L = λ · W para la variable que falte. Exactamente uno de los tres parámetros debe ser None.
L = wl.littles_law(lam=5.0, W=0.4) # L = 2.0
W = wl.littles_law(L=2.0, lam=5.0) # W = 0.4
lam = wl.littles_law(L=3.0, W=0.6) # λ = 5.0
2. Modelos avanzados y de capacidad finita
mm1k(lam, mu, K) — M/M/1/K
Cola con capacidad finita K (servidor + sala de espera). Los clientes que llegan cuando el sistema está lleno se pierden. El sistema es siempre estable incluso si λ > μ.
Parámetros adicionales en result.params:
PK (blocking prob)— probabilidad de rechazo (sistema lleno)lam_eff— tasa de llegada efectiva = λ · (1 − P_K)
r = wl.mm1k(lam=5.0, mu=3.0, K=10)
print(r.params["PK (blocking prob)"]) # fracción de clientes rechazados
print(r.params["lam_eff"]) # tasa real de entrada al sistema
mmck(lam, mu, c, K) — M/M/c/K
Multi-servidor con capacidad finita. Generaliza tanto M/M/c (K→∞) como M/M/1/K (c=1). Las probabilidades de estado se calculan en espacio logarítmico para evitar desbordamientos con K grande.
r = wl.mmck(lam=8.0, mu=3.0, c=2, K=20)
print(r.params["PK (blocking prob)"])
# Verificación: con K muy grande se acerca a M/M/c
r_inf = wl.mmc(lam=3.0, mu=5.0, c=2)
r_fin = wl.mmck(lam=3.0, mu=5.0, c=2, K=500)
print(abs(r_fin.Lq - r_inf.Lq) < 0.01) # True
Restricción: K debe ser ≥ c (la capacidad total no puede ser menor que el número de servidores).
erlang_b(lam, mu, c) — Erlang B
Probabilidad de bloqueo para un sistema de pérdidas M/M/c/c (sin sala de espera: si todos los servidores están ocupados el cliente se va). Usado en dimensionamiento de líneas telefónicas y redes de comunicación.
pb = wl.erlang_b(lam=5.0, mu=1.0, c=8)
print(f"Blocking probability: {pb:.2%}") # ej. 1.3%
# Cuántos servidores para bloqueo < 2%?
for c in range(1, 20):
if wl.erlang_b(5.0, 1.0, c) < 0.02:
print(f"Need c = {c} servers")
break
queue_length_pmf(lam, mu, n_max) — PMF de longitud de cola
Distribución de probabilidad del número de clientes en el sistema para M/M/1.
Devuelve un pd.DataFrame con columnas n, P(N=n), P(N<=n).
df = wl.queue_length_pmf(lam=3.0, mu=5.0, n_max=20)
print(df.head())
# n P(N=n) P(N<=n)
# 0 0.400 0.400
# 1 0.240 0.640
# 2 0.144 0.784
# ...
# ¿Probabilidad de tener más de 5 clientes en sistema?
prob_gt5 = 1 - df.loc[df["n"] == 5, "P(N<=n)"].iloc[0]
sojourn_cdf(lam, mu, t_max, n_points) — CDF del tiempo de permanencia
Distribución acumulada del tiempo total en el sistema (sojourn time) para M/M/1.
Devuelve un pd.DataFrame con columnas t, F(t), f(t).
df = wl.sojourn_cdf(lam=3.0, mu=5.0)
# ¿Cuánto tiempo cubre el 95% de los clientes?
t95 = df.loc[df["F(t)"] >= 0.95, "t"].iloc[0]
print(f"95% of customers leave before t = {t95:.3f}")
3. Colas con prioridad
mm1_priority(lam_list, mu, *, class_names) — M/M/1 HOL no-preemptiva
Cola de un servidor con múltiples clases de prioridad. La clase 0 tiene la prioridad más alta. La disciplina es Head-Of-Line (HOL) no-preemptiva: un cliente de alta prioridad que llega no interrumpe al que está siendo atendido, pero sí pasa delante de todos los que esperan.
Fórmula de Kleinrock:
Wq_k = R / ((1 − σ_{k−1}) · (1 − σ_k))
donde R = ρ_total / μ es el tiempo residual de servicio y σ_k = Σ_{i=0}^{k} ρ_i.
# 3 clases: alta (λ=2), media (λ=1.5), baja (λ=0.5), μ=5 compartido
pri = wl.mm1_priority([2.0, 1.5, 0.5], mu=5.0)
print(pri)
for cls in pri.classes:
print(f"Clase {cls['class_id']}: Wq = {cls['Wq']:.4f}")
# Con nombres de clase personalizados
pri = wl.mm1_priority(
[2.0, 1.0],
mu=5.0,
class_names=["VIP", "Estándar"],
)
print(pri.classes[0]["Wq"]) # VIP espera mucho menos
print(pri.classes[1]["Wq"]) # Estándar espera más que en cola FIFO
df = pri.to_frame() # DataFrame con una fila por clase
PriorityQueueResult — atributos:
| Atributo | Descripción |
|---|---|
classes |
Lista de dicts con class_id, lam, rho, Wq, W, Lq, L por clase |
rho_total |
Utilización total del servidor |
mu |
Tasa de servicio compartida |
Restricción: la suma de todas las tasas de llegada debe ser < μ (sistema estable).
4. Simulación Monte Carlo G/G/1
monte_carlo_gg1(lam, mu, ca2, cs2, n_customers, seed) — Simulación event-driven
Simula una cola G/G/1 con distribuciones gamma calibradas para reproducir cualquier ca² y cs². Útil para validar aproximaciones analíticas y obtener percentiles que los modelos cerrados no dan.
sim = wl.monte_carlo_gg1(
lam=3.0, mu=5.0,
ca2=1.0, cs2=0.5, # ca2=1 → Poisson, cs2=0.5 → Erlang-2
n_customers=50_000,
seed=42,
)
print(sim.Wq_mean) # promedio
print(sim.Wq_p50) # mediana
print(sim.Wq_p90) # percentil 90
print(sim.Wq_p95) # percentil 95
print(sim.Wq_p99) # percentil 99
sim.plot() # histograma + CDF empírica con percentiles anotados
SimulationResult — atributos:
| Atributo | Descripción |
|---|---|
Wq_mean |
Tiempo promedio de espera simulado |
Wq_p50/p90/p95/p99 |
Percentiles de la distribución de espera |
L_mean, Lq_mean |
Longitudes de sistema y cola promedio |
model |
Descripción del modelo simulado |
5. Ajuste de parámetros desde datos reales
fit_from_data(inter_arrivals, service_times, *, arrival_timestamps) — Estimación de parámetros
Estima λ, μ, ca², cs² directamente desde datos observados. Útil para calibrar modelos cuando los parámetros no se conocen teóricamente.
Formas de uso:
import numpy as np
import walopy as wl
rng = np.random.default_rng(42)
# 1. Desde tiempos entre llegadas y tiempos de servicio
ia = rng.exponential(scale=0.2, size=1000) # media = 0.2 → λ ≈ 5
svc = rng.exponential(scale=0.1, size=1000) # media = 0.1 → μ ≈ 10
fit = wl.fit_from_data(ia, svc)
print(fit)
# λ ≈ 5.0 ca² ≈ 1.0 (exponencial → CV² = 1)
# μ ≈ 10.0 cs² ≈ 1.0
# 2. Solo tiempos entre llegadas (sin datos de servicio)
fit_ia = wl.fit_from_data(ia)
print(fit_ia.lam, fit_ia.ca2)
print(fit_ia.mu) # None
# 3. Desde timestamps crudos (no se tienen los intervalos directamente)
ts = np.cumsum(rng.exponential(0.2, 1000)) # timestamps acumulados
fit = wl.fit_from_data(arrival_timestamps=ts)
# 4. Conectar directamente con un modelo
r = wl.kingman(**fit.to_model_kwargs()) # usa lam, mu, ca2, cs2
FitResult — atributos:
| Atributo | Descripción |
|---|---|
lam |
Tasa de llegada estimada (None si no hay datos de llegada) |
mu |
Tasa de servicio estimada (None si no hay datos de servicio) |
ca2 |
CV² de tiempos entre llegadas |
cs2 |
CV² de tiempos de servicio |
n_arrivals |
Número de observaciones de llegada |
n_services |
Número de observaciones de servicio |
mean_ia |
Media de tiempos entre llegadas |
mean_svc |
Media de tiempos de servicio |
Métodos: .to_model_kwargs() → dict con lam, mu, ca2, cs2 listos para kingman(). .to_frame() → DataFrame.
6. Redes de Jackson
jackson_network(station_names, mu, gamma, routing, *, servers) — Red abierta de colas
Analiza una red de colas de Jackson abierta. Por el Teorema de Jackson, cada estación se comporta como una cola M/M/c independiente con una tasa de llegada efectiva que satisface las ecuaciones de tráfico:
(I − P^T) · λ = γ
donde γ_i es la tasa de llegada externa a la estación i y P_ij es la probabilidad de routing de i a j.
# Red de 3 estaciones en serie con estación final de 2 servidores
net = wl.jackson_network(
station_names=["Recepción", "Control de calidad", "Empaque"],
mu=[10.0, 8.0, 12.0], # tasa de servicio por servidor
gamma=[5.0, 0.0, 0.0], # solo llegan clientes externos a Recepción
routing=[
[0.0, 1.0, 0.0], # Recepción → 100% a QC
[0.0, 0.0, 1.0], # QC → 100% a Empaque
[0.0, 0.0, 0.0], # Empaque → sale del sistema
],
servers=[1, 1, 2], # servidores por estación
)
print(net)
print(net.to_frame()) # DataFrame con métricas por estación
# Acceder a métricas por estación
for st in net.stations:
print(f"{st.name}: λ={st.lam_total:.2f}, Wq={st.Wq:.4f}, ρ={st.rho:.2f}")
# Indicadores del sistema completo
print(f"L_sistema = {net.L_system:.4f}") # clientes totales en toda la red
print(f"W_sistema = {net.W_system:.4f}") # tiempo total en la red
# Red con bifurcación
net2 = wl.jackson_network(
station_names=["Entrada", "Ruta A", "Ruta B"],
mu=[20.0, 8.0, 10.0],
gamma=[10.0, 0.0, 0.0],
routing=[
[0.0, 0.6, 0.4], # 60% va a Ruta A, 40% a Ruta B
[0.0, 0.0, 0.0],
[0.0, 0.0, 0.0],
],
)
JacksonResult — atributos:
| Atributo | Descripción |
|---|---|
stations |
Lista de StationMetrics por estación |
L_system |
Número total promedio de clientes en toda la red |
W_system |
Tiempo total promedio en la red (Little: L = λ_ext · W) |
StationMetrics — atributos:
| Atributo | Descripción |
|---|---|
name |
Nombre de la estación |
lam_total |
Tasa de llegada total (externa + interna) |
lam_external |
Tasa de llegada externa γ_i |
mu |
Tasa de servicio por servidor |
servers |
Número de servidores |
rho |
Utilización ρ = λ / (c·μ) |
L, Lq, W, Wq |
Indicadores estándar de cola |
Restricciones: todas las estaciones deben ser estables (ρ < 1). La suma de probabilidades de routing por fila debe ser ≤ 1.
7. Inventarios
eoq(demand_rate, ordering_cost, holding_cost) — Cantidad Económica de Pedido
Fórmula de Wilson/Harris: minimiza la suma de costo de ordenar y costo de almacenar.
Fórmula:
Q* = √(2 · D · K / h)
En el óptimo: costo de mantener = costo de ordenar (propiedad de cruce).
r = wl.eoq(
demand_rate=1000, # D: unidades por período
ordering_cost=50, # K: costo fijo por orden
holding_cost=2, # h: costo por unidad por período
)
print(r)
# EOQ (order quantity): 223.6 units
# Order frequency : 4.472 orders/period
# Cycle time : 0.2236 periods
# Total cost : 447.2
# Holding cost : 223.6
# Ordering cost : 223.6 ← iguales en el óptimo
r.plot() # gráfica de curvas de costo
df = r.to_frame()
EOQResult — atributos:
| Atributo | Descripción |
|---|---|
eoq |
Cantidad óptima Q* |
total_cost |
Costo total mínimo por período |
holding_cost_total |
Componente de almacenamiento en Q* |
ordering_cost_total |
Componente de pedido en Q* |
order_frequency |
Número de pedidos por período D/Q* |
cycle_time |
Tiempo entre pedidos 1/(D/Q*) |
reorder_point(demand_rate, lead_time, *, demand_std, lead_time_std, service_level) — Punto de reorden
Calcula el nivel de inventario al que se debe lanzar una orden de reposición, incluyendo stock de seguridad para cubrir la variabilidad de la demanda y del tiempo de entrega.
Fórmulas:
σ_DLT = √(L̄ · σ_D² + D̄² · σ_L²)
ROP = D̄ · L̄ + z · σ_DLT
r = wl.reorder_point(
demand_rate=50, # demanda media por período
lead_time=2, # tiempo de entrega medio (en mismas unidades)
demand_std=10, # desviación estándar de la demanda
lead_time_std=0.5, # desviación estándar del lead time
service_level=0.95, # nivel de servicio deseado
)
print(r)
# Reorder point : 132.4 units
# Safety stock : 32.4 units
# Service level : 95.00%
# z-score : 1.645
# Mean demand LT : 100.0
# Std demand LT : 19.72
# Sin variabilidad → solo demanda esperada durante LT
r_det = wl.reorder_point(demand_rate=50, lead_time=2,
demand_std=0, lead_time_std=0)
print(r_det.safety_stock) # 0.0 — no se necesita buffer
# Comparar niveles de servicio
r_90 = wl.reorder_point(50, 2, demand_std=10, service_level=0.90)
r_99 = wl.reorder_point(50, 2, demand_std=10, service_level=0.99)
print(r_99.safety_stock > r_90.safety_stock) # True
ReorderResult — atributos:
| Atributo | Descripción |
|---|---|
reorder_point |
Nivel ROP en unidades |
safety_stock |
Stock de seguridad = z · σ_DLT |
service_level |
Nivel de servicio de ciclo |
z_score |
Factor de seguridad z |
mean_demand_lt |
Demanda esperada durante el lead time |
std_demand_lt |
Desviación estándar de la demanda durante LT |
newsvendor(demand_mean, demand_std, price, cost, *, salvage) — Modelo del vendedor de periódicos
Optimiza la cantidad a ordenar para un producto perecedero o de temporada bajo demanda incierta. Equilibra el costo de quedarse corto (Cu) con el costo de sobrar (Co).
Fórmulas:
Cu = precio − costo (costo de substock: venta perdida)
Co = costo − salvage (costo de sobrestock: unidad no vendida)
CR = Cu / (Cu + Co) (ratio crítico)
Q* = F⁻¹(CR) (cuantil CR de la demanda)
r = wl.newsvendor(
demand_mean=100,
demand_std=20,
price=10, # precio de venta
cost=6, # costo de compra
salvage=2, # valor residual de unidades no vendidas
)
print(r)
# Optimal quantity : 100.0 units
# Critical ratio : 0.5
# Expected profit : 320.0
# Expected sales : 100.0
# Expected leftover : 0.0
# Expected stockout : 0.0
# Underage cost Cu : 4.0
# Overage cost Co : 4.0
# Producto con alto margen → pedir por encima de la media
r_alto = wl.newsvendor(demand_mean=100, demand_std=20,
price=50, cost=6, salvage=0)
print(r_alto.optimal_qty > 100) # True
# Producto con bajo margen → pedir por debajo de la media
r_bajo = wl.newsvendor(demand_mean=100, demand_std=20,
price=7, cost=6, salvage=2)
print(r_bajo.optimal_qty < 100) # True
NewsvendorResult — atributos:
| Atributo | Descripción |
|---|---|
optimal_qty |
Q* óptimo |
critical_ratio |
CR = Cu/(Cu+Co) |
expected_profit |
Beneficio esperado en Q* |
expected_sales |
Ventas esperadas E[min(D, Q*)] |
expected_leftover |
Sobrante esperado E[max(Q*−D, 0)] |
expected_stockout |
Rotura esperada E[max(D−Q*, 0)] |
underage_cost |
Cu — costo de substock |
overage_cost |
Co — costo de sobrestock |
ebq(demand_rate, setup_cost, holding_cost, production_rate) — Lote Económico de Producción (EBQ/EPQ)
Extiende el EOQ al caso en que la producción y el consumo ocurren simultáneamente (tasa de producción finita P > D).
Fórmula:
Q* = √(2 · D · S / (h · (1 − D/P)))
Inventario máximo = Q* · (1 − D/P)
Inventario medio = Inventario máximo / 2
r = wl.ebq(
demand_rate=1000, # D: unidades/período
setup_cost=50, # S: costo fijo de setup por lote
holding_cost=2, # h: costo/unidad/período
production_rate=4000, # P: tasa de producción (debe ser > D)
)
print(r)
# EBQ (batch size) : 258.2 units
# Max inventory : 193.6
# Avg inventory : 96.82
# Order frequency : 3.873 runs/period
# Cycle time : 0.2582 periods
# Production time/cyc : 0.06455 periods
# Total cost : 193.6
# Holding cost : 96.82
# Setup cost : 96.82
EBQResult — atributos:
| Atributo | Descripción |
|---|---|
ebq |
Lote óptimo Q* |
max_inventory |
Nivel máximo de inventario Q*(1−D/P) |
avg_inventory |
Inventario promedio = max/2 |
production_time |
Tiempo de producción por ciclo = Q*/P |
cycle_time |
Duración del ciclo = Q*/D |
total_cost |
Costo total mínimo |
eoq_multi(demand_rates, ordering_costs, holding_costs, *, names) — EOQ multi-ítem independiente
Resuelve el EOQ para cada ítem por separado sin restricciones compartidas.
r = wl.eoq_multi(
demand_rates = [1000, 500, 800],
ordering_costs = [50, 30, 40 ],
holding_costs = [2, 1, 1.5],
names = ["A", "B", "C"],
)
print(r.total_cost) # suma de costos óptimos individuales
df = r.to_frame() # DataFrame con EOQ, costos y frecuencias por ítem
ebq_multi(demand_rates, setup_costs, holding_costs, production_rates, *, names) — EBQ multi-ítem independiente
r = wl.ebq_multi(
demand_rates = [1000, 500],
setup_costs = [50, 30 ],
holding_costs = [2, 1 ],
production_rates= [4000, 2000],
)
df = r.to_frame() # EBQ, inventario máximo/medio, costo por ítem
eoq_multi_constrained(demand_rates, ordering_costs, holding_costs, ...) — EOQ multi-ítem con restricciones
Minimiza el costo total de inventario sujeto a restricciones lineales sobre las cantidades de pedido mediante relajación Lagrangiana (búsqueda binaria sobre el multiplicador).
Restricciones soportadas:
| Parámetro | Restricción |
|---|---|
budget, budget_unit_costs |
Σ(c_i · Q_i / 2) ≤ budget — valor promedio en inventario |
space, space_per_unit |
Σ(s_i · Q_i / 2) ≤ space — espacio promedio ocupado |
constraints |
Lista de {"name", "weights", "bound"} — Σ(a_i · Q_i) ≤ B |
# Restricción de presupuesto
r = wl.eoq_multi_constrained(
[1000, 500, 800], [50, 30, 40], [2, 1, 1.5],
budget=5000,
budget_unit_costs=[10, 8, 12], # costo unitario de compra
)
print(r.binding_constraints) # ["budget"] si la restricción está activa
print(r.lagrange_multipliers) # {"budget": λ}
df = r.to_frame() # Q*, holding cost, ordering cost por ítem
# Restricción de espacio
r2 = wl.eoq_multi_constrained(
[1000, 500, 800], [50, 30, 40], [2, 1, 1.5],
space=200,
space_per_unit=[0.5, 0.3, 0.4],
)
# Restricción genérica: suma total de unidades pedidas ≤ 400
r3 = wl.eoq_multi_constrained(
[1000, 500, 800], [50, 30, 40], [2, 1, 1.5],
constraints=[{"name": "total_qty", "weights": [1, 1, 1], "bound": 400}],
)
ConstrainedMultiEOQResult — atributos:
| Atributo | Descripción |
|---|---|
items |
Lista de dicts por ítem: nombre, Q*, costos |
total_cost |
Costo total bajo las restricciones |
unconstrained_total_cost |
Costo sin restricciones (cota inferior) |
lagrange_multipliers |
Diccionario {nombre_restricción: λ} |
binding_constraints |
Nombres de restricciones activas (λ > 0) |
lot_for_lot(demands, setup_cost, holding_cost) — Lote por Lote
Heurística de dimensionado dinámico: ordena exactamente la demanda de cada período. Minimiza el costo de almacenamiento (cero inventario en tránsito) a cambio de un setup por período.
demands = [100, 80, 0, 120, 60] # demandas por período
r = wl.lot_for_lot(demands, setup_cost=200, holding_cost=1)
print(r.total_holding_cost) # 0.0 — sin inventario residual
print(r.n_orders) # 4 — un pedido por período con demanda > 0
print(r)
# Method : Lot-for-Lot
# Number of orders : 4
# Total cost : 800.0
silver_meal(demands, setup_cost, holding_cost) — Silver-Meal
Heurística de dimensionado dinámico que agrupa períodos mientras el costo promedio por período decrece, reduciendo el número de setups.
r = wl.silver_meal(demands, setup_cost=200, holding_cost=1)
print(r.total_cost <= wl.lot_for_lot(demands, 200, 1).total_cost) # True
df = r.to_frame() # pedidos: período, cantidad, períodos cubiertos
LotSizingResult — atributos (Lot-for-Lot y Silver-Meal):
| Atributo | Descripción |
|---|---|
orders |
Lista de pedidos: período, cantidad, períodos cubiertos |
total_cost |
Costo total (setup + almacenamiento) |
total_setup_cost |
Costo total de setups |
total_holding_cost |
Costo total de almacenamiento |
n_orders |
Número de pedidos emitidos |
method |
Nombre de la heurística usada |
eoq_quantity_discount(demand_rate, ordering_cost, holding_cost_rate, price_breaks) — EOQ con descuentos por cantidad
Evalúa todos los tramos de precio (all-units) y selecciona la cantidad que minimiza el costo total anual (compra + pedido + almacenamiento), ajustando el EOQ al tramo válido cuando cae fuera de rango.
r = wl.eoq_quantity_discount(
demand_rate=1000,
ordering_cost=50,
holding_cost_rate=0.20, # 20% del precio unitario por año
price_breaks=[
(0, 10.00), # precio base
(500, 9.50), # ≥ 500 unidades → $9.50
(1000, 9.00), # ≥ 1000 unidades → $9.00
],
)
print(r.optimal_qty) # cantidad óptima seleccionada
print(r.unit_price) # precio en ese tramo
print(r.total_cost) # costo anual mínimo
df = r.to_frame() # todos los candidatos evaluados
QuantityDiscountResult — atributos:
| Atributo | Descripción |
|---|---|
optimal_qty |
Cantidad óptima ajustada al tramo |
unit_price |
Precio unitario en el tramo seleccionado |
total_cost |
Costo total anual (compra + pedido + almacenamiento) |
purchase_cost |
Costo de compra anual D · precio |
ordering_cost_total |
Costo de pedido anual (D/Q) · K |
holding_cost_total |
Costo de almacenamiento anual (Q/2) · h · precio |
candidates |
Lista de todos los tramos evaluados |
8. Análisis de operaciones
oee(availability, performance, quality) — OEE
Calcula la Eficiencia Global de los Equipos (OEE = Availability × Performance × Quality).
r = wl.oee(
availability=0.90, # tiempo disponible / tiempo planificado
performance=0.80, # producción real / producción teórica
quality=0.95, # unidades buenas / unidades totales
)
print(r)
# OEE = 68.4%
# Clase mundial: ≥ 85%
r.plot() # gráfica horizontal con referencia world-class (85%)
utilization_efficiency(actual_output, max_output) — Utilización
r = wl.utilization_efficiency(actual_output=750, max_output=1000)
print(r.utilization) # 0.75 — 75% de utilización
print(r.idle_fraction) # 0.25 — 25% capacidad ociosa
unit_cost(fixed_cost, variable_cost, units) — Costo unitario
r = wl.unit_cost(fixed_cost=10_000, variable_cost=5_000, units=500)
print(r.unit_cost) # 30.0 $/unidad
print(r.fixed_cost_per_unit) # 20.0
print(r.var_cost_per_unit) # 10.0
9. Análisis de cuellos de botella
bottleneck_analysis(station_names, capacities, demand_rate) — Teoría de restricciones
Identifica el cuello de botella de una línea de producción comparando la capacidad de cada estación con la demanda.
r = wl.bottleneck_analysis(
station_names=["Corte", "Soldadura", "Pintura"],
capacities=[120.0, 80.0, 100.0], # unidades/hora por estación
demand_rate=70.0, # demanda requerida
)
print(r)
# Bottleneck: Soldadura (capacidad 80 < demanda 70 con menor margen)
# Utilization: Corte=58.3%, Soldadura=87.5%, Pintura=70.0%
r.plot() # barras de utilización con cuello de botella en rojo
10. Balance de línea y tiempo takt
takt_time(available_time, demand) — Tiempo takt
Ritmo de producción requerido para satisfacer la demanda del cliente.
takt = wl.takt_time(available_time=480, demand=60)
print(takt) # 8.0 min/unidad
line_balance(names, cycle_times, takt) — Balance de línea
Analiza si cada estación puede cumplir con el tiempo takt y calcula la eficiencia de balance.
lb = wl.line_balance(
names=["A", "B", "C"],
cycle_times=[5.0, 9.0, 4.0],
takt=10.0,
)
print(lb.bottleneck) # "B" — estación más lenta
print(lb.balance_efficiency) # 0.60 — 60% de eficiencia
print(lb.total_idle_time) # tiempo ocioso total
lb.plot() # barras de cycle time vs takt
11. Análisis de punto de equilibrio
break_even(fixed_cost, price_per_unit, variable_cost_per_unit, *, actual_units) — Punto de equilibrio
be = wl.break_even(
fixed_cost=10_000,
price_per_unit=25,
variable_cost_per_unit=15,
actual_units=1_500,
)
print(be.bep_units) # 1000.0 — punto de equilibrio en unidades
print(be.bep_revenue) # 25000.0 — ingresos en el punto de equilibrio
print(be.margin_of_safety_pct) # 33.3% — margen de seguridad sobre ventas actuales
print(be.profit) # 5000.0 — beneficio con actual_units
be.plot() # líneas de ingresos/costos con BEP y zona de beneficio
break_even_multi(fixed_cost, prices, variable_costs, sales_mix) — Multi-producto
Calcula el punto de equilibrio para una mezcla de varios productos usando la contribución marginal ponderada (WACM).
Fórmula: WACM = Σ(CMᵢ × mixᵢ) / Σmixᵢ, luego BEP_total = FC / WACM
r = wl.break_even_multi(
fixed_cost=120_000,
prices=[50, 80, 120],
variable_costs=[30, 50, 70],
sales_mix=[3, 2, 1], # proporción de ventas
)
print(r.weighted_avg_cm) # WACM ≈ 36.67
print(r.bep_units_total) # unidades totales en el punto de equilibrio
print(r.bep_revenue_total) # ingresos totales en el punto de equilibrio
for item in r.items:
print(item["Name"], item["BEP units"])
r.to_frame() # DataFrame con una fila por producto
| Atributo | Descripción |
|---|---|
weighted_avg_cm |
Contribución marginal ponderada |
bep_units_total |
Unidades totales en el BEP |
bep_revenue_total |
Ingresos totales en el BEP |
items |
Lista de dicts por producto con BEP units, BEP revenue |
break_even_sales(fixed_cost, variable_cost_ratio, *, actual_revenue) — Punto de equilibrio en ingresos
Calcula el BEP en términos de ingresos cuando los costos variables se expresan como porcentaje de las ventas.
Fórmula: BEP = FC / (1 − RCV)
r = wl.break_even_sales(
fixed_cost=50_000,
variable_cost_ratio=0.60, # 60% de los ingresos son costos variables
actual_revenue=200_000,
)
print(r.bep_revenue) # 125 000 — ingresos en el BEP
print(r.contribution_margin_ratio) # 0.40 — margen de contribución
print(r.margin_of_safety_units) # 75 000 — margen de seguridad en ingresos
print(r.margin_of_safety_pct) # 0.375 — porcentaje del margen de seguridad
14. Árboles de KPI
Los árboles de KPI crean jerarquías interactivas visualizadas como treemap o sunburst con Plotly.
oee_kpi_tree(availability, performance, quality)
tree = wl.oee_kpi_tree(0.90, 0.80, 0.95)
tree.plot() # treemap (por defecto)
tree.plot(kind="sunburst") # diagrama radial
throughput_kpi_tree(actual_throughput, capacity, defect_rate)
tree = wl.throughput_kpi_tree(
actual_throughput=750,
capacity=1000,
defect_rate=0.05,
)
tree.plot()
roi_kpi_tree(revenue, fixed_cost, variable_cost_per_unit, units_sold, investment)
tree = wl.roi_kpi_tree(
revenue=50_000,
fixed_cost=10_000,
variable_cost_per_unit=8,
units_sold=2_000,
investment=20_000,
)
print(tree.value) # ROI = 0.2 (20%)
tree.plot()
KPINode — árbol personalizado
root = wl.KPINode("EBITDA", value=100_000, unit="€")
rev = root.add_child("Revenue", value=200_000, unit="€")
cost = root.add_child("Operating costs", value=100_000, unit="€")
rev.add_child("Product A", value=120_000, unit="€")
rev.add_child("Product B", value=80_000, unit="€")
from walopy.plotting import plot_kpi_tree
fig = plot_kpi_tree(root, kind="treemap")
fig.show()
15. Solvers y optimización
solve_lam(metric, target, *, mu, model) — Máxima tasa de llegada
Encuentra la mayor λ tal que una métrica no supere el objetivo.
r = wl.solve_lam("Wq", target=0.5, mu=5.0, model="mm1")
print(r.value) # λ máxima ≈ 3.33
print(r.achieved_value) # Wq ≈ 0.5
print(r.model_result) # QueueResult completo en la solución
solve_mu(metric, target, *, lam, model) — Mínima tasa de servicio
r = wl.solve_mu("Wq", target=0.3, lam=3.0, model="mm1")
print(r.value) # μ mínima necesaria ≈ 5.0
solve_servers(metric, target, *, lam, mu) — Mínimo número de servidores
r = wl.solve_servers("Wq", target=0.1, lam=8.0, mu=5.0)
print(r.value) # c mínimo = 3
print(r.model_result) # QueueResult para M/M/3
optimize_servers(lam, mu, cost_per_server, cost_per_wait) — Optimización de costo total
Minimiza cost_per_server × c + cost_per_wait × λ × Wq evaluando todos los c viables.
r = wl.optimize_servers(
lam=6.0,
mu=5.0,
cost_per_server=10.0, # costo fijo por servidor por unidad de tiempo
cost_per_wait=5.0, # costo por unidad de tiempo de espera de cada cliente
)
print(r.optimal_servers) # c óptimo
print(r.optimal_cost) # costo mínimo
r.plot() # gráfica de costo vs número de servidores
16. Análisis de sensibilidad
sensitivity(model_fn, param, values, **fixed_kwargs) — Barrido paramétrico
Evalúa un modelo sobre un rango de valores de un parámetro.
import numpy as np
df = wl.sensitivity(
wl.mm1,
param="lam",
values=np.linspace(0.5, 4.5, 20),
mu=5.0, # parámetros fijos
)
print(df.columns.tolist()) # ['lam', 'rho', 'L', 'Lq', 'W', 'Wq', ...]
# Barrido sobre μ
df2 = wl.sensitivity(wl.mm1, "mu", np.linspace(4.0, 10.0, 15), lam=3.0)
# Barrido sobre número de servidores en M/M/c
df3 = wl.sensitivity(wl.mmc, "c", range(1, 6), lam=8.0, mu=5.0)
# Visualizar
from walopy.plotting import plot_sensitivity
fig = plot_sensitivity(df, "lam", metrics=["Wq", "Lq"])
fig.show()
17. Escenarios en lote (batch_model)
batch_model(model_fn, df, **fixed_kwargs) — Aplicar modelo a un DataFrame
Aplica cualquier función de walopy a cada fila de un DataFrame de escenarios.
import pandas as pd
# Varios escenarios con distintas tasas de llegada
escenarios = pd.DataFrame({
"lam": [1.0, 2.0, 3.0, 4.0]
})
resultado = wl.batch_model(wl.mm1, escenarios, mu=5.0)
print(resultado[["lam", "rho", "Wq", "L"]])
# Escenarios mixtos (lambda y mu variables)
escenarios2 = pd.DataFrame({
"lam": [2.0, 3.0, 4.0],
"mu": [5.0, 6.0, 7.0],
})
resultado2 = wl.batch_model(wl.mm1, escenarios2)
# Con número de servidores variable (columna int)
escenarios3 = pd.DataFrame({
"c": [1, 2, 3, 4],
})
resultado3 = wl.batch_model(wl.mmc, escenarios3, lam=8.0, mu=5.0)
# Las filas con error quedan marcadas en "_error"
print(resultado[resultado["_error"].notna()]) # filas fallidas
18. Comparar múltiples resultados
compare(*results, labels) — DataFrame comparativo
Construye un DataFrame con una fila por resultado para facilitar la comparación.
df = wl.compare(
wl.mm1(3.0, 5.0),
wl.mmc(3.0, 5.0, 2),
wl.md1(3.0, 5.0),
labels=["M/M/1", "M/M/2", "M/D/1"],
)
print(df[["label", "ρ (utilization)", "Wq (wait time)", "L (system)"]])
# label ρ (utilization) Wq (wait time) L (system)
# 0 M/M/1 0.6 0.3000 1.50
# 1 M/M/2 0.3 0.0302 1.02
# 2 M/D/1 0.6 0.1500 1.05
# Sin etiquetas → scenario_1, scenario_2, ...
df2 = wl.compare(wl.mm1(1, 5), wl.mm1(2, 5), wl.mm1(3, 5))
19. Gráficas
Todos los resultados exponen .plot() que devuelve una figura de Matplotlib o Plotly. También se pueden llamar directamente desde walopy.plotting.
Función .plot() |
Tipo | Descripción |
|---|---|---|
QueueResult.plot() |
Matplotlib | Curvas Wq y Lq vs ρ con punto operativo |
OEEResult.plot() |
Matplotlib | Barras horizontales OEE con referencia 85% |
BottleneckResult.plot() |
Matplotlib | Utilización por estación, cuello de botella en rojo |
EOQResult.plot() |
Matplotlib | Curvas de costo de mantener, ordenar y total |
LineBalanceResult.plot() |
Plotly | Cycle time vs takt por estación |
BreakEvenResult.plot() |
Plotly | Líneas ingreso/costo con BEP y zona de beneficio |
SimulationResult.plot() |
Plotly | Histograma de Wq + CDF empírica con percentiles |
OptimizeResult.plot() |
Plotly | Costo por servidor, costo de espera y total vs c |
KPINode.plot(kind=) |
Plotly | Treemap o sunburst interactivo |
# Guardar como HTML interactivo (Plotly)
fig = r.plot()
fig.write_html("resultado.html")
# Guardar como imagen (Matplotlib)
fig = wl.oee(0.9, 0.8, 0.95).plot()
fig.savefig("oee.png", dpi=150)
Para la curva de sensibilidad:
from walopy.plotting import plot_sensitivity
df = wl.sensitivity(wl.mm1, "lam", np.linspace(0.5, 4.5, 20), mu=5.0)
fig = plot_sensitivity(df, "lam", metrics=["Wq", "Lq", "L"])
fig.show()
20. CLI
walopy incluye una interfaz de línea de comandos para uso rápido sin escribir código.
# M/M/1
python -m walopy mm1 --lam 3 --mu 5
# M/M/c
python -m walopy mmc --lam 8 --mu 5 --c 2
# M/D/1
python -m walopy md1 --lam 3 --mu 5
# G/G/1 (Kingman)
python -m walopy gg1 --lam 3 --mu 5 --ca2 1.2 --cs2 0.8
# Ley de Little (resolver W)
python -m walopy littles --L 2.5 --lam 5.0
# EOQ
python -m walopy eoq --demand 1000 --ordering 50 --holding 2
# Versión
python -m walopy --version
Ejemplo de salida:
Model : M/M/1
λ : 3.0 (arrival rate)
μ : 5.0 (service rate per server)
ρ : 0.6 (utilization)
L : 1.5 (avg units in system)
Lq : 0.9 (avg units in queue)
W : 0.5 (avg time in system)
Wq : 0.3 (avg wait time in queue)
P0 : 0.4
12. Programación de producción (scheduling)
Máquina única — schedule_single
Asigna n trabajos a una sola máquina según una regla de prioridad.
import walopy as wl
p = [3, 1, 4, 1, 5] # tiempos de proceso
d = [8, 3, 10, 4, 12] # fechas de entrega
w = [2, 5, 1, 4, 1] # pesos de importancia
r = wl.schedule_single(p, rule="SPT", due_dates=d, weights=w,
names=["A","B","C","D","E"])
print(r)
Rule : SPT
Sequence : B → D → A → C → E
Makespan (Cmax) : 14
Total completion ΣCj : 37
Weighted completion ΣwCj: 89
Max lateness : 2
Total tardiness ΣTj : 2
Tardy jobs : 1
Reglas disponibles
rule |
Criterio minimizado | Datos requeridos |
|---|---|---|
'SPT' |
ΣCj (tiempo total de completación) | — |
'EDD' |
Lmax (máxima tardanza) | due_dates |
'WSPT' |
ΣwjCj (completación ponderada) | weights |
'CR' |
Ratio crítico dj/pj | due_dates |
'FIFO' |
Orden de llegada (línea base) | — |
Atributos de ScheduleResult
| Atributo | Descripción |
|---|---|
rule |
Regla utilizada |
sequence |
Lista de nombres en orden de procesamiento |
jobs |
Lista de JobSchedule (por trabajo) |
makespan |
Cmax = suma de tiempos de proceso |
total_completion_time |
ΣCj |
total_weighted_completion_time |
ΣwjCj |
max_lateness |
max(Lj) |
total_tardiness |
ΣTj |
n_tardy |
Número de trabajos tarde |
df = r.to_frame() # columnas: Name, p, d, w, Start, C, L, T, Tardy
Flow-shop de 2 máquinas — johnson_flowshop
El algoritmo de Johnson encuentra la secuencia óptima que minimiza el makespan cuando todos los trabajos pasan primero por M1 y luego por M2.
m1 = [3, 8, 5, 7, 2] # tiempos en máquina 1
m2 = [5, 2, 8, 4, 6] # tiempos en máquina 2
r = wl.johnson_flowshop(m1, m2)
print(r)
Sequence : J1 → J5 → J4 → J3 → J2
Makespan : 34
df = r.to_frame()
# columnas: Job | M1 start | M1 end | M2 start | M2 end
13. Confiabilidad — reliability
Modelos de confiabilidad con distribución exponencial (tasa de falla constante).
Componente individual — mtbf_analysis
r = wl.mtbf_analysis(failure_rate=0.01, mttr=5.0, t=100)
print(r)
Topology : component
Components : 1
Failure rates λ : ['0.01']
MTBF : 100
R(t=100) : 0.367879
Availability : 95.2381%
Sistema en serie — series_system
El sistema falla si cualquier componente falla. λ_sys = Σλi.
r = wl.series_system([0.01, 0.02, 0.03], t=10)
print(r.mtbf) # 1/0.06 ≈ 16.67
print(r.R_t) # e^{-0.06·10} ≈ 0.549
Sistema en paralelo — parallel_system
El sistema opera si al menos uno de los componentes opera.
R_sys(t) = 1 − ∏(1 − e^{−λi·t}). MTBF calculado numéricamente.
r = wl.parallel_system([0.01, 0.02], t=50)
print(r.R_t) # mayor que el sistema en serie
print(r.mtbf) # mayor que cualquier componente individual
Sistema k-de-n — koon_system
Opera si al menos k de n componentes idénticos están en servicio.
MTBF exacto = (1/λ) · Σ_{j=k}^{n} (1/j).
# 2-de-3: requiere al menos 2 de 3 componentes idénticos
r = wl.koon_system(n=3, k=2, failure_rate=0.01, t=50)
print(r.mtbf) # (1/0.01) * (1/2 + 1/3) ≈ 83.33
print(r.R_t)
Casos especiales
k=1→ equivale aparallel_systemcon n componentes idénticos.k=n→ equivale aseries_systemcon n componentes idénticos.
Atributos de ReliabilityResult
| Atributo | Descripción |
|---|---|
topology |
'component', 'series', 'parallel', 'k-of-n' |
n_components |
Número de componentes |
failure_rates |
Lista de λi |
mtbf |
MTBF del sistema |
R_t |
Confiabilidad R(t) si se proporcionó t |
availability |
Disponibilidad estacionaria A si se proporcionó mttr |
mttr |
Tiempo medio de reparación (si se proporcionó) |
df = r.to_frame() # columnas: Topology, Components, MTBF[, R(t), Availability]
r.R(t=200) # evalúa R(t) en cualquier tiempo posterior
21. Referencia de módulos
| Módulo | Funciones y clases principales |
|---|---|
queuing |
mm1, mmc, md1, kingman, mg1, littles_law, QueueResult, cv2_normal, cv2_triangular, cv2_uniform, cv2_erlang, cv2_gamma, cv2_lognormal, cv2_weibull |
advanced |
mm1k, mmck, erlang_b, mm1_priority, monte_carlo_gg1, takt_time, line_balance, break_even, break_even_multi, break_even_sales, queue_length_pmf, sojourn_cdf, PriorityQueueResult, SimulationResult, LineBalanceResult, BreakEvenResult, BreakEvenMultiResult |
fitting |
fit_from_data, FitResult |
inventory |
eoq, ebq, eoq_multi, ebq_multi, eoq_multi_constrained, lot_for_lot, silver_meal, eoq_quantity_discount, wagner_whitin, rq_policy, rs_policy, reorder_point, newsvendor, EOQResult, EBQResult, MultiItemResult, ConstrainedMultiEOQResult, LotSizingResult, QuantityDiscountResult, ReorderResult, NewsvendorResult, RQPolicyResult, RSPolicyResult |
scheduling |
schedule_single, johnson_flowshop, ScheduleResult, JobSchedule, FlowShopResult |
reliability |
mtbf_analysis, series_system, parallel_system, koon_system, ReliabilityResult |
network |
jackson_network, JacksonResult, StationMetrics |
operations |
oee, utilization_efficiency, unit_cost, OEEResult, UtilizationResult, UnitCostResult |
bottleneck |
bottleneck_analysis, BottleneckResult, StationResult |
kpi |
KPINode, oee_kpi_tree, throughput_kpi_tree, roi_kpi_tree |
solver |
solve_lam, solve_mu, solve_servers, optimize_servers, sensitivity, batch_model, compare, SolverResult, OptimizeResult |
plotting |
plot_queue_sensitivity, plot_queue_metrics, plot_oee, plot_bottleneck, plot_eoq, plot_kpi_tree, plot_sensitivity, plot_optimize_servers, plot_simulation, plot_line_balance, plot_break_even, plot_queue_distribution |
22. Requisitos
Python ≥ 3.9
numpy ≥ 1.22
pandas ≥ 1.4
matplotlib ≥ 3.5
plotly ≥ 5.0
No se requiere scipy. Los cálculos estadísticos usan statistics.NormalDist de la biblioteca estándar de Python.
Licencia
MIT — ver LICENSE.
Contribuciones
Pull requests bienvenidas. Asegúrate de que pytest tests/ pase antes de abrir un PR.
Metadata
Release files for walopy 0.2.6
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| walopy-0.2.6.tar.gz | 111.2 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| walopy-0.2.6-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 184.2 kB
Release files / walopy-0.2.6.tar.gz
| Download URL | walopy-0.2.6.tar.gz |
|---|---|
| Size | 111.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5dc2aa483774252a034308bf2e6d3db808703549a77c7b4a8dc175c9b893d459
|
|
BLAKE2b-256 checksum How to use checksums |
3a6480842617477b2d9849b6025cbb3ac76966c699237defe61c9c1158049cee
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.
Transparency logRelease files / walopy-0.2.6-py3-none-any.whl
| Download URL | walopy-0.2.6-py3-none-any.whl |
|---|---|
| Size | 73.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9e58fe04695a4f10717823c921a91670e2e1a504b3b27e365ef7358b14ce846a
|
|
BLAKE2b-256 checksum How to use checksums |
f932dc97569f77472706a34b859138b21f62385ba27d0f1d83dec27bcc3f4f2e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.
Transparency logSecurity for walopy 0.2.6
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for walopy-0.2.6.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.