Ejercicio 17

Enunciado

Cálculo de la duración de un proyecto usando el Método de Montecarlo

Considere el proyecto cuya información se adjunta.

  1. Dibuje el grafo PERT del proyecto.
  1. Determine la matriz de caminos del proyecto.
  1. Calcule, utilizando la matriz de caminos del proyecto, los tiempos tempranos de cada nodo para 1000 iteraciones del método de MonteCarlo.
  1. Determina la duración media y la desviación típica de la duración del proyecto.
  1. Determine la duración para una probabilidad de completar antes el proyecto del 98%.
import pandas as pd
pd.set_option("display.max_columns", None)
pd.set_option("display.max_rows", None)

import numpy as np
import matplotlib as plt
%matplotlib inline

datos_enunciado = pd.DataFrame([
#  actividad, predecesora, duracion media, desviación típica
     ('A',    '---  ', 10, 1.5 ),
     ('B',    '---  ',  5, 0.4 ),
     ('C',    'A    ',  6, 0.8 ),
     ('D',    'A    ',  2, 0.7 ),
     ('E',    'B,C,D',  7, 1.8 ),
     ('F',    'B,D  ',  8, 0.8 ),
     ('G',    'E,F  ',  4, 1.4 ),
     ('H',    'B    ',  7, 1.6 ),
] ,columns=['activity', 'predecessor', 'mean', 'stdev']).set_index('activity')

datos_enunciado
Tabla 1: Cuadro de datos del enunciado del ejercicio
predecessor mean stdev
activity
A --- 10 1.5
B --- 5 0.4
C A 6 0.8
D A 2 0.7
E B,C,D 7 1.8
F B,D 8 0.8
G E,F 4 1.4
H B 7 1.6

Solución

Apartado 1

  1. Dibuje el grafo PERT del proyecto.
datos_proyecto =  datos_enunciado.copy()
prelaciones = PredecessorTable.from_dataframe_of_strings(datos_proyecto, activity='activity', predecessor='predecessor')
proyecto = prelaciones.create_project()
proyecto.pert(ranksep=0.6, nodesep=0.6)

Apartado 2

  1. Determine la matriz de caminos del proyecto.
path_matrix = proyecto.path_matrix(dummies=False)
proyecto.display_rutas("pert")
👆 Interactivo: haz clic en una fila de la matriz para resaltar en el grafo las actividades y los nodos de esa ruta (otro clic la deselecciona).

Apartado 3

  1. Calcule, utilizando la matriz de caminos del proyecto, los tiempos tempranos de cada nodo para 1000 iteraciones del método de MonteCarlo.

Generación aleatoria de las duraciones de las actividades

number_of_samples = 1000
activity_names = datos_proyecto.index
np.random.seed(2025)   # reproducibilidad de la simulación
duration = pd.DataFrame([np.random.normal(size = number_of_samples,
                                          loc  = datos_proyecto.loc[task, 'mean',],
                                          scale= datos_proyecto.loc[task, 'stdev'])
                         for task in activity_names
                        ],
                        index=activity_names)
duration.T.head()
activity A B C D E F G H
0 9.861415 5.333463 5.664786 2.503223 8.664686 7.610057 3.623105 6.154956
1 11.101428 5.365496 3.876074 2.087051 8.603406 7.468062 2.533273 7.211664
2 7.841677 5.043916 6.088211 2.025739 5.381889 8.508664 4.581449 8.517712
3 9.004867 5.430444 7.022188 3.160544 9.021766 9.098809 1.077909 8.956832
4 9.848908 4.842277 5.885648 0.245137 8.982527 6.907041 4.243551 7.096002

Cálculo de la duración del proyecto

La duración del proyecto se obtiene multiplicando la matriz de caminos por las duraciones de las actividades. El resultado es la duración de las rutas en cada iteración.

duraciones_caminos = path_matrix @ duration   # Multiplicación matricial
duraciones_caminos.T.head()
Route_1 Route_2 Route_3 Route_4 Route_5 Route_6
0 27.813991 24.652429 23.597800 17.621254 16.566625 11.488419
1 26.114181 24.325158 23.189814 16.502174 15.366830 12.577159
2 23.893226 19.830753 22.957528 15.007254 18.134028 13.561628
3 26.126730 22.265085 22.342128 15.530118 15.607161 14.387276
4 28.960635 23.320124 21.244638 18.068356 15.992870 11.938279

La duración del proyecto en cada iteración se obtiene calculando el máximo de la duración de los caminos en esa iteración.

duracion_proyecto = duraciones_caminos.max(axis="rows").to_frame(name='duracion_proyecto')
duracion_proyecto.head(10)
duracion_proyecto
0 27.813991
1 26.114181
2 23.893226
3 26.126730
4 28.960635
5 26.674733
6 29.099118
7 28.088891
8 29.190272
9 28.317675

Apartado 4

  1. Determina la duración media y la desviación típica de la duración del proyecto.
from pmule import display_histograma_tikz
display_histograma_tikz(duracion_proyecto['duracion_proyecto'])
histograma de Montecarlo
duracion_proyecto.describe()
duracion_proyecto
count 1000.000000
mean 27.061946
std 2.666572
min 18.545490
25% 25.198429
50% 27.088022
75% 28.783172
max 35.433588
Markdown(f"La duración media del proyecto es: {round(duracion_proyecto.mean(), 1)}, la desviación típica de la duración del proyecto es: {round(duracion_proyecto.std(), 1)}")

La duración media del proyecto es: duracion_proyecto 27.1 dtype: float64, la desviación típica de la duración del proyecto es: duracion_proyecto 2.7 dtype: float64

Apartado 5

  1. Determine la duración para una probabilidad de completar antes el proyecto del 98%.

Podemos ordenar los valores en sentido ascendente y quedarnos con aquél valor superior al 98% de las muestras.

resultado_1 = round(duracion_proyecto.sort_values(ascending=True, by='duracion_proyecto').iloc[int(0.98*number_of_samples),0], 1)
Markdown(f"Podemos ordenar los valores en sentido ascendente y quedarnos con aquél valor superior al 98% de las muestras. De esta manera obtenemos un resultado igual a {resultado_1} periodos")

Podemos ordenar los valores en sentido ascendente y quedarnos con aquél valor superior al 98% de las muestras. De esta manera obtenemos un resultado igual a 32.8 periodos

O bien, utilizando la función quantile

resultado_2 = round(duracion_proyecto.quantile(q=0.98, interpolation='higher'), 1)
from pmule import display_histograma_tikz
display_histograma_tikz(duracion_proyecto['duracion_proyecto'], percentil=0.98)
histograma de Montecarlo
Markdown(f"O bien, utilizando la función `quantile`, obteniendo igualmente un valor de {resultado_2} periodos.")

O bien, utilizando la función quantile, obteniendo igualmente un valor de duracion_proyecto 32.8 Name: 0.98, dtype: float64 periodos.