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_path_matrix(dummies=False)
  A B C D E F G H
Route_1 1 0 1 0 1 0 1 0
Route_2 1 0 0 1 1 0 1 0
Route_3 1 0 0 1 0 1 1 0
Route_4 0 1 0 0 1 0 1 0
Route_5 0 1 0 0 0 1 1 0
Route_6 0 1 0 0 0 0 0 1

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
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 10.925301 4.446971 6.645871 1.839015 6.548168 9.488839 4.176080 4.809138
1 10.336844 4.653989 5.587939 2.459432 6.773722 7.531909 5.320504 8.328515
2 9.900782 5.748689 5.861894 3.397602 2.419875 7.681707 1.904833 6.327645
3 9.023336 4.765365 4.989582 2.432137 7.606742 9.317281 2.558808 8.133549
4 10.867391 5.509260 5.052959 2.227036 8.093987 8.077850 4.918052 9.203615

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 28.295421 23.488565 26.429235 15.171219 18.111890 9.256109
1 28.019009 24.890501 25.648689 16.748215 17.506402 12.982504
2 20.087384 17.623092 22.884924 10.073397 15.335228 12.076333
3 24.178468 21.621023 23.331562 14.930915 16.641453 12.898914
4 28.932390 26.106467 26.090330 18.521299 18.505162 14.712875

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 28.295421
1 28.019009
2 22.884924
3 24.178468
4 28.932390
5 28.135973
6 25.834992
7 31.058014
8 28.493112
9 25.954879

Apartado 4

  1. Determina la duración media y la desviación típica de la duración del proyecto.
duracion_proyecto.plot(kind='density');

duracion_proyecto.describe()
duracion_proyecto
count 1000.000000
mean 27.165373
std 2.708426
min 18.588942
25% 25.285334
50% 27.055871
75% 28.937220
max 35.337673
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.2 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 33.0 periodos

O bien, utilizando la función quantile

resultado_2 = round(duracion_proyecto.quantile(q=0.98, interpolation='higher'), 1)
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 33.0 Name: 0.98, dtype: float64 periodos.