<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Microsoft Excel archivos &#187; Ingenieria Industrial Online</title>
	<atom:link href="https://ingenieriaindustrialonline.com/tag/microsoft-excel/feed/" rel="self" type="application/rss+xml" />
	<link>https://ingenieriaindustrialonline.com/tag/microsoft-excel/</link>
	<description>ingenieriaindustriaonline.com</description>
	<lastBuildDate>Sat, 09 Oct 2021 19:11:37 +0000</lastBuildDate>
	<language>es</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.3</generator>

<image>
	<url>https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/cropped-faVicon-32x32.png</url>
	<title>Microsoft Excel archivos &#187; Ingenieria Industrial Online</title>
	<link>https://ingenieriaindustrialonline.com/tag/microsoft-excel/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>¿Cómo hacer un diagrama de Pareto utilizando Excel o Python?</title>
		<link>https://ingenieriaindustrialonline.com/gestion-y-control-de-calidad/como-hacer-un-diagrama-de-pareto-utilizando-excel-o-python/</link>
					<comments>https://ingenieriaindustrialonline.com/gestion-y-control-de-calidad/como-hacer-un-diagrama-de-pareto-utilizando-excel-o-python/#respond</comments>
		
		<dc:creator><![CDATA[Bryan Salazar López]]></dc:creator>
		<pubDate>Tue, 10 Aug 2021 03:05:50 +0000</pubDate>
				<category><![CDATA[Gestión y control de calidad]]></category>
		<category><![CDATA[Diagrama de Pareto]]></category>
		<category><![CDATA[Herramientas de Calidad]]></category>
		<category><![CDATA[Microsoft Excel]]></category>
		<category><![CDATA[Pareto]]></category>
		<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://ingenieriaindustrialonline.com/?p=26801</guid>

					<description><![CDATA[<p>El diagrama de Pareto es una variación del histograma tradicional, puesto que en el Pareto se ordenan los datos por su frecuencia de mayor a menor. El principio de Pareto, también conocido como la regla 80 &#8211; 20, enunció en su momento que «el 20% de la población, poseía el 80% de la riqueza». Su &#8230;</p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/gestion-y-control-de-calidad/como-hacer-un-diagrama-de-pareto-utilizando-excel-o-python/">¿Cómo hacer un diagrama de Pareto utilizando Excel o Python?</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>El diagrama de Pareto es una variación del histograma tradicional, puesto que en el Pareto se ordenan los datos por su frecuencia de mayor a menor. El principio de Pareto, también conocido como la regla 80 &#8211; 20, enunció en su momento que «<em>el 20% de la población, poseía el 80% de la riqueza</em>».</p>
<p>Su uso se aplica con relativo éxito en muchos ámbitos, soportando metodologías de priorización, por ejemplo. El diagrama de Pareto hace parte de lo que se denominan técnicas gráficas de calidad, como lo son las <a href="https://ingenieriaindustrialonline.com/gestion-de-calidad/las-siete-herramientas-de-la-calidad/">siete herramientas básicas</a><strong>, </strong>utilizadas para la solución de problemas referentes a la calidad, mencionadas por primera vez por <em><strong>Kaoru Ishikawa</strong></em>.</p>
<p>El objetivo de este artículo consiste en el desarrollo de un par de metodologías de elaboración de diagramas de Pareto, haciendo uso de dos herramientas: la tradicional hoja de cálculo de Excel, y el popularizado lenguaje de programación Python.</p>
<h2>Diagrama de Pareto en Excel (2016)</h2>

		<div id="diagrama-de-pareto-en-excel-2016" data-title="Diagrama de Pareto en Excel (2016)" class="index-title"></div>
	
<p>Supongamos que un proceso que produce refrigeradores desea establecer controles sobre los defectos que aparecen en las unidades que salen como producto terminado en la línea de producción. Para ello se hace necesario determinar cuáles son los defectos más frecuentes. En primer lugar se clasificaron todos los defectos posibles:</p>

		<div class="thumbdown tie-list-shortcode">
<ul>
<li>Motor no detiene</li>
<li>No enfría</li>
<li>Burlete def.</li>
<li>Pintura def.</li>
<li>Rayas</li>
<li>No funciona</li>
<li>Puerta no cierra</li>
<li>Gavetas def.</li>
<li>Motor no arranca</li>
<li>Mala nivelación</li>
<li>Puerta def.</li>
<li>Otros</li>
</ul>

		</div>
	
<p>Después de inspeccionar 88 refrigeradores defectuosos, se obtuvo la siguiente tabla de frecuencias:</p>
<table width="215">
<tbody>
<tr>
<td width="135" style="text-align: center;"><strong>Tipo de defecto</strong></td>
<td width="80" style="text-align: center;"><strong>Frecuencia</strong></td>
</tr>
<tr>
<td>Burlete defectuoso</td>
<td style="text-align: center;">9</td>
</tr>
<tr>
<td>Pintura defectuosa</td>
<td style="text-align: center;">5</td>
</tr>
<tr>
<td>Gavetas defectuosas</td>
<td style="text-align: center;">1</td>
</tr>
<tr>
<td>Mala nivelación</td>
<td style="text-align: center;">1</td>
</tr>
<tr>
<td>Motor no arranca</td>
<td style="text-align: center;">1</td>
</tr>
<tr>
<td>Motor no se detiene</td>
<td style="text-align: center;">36</td>
</tr>
<tr>
<td>No enfría</td>
<td style="text-align: center;">27</td>
</tr>
<tr>
<td>No funciona</td>
<td style="text-align: center;">2</td>
</tr>
<tr>
<td>Otros</td>
<td style="text-align: center;">0</td>
</tr>
<tr>
<td>Puerta defectuosa</td>
<td style="text-align: center;">0</td>
</tr>
<tr>
<td>Puerta no cierra</td>
<td style="text-align: center;">2</td>
</tr>
<tr>
<td>Rayas</td>
<td style="text-align: center;">4</td>
</tr>
<tr>
<td style="text-align: center;"><strong>Total</strong></td>
<td style="text-align: center;"><strong>88</strong></td>
</tr>
</tbody>
</table>
<p>Ordenamos los datos de mayor  a menor y anexamos una columna de frecuencia relativa (porcentual) y otra de frecuencia relativa acumulada (porcentual). Para obtener las frecuencias relativas utilizamos la siguientes fórmulas:</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Sin-titulo.png" alt="Frecuencia relativa" width="478" height="78" class="aligncenter size-full wp-image-26803" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Sin-titulo.png 478w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Sin-titulo-300x49.png 300w" sizes="(max-width: 478px) 100vw, 478px" /></p>
<p>Veamos por ejemplo la frecuencia relativa del defecto «<em><strong>motor no se detiene</strong></em>«:</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Sin-titulo_1.png" alt="" width="500" height="86" class="aligncenter size-full wp-image-26804" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Sin-titulo_1.png 500w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Sin-titulo_1-300x52.png 300w" sizes="(max-width: 500px) 100vw, 500px" /></p>
<p>Podemos calcular la frecuencia relativa porcentual de la siguiente manera:</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Sin-titulo_2.png" alt="" width="500" height="86" class="aligncenter size-full wp-image-26805" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Sin-titulo_2.png 500w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Sin-titulo_2-300x52.png 300w" sizes="(max-width: 500px) 100vw, 500px" /></p>
<p><em>Veamos cómo hacerlo en Excel:</em></p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Sin-titulo_3.png" alt="Tabla de frecuencias Excel" width="719" height="299" class="aligncenter size-full wp-image-26806" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Sin-titulo_3.png 719w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Sin-titulo_3-300x125.png 300w" sizes="(max-width: 719px) 100vw, 719px" /></p>
<p>Recuerde que previamente debe ordenar las frecuencias de mayor a menor. Utilizando esta formulación, obtenemos el siguiente resultado:</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Sin-titulo_4.png" alt="" width="588" height="287" class="aligncenter size-full wp-image-26807" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Sin-titulo_4.png 588w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Sin-titulo_4-300x146.png 300w" sizes="(max-width: 588px) 100vw, 588px" /></p>
<p>Aquí podemos observar como la <em><strong>causa 1: motor no se detiene</strong></em> se ha presentado con una frecuencia de 36 defectos (de un total de 88 defectos totales), lo que representa un 40,91% del total de defectos. También podemos, con base en nuestra data de estudio, que las causas 1, 2 y 3, representan más del 80% de los defectos totales (81,82% exactamente). Si consideramos que estas son 3 causas de 12 causas totales, esto se aproxima a la regla empírica de Pareto (3/12 = 0,25). En este caso, el 81,82% de los defectos se debe al 25% de las causas.</p>
<p>Veamos ahora cómo graficar el Diagrama de Pareto tradicional:</p>
<p>Seleccionamos los datos de las columnas &#8216;Tipo de defecto&#8217;, &#8216;Frecuencia&#8217; y &#8216;Frecuencia relativa acumulada&#8217;. A continuación damos clic en el Menú “<em><strong>Insertar</strong></em>”; seleccionamos «<em><strong>Gráficos recomendados</strong></em>”; luego en “<em><strong>Pareto</strong></em>”.</p>

		<div class="post-content-slideshow-outer">
			<div class="post-content-slideshow">

			<div class="loader-overlay"><div class="spinner-circle"></div></div>

				<div class="tie-slick-slider">

			<div class="slide post-content-slide">
				 <img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Excel1.webp" alt="" width="1183" height="518" class="aligncenter size-full wp-image-26810" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Excel1.webp 1183w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Excel1-300x131.webp 300w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Excel1-1024x448.webp 1024w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Excel1-768x336.webp 768w" sizes="(max-width: 1183px) 100vw, 1183px" /> 
			</div><!-- post-content-slide -->
		

			<div class="slide post-content-slide">
				 <img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Excel2.webp" alt="" width="1183" height="518" class="aligncenter size-full wp-image-26811" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Excel2.webp 1183w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Excel2-300x131.webp 300w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Excel2-1024x448.webp 1024w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Excel2-768x336.webp 768w" sizes="(max-width: 1183px) 100vw, 1183px" /> 
			</div><!-- post-content-slide -->
		

			<div class="slide post-content-slide">
				
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Excel5.png" alt="Excel Pareto" width="1183" height="518" class="aligncenter size-full wp-image-26815" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Excel5.png 1183w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Excel5-300x131.png 300w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Excel5-1024x448.png 1024w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Excel5-768x336.png 768w" sizes="(max-width: 1183px) 100vw, 1183px" /></p>

			</div><!-- post-content-slide -->
		

			<div class="slide post-content-slide">
				
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Excel6.png" alt="Excel Pareto" width="1183" height="518" class="aligncenter size-full wp-image-26816" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Excel6.png 1183w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Excel6-300x131.png 300w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Excel6-1024x448.png 1024w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Excel6-768x336.png 768w" sizes="(max-width: 1183px) 100vw, 1183px" /></p>

			</div><!-- post-content-slide -->
		


					<div class="slider-nav-wrapper">
						<ul class="tie-slider-nav"></ul>
					</div>
				</div><!-- tie-slick-slider -->
			</div><!-- post-content-slideshow -->
		</div><!-- post-content-slideshow-outer -->
	
<p>A diferencia de otras versiones, desde <em>Microsoft Excel 2016</em> podemos acceder a este tipo de representación gráfica con suma facilidad:</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Excel7.png" alt="Diagrama de Pareto Excel" width="611" height="370" class="aligncenter size-full wp-image-26817" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Excel7.png 611w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Excel7-300x182.png 300w" sizes="(max-width: 611px) 100vw, 611px" /></p>

		<div class="box download  ">
			<div class="box-inner-block">
				<span class="fa tie-shortcode-boxicon"></span>Descargar la plantilla de Excel: <a href="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Diagrama-de-Pareto-Excel.xlsx"><strong>Diagrama de Pareto</strong></a>
			</div>
		</div>
	
<hr />
<h2>Diagrama de Pareto en Python</h2>

		<div id="diagrama-de-pareto-en-python" data-title="Diagrama de Pareto en Python" class="index-title"></div>
	
<p>Quienes prefieran utilizar algún lenguaje de programación, ya sea porque el diagrama que requieren acompañará un desarrollo o un modelo más robusto, integrará datos desde fuentes externas, o simplemente así lo prefieren; pueden utilizar Python para graficar su Pareto son suma facilidad. Explicaremos el código paso a paso, sin embargo, al finalizar, dejaremos el código completo para su disposición.</p>
<h4><em>Importar las librerías</em></h4>
<p>En este caso, requerimos del uso de dos librerías</p>

		<div class="checklist tie-list-shortcode">
<ul>
<li><strong>Pandas</strong>: Un paquete de Python que proporciona estructuras de datos rápidas, flexibles y expresivas diseñadas para que el trabajo con datos «relacionales» o «etiquetados» sea fácil e intuitivo.</li>
<li><strong>Matplotlib</strong>: Es una biblioteca completa para crear visualizaciones estáticas, animadas e interactivas en Python.</li>
</ul>

		</div>
	
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>import pandas as pd
import matplotlib.pyplot as plt</code></pre>
</div>
<h4><em>Crear la data de entrada y ordenarla</em></h4>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>df = pd.DataFrame({'frecuencia': [9, 5, 1, 1, 1, 36, 27, 2, 0, 0, 2, 4]})
df.index = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12']
df = df.sort_values(by='frecuencia',ascending=False)</code></pre>
</div>
<p>En la variable <em><strong>df </strong></em>quedará contenida la data de entrada en un <em>Dataframe</em>. Inicialmente agregaremos las frecuencias de acuerdo a nuestro ejemplo.</p>
<p>Posteriormente, por medio de <em><strong>df.index</strong></em> nombraremos las causas de acuerdo al orden de las frecuencias ya asignadas. Preferimos en este caso numerarlas en lugar de colocar el nombre de cada defecto. Por ejemplo:</p>
<p style="text-align: center;"><em>Burlete defectuoso = Causa 1 (Frecuencia 9)</em></p>
<p style="text-align: center;"><em>Pintura defectuosa = Causa 2 (Frecuencia 5)</em></p>
<p>La siguiente línea de código utiliza el método <em><strong>.sort_values</strong></em> para ordenar las frecuencias contenidas en <em><strong>df </strong></em>de forma descendente. El resultado parcial de <em><strong>df </strong></em>será:</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/python_pareto.png" alt="python_pareto" width="228" height="243" class="aligncenter size-full wp-image-26820" /></p>
<h4><em>Calcular la frecuencia relativa porcentual acumulada</em></h4>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>frecuencia_relativa = df["frecuencia"].cumsum()
total_defectos = df["frecuencia"].sum()
df["relativa_acum_porcentual"] = frecuencia_relativa/total_defectos*100
</code></pre>
</div>
<p>La frecuencia relativa se calcula con la función <strong><em>.cumsum</em>. </strong>Por eso seleccionamos dentro del <em>DataFrame <strong>df</strong> </em>la columna <em>frecuencia. </em>El número total de defectos se calcula con la función <em><strong>.sum </strong></em>sobre la columna frecuencia.</p>
<p>Para calcular la frecuencia relativa porcentual, dividimos la columna <em>frecuencia relativa </em>entre el <em>total de defectos</em> y se multiplica por <em>100</em>. Tal como se puede observar en la última línea del código. El resultado parcial de <em><strong>df </strong></em>será:</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/python_pareto-1.png" alt="" width="444" height="239" class="aligncenter size-full wp-image-26821" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/python_pareto-1.png 444w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/python_pareto-1-300x161.png 300w" sizes="(max-width: 444px) 100vw, 444px" /></p>
<p>&nbsp;</p>
<h4><em>Graficar el diagrama de Pareto</em></h4>
<p>El último paso corresponde a graficar:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>fig, ax = plt.subplots()
ax.bar(df.index, df["frecuencia"], color="C0")
ax2 = ax.twinx()
ax2.plot(df.index, df["cumpercentage"], color="C1", marker="o", ms=5)


plt.show() </code></pre>
</div>
<p>El resultado será:</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Pareto_Python.png" alt="Pareto_Python" width="394" height="248" class="aligncenter size-full wp-image-26822" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Pareto_Python.png 394w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/08/Pareto_Python-300x189.png 300w" sizes="(max-width: 394px) 100vw, 394px" /></p>
<hr />
<h2>¿Cómo ejecutar el modelo?
		<div id="como-ejecutar-el-modelo" data-title="¿Cómo ejecutar el modelo?" class="index-title"></div>
	</h2>
<p><em><strong>Alternativa 1, ejecución en nuestro equipo:</strong></em></p>
<p>Lo primero que debemos considerar, en el caso de que queramos ejecutar este código en nuestro equipo, es que es preciso contar con la instalación de <em>Python</em>.</p>
<p>Ahora, lo recomendable es trabajar con algún editor de código práctico (IDE), por ejemplo: <a href="https://www.sublimetext.com/3"><strong><em>Sublime Text</em></strong></a>, o <em><strong>Spyder</strong></em> (Una herramienta más completa y por ende más robusta y pesada).</p>
<p><em><strong>Alternativa 2, ejecución en un entorno virtual (Recomendado):</strong></em></p>
<p>Podemos utilizar del mismo modo, un entorno virtual. En este caso recomendamos el uso de <em><strong>Colaboratory de Google</strong></em>, un entorno que cuenta con todas las herramientas necesarias para nuestros desarrollos. No tendremos que instalar nada en nuestro equipo, y aprovecharemos la potencia de las máquinas de Google.</p>
<p>Puedes ver y ejecutar el cuaderno de este módulo en nuestro <em>Colaboratory: <a href="https://colab.research.google.com/drive/1ayGiC4HrISKZpPJ6a9qmDANeF3-xtrvQ?usp=sharing"><strong>Diagrama de Pareto en Python</strong></a>.</em></p>
<p>Código completo:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame({'frecuencia': [9, 5, 1, 1, 1, 36, 27, 2, 0, 0, 2, 4]})
df.index = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12']
df = df.sort_values(by='frecuencia',ascending=False)
frecuencia_relativa = df["frecuencia"].cumsum()
total_defectos = df["frecuencia"].sum()
df["cumpercentage"] = frecuencia_relativa/total_defectos*100


fig, ax = plt.subplots()
ax.bar(df.index, df["frecuencia"], color="C0")
ax2 = ax.twinx()
ax2.plot(df.index, df["cumpercentage"], color="C1", marker="o", ms=5)


plt.show()</code></pre>
</div>
<hr />
<p>&nbsp;</p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/gestion-y-control-de-calidad/como-hacer-un-diagrama-de-pareto-utilizando-excel-o-python/">¿Cómo hacer un diagrama de Pareto utilizando Excel o Python?</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ingenieriaindustrialonline.com/gestion-y-control-de-calidad/como-hacer-un-diagrama-de-pareto-utilizando-excel-o-python/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Problema de la ruta más corta en Google OR-Tools</title>
		<link>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problema-de-la-ruta-mas-corta-en-google-or-tools/</link>
					<comments>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problema-de-la-ruta-mas-corta-en-google-or-tools/#respond</comments>
		
		<dc:creator><![CDATA[Bryan Salazar López]]></dc:creator>
		<pubDate>Thu, 15 Apr 2021 21:44:18 +0000</pubDate>
				<category><![CDATA[Investigación de operaciones]]></category>
		<category><![CDATA[Google OR-Tools]]></category>
		<category><![CDATA[Investigación de Operaciones]]></category>
		<category><![CDATA[La ruta más corta]]></category>
		<category><![CDATA[Microsoft Excel]]></category>
		<category><![CDATA[Problema de flujo de costo mínimo]]></category>
		<category><![CDATA[Problemas de flujos de red]]></category>
		<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://ingenieriaindustrialonline.com/?p=25407</guid>

					<description><![CDATA[<p>Podemos decir que el problema o algoritmo de la ruta más corta es una popularización del problema del flujo del costo mínimo, una variación de los modelos generales de flujos. Cuando nos referimos al costo mínimo, este en realidad puede expresarse en diversas magnitudes: distancia, tiempo, volumen, y en general, cual cualquier unidad que represente el caso &#8230;</p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problema-de-la-ruta-mas-corta-en-google-or-tools/">Problema de la ruta más corta en Google OR-Tools</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></description>
										<content:encoded><![CDATA[
		<div id="introduccion" data-title="Introducción" class="index-title"></div>
	
<p>Podemos decir que el problema o algoritmo de la ruta más corta es una popularización del problema del flujo del costo mínimo, una variación de los modelos generales de flujos. Cuando nos referimos al <em><strong>costo mínimo</strong></em>, este en realidad puede expresarse en diversas magnitudes: distancia, tiempo, volumen, y en general, cual cualquier unidad que represente el caso de estudio.</p>
<p>En el problema del flujo de costo mínimo, cada arco de la red tiene un costo asociado a transportar unidades a través de él, es decir, por ejemplo, que el arco que une a los nodos 0 y 1, tiene un costo subyacente a su transporte, en los términos que representen mejor al modelo. Además del costo, cada arco debe considerar una capacidad de transporte.</p>
<p>Del mismo modo, debe considerarse que en este problema los nodos tienen una naturaleza, y existen algunos nodos especiales:</p>

		<div class="checklist tie-list-shortcode">
<ul>
<li>nodos de oferta: puntos desde los cuales se suministran unidades de flujo (fuentes)</li>
<li>nodos de demanda: puntos hacia los cuales se llevan unidades de flujo para su consumo, sumidero o hundimiento (destinos)</li>
<li>nodos neutrales: puntos a través de los cuales pasan unidades de flujo, no genera nuevas unidades, no consume unidades, son puntos de tránsito.</li>
</ul>

		</div>
	
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Tipos-de-nodo.png" alt="Ejemplo de tipos de nodos" width="606" height="427" class="aligncenter size-full wp-image-25409" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Tipos-de-nodo.png 606w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Tipos-de-nodo-300x211.png 300w" sizes="(max-width: 606px) 100vw, 606px" /></p>
<p>En el anterior gráfico se puede apreciar una representación básica de lo que sería la naturaleza de los nodos aplicada a un caso común: una planta que produce material, lo envía hacia algunos centros de distribución o consolidación y un cliente, lugar que representa el destino final del material. Claramente pueden apreciarse cuales son los nodos de oferta, de demanda y de tránsito. Ahora bien, es posible que en la práctica existan múltiples nodos de oferta y múltiples nodos de demanda, del mismo modo, es posible que los nodos de tránsito también suministren o consuman unidades, lo que los convertiría en nodos mixtos, y lo que podría representar múltiples casos prácticos, por ejemplo: un centro de distribución ubicado en una planta, el cual recibe unidades para consumo interno y despacha excedentes hacia otros centros o clientes.</p>
<h2>Problema de la ruta más corta
		<div id="problema-de-la-ruta-mas-corta" data-title="Problema de la ruta más corta" class="index-title"></div>
	</h2>
<p>Tal como se mencionó en la introducción del artículo, el problema de la ruta más corta puede abordarse desde la perspectiva del problema del flujo de costo mínimo, en el cual el objetivo consiste en determinar el plan de rutas que genere la trayectoria con la mínima distancia total, que una un <em>nodo fuente (puro)</em> con un <em>nodo destino (puro)</em>, sin importar el número de nodos que existan entre estos.</p>
<p>Así entonces, en su versión más básica, el flujo y la capacidad de los arcos puede reducirse a la unidad (1), asumiendo que no se transportan materiales y que el único objetivo que se persigue consiste en determinar la ruta más corta que une a un nodo fuente con un nodo destino. Dicho de otro modo, el nodo fuente produce una unidad que es transportada por medio de arcos con capacidad de unidad y se consume en la medida de una unidad en el nodo destino.</p>
<p>Es preciso reiterar que cuando nos referimos distancia nos ajustamos al nombre del algoritmo «la ruta más corta», sin embargo, lo que se considera distancia, bien puede expresarse en otras unidades de medida, por ejemplo: costo.</p>
<hr />
<p><span>El objetivo de este artículo consiste en utilizar las librerías del software Google OR-Tools para abordar el problema de la ruta más corta a través de una interfaz base del algoritmo del flujo de costo mínimo. Posteriormente, abordaremos un <em>script </em>básico en <em>Python</em> que nos permita integrar al modelo de optimización, data de entrada proveniente de fuentes como un documento de Excel.</span></p>
<h2>El problema
		<div id="el-problema" data-title="El problema" class="index-title"></div>
	</h2>
<p>Con el propósito de evaluar los resultados obtenidos mediante distintos métodos y solucionadores, utilizaremos el mismo problema que abordamos mediante <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/algoritmo-de-la-ruta-mas-corta/"><b>programación lineal </b>y el módulo de <em>network modeling</em> de WinQSB.</a></p>
<blockquote class=" quote-simple "><p>Un minero ha quedado atrapado en una mina, la entrada a la mina se encuentra ubicada en el nodo 0, se conoce de antemano que el minero permanece atrapado en el nodo 8, para llegar a dicho nodo hay que atravesar una red de túneles que van conectados entre sí. El tiempo de vida que le queda al minero sin recibir auxilio es cada vez menor y se hace indispensable hallar la ruta de acceso al nodo 8 más corta. Las distancias entre nodos de la mina se encuentran en la siguiente gráfica dadas en cientos de metros:</p></blockquote>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Ruta_corta_minero.png" alt="Ruta_corta_minero" width="662" height="287" class="aligncenter size-full wp-image-25411" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Ruta_corta_minero.png 662w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Ruta_corta_minero-300x130.png 300w" sizes="(max-width: 662px) 100vw, 662px" /></p>
<h2>Resolviendo un problema de la ruta más corta mediante Google OR-Tools
		<div id="resolviendo-un-problema-de-la-ruta-mas-corta-mediante-google-or-tools" data-title="Resolviendo un problema de la ruta más corta mediante Google OR-Tools" class="index-title"></div>
	</h2>
<p>De acuerdo a lo mencionado en el artículo de <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/que-es-y-para-que-sirve-google-or-tools/"><strong>introducción a Google OR-Tools</strong></a>, esta herramienta soporta múltiples lenguajes de programación, así entonces, haremos uso del lenguaje de programación Python.</p>
<p>Es posible que para la ejecución de este <em>script, </em>debas instalar el comando <em>future</em>, el cual puedes encontrar en el siguiente enlace: <em><strong><a href="https://pypi.org/project/future/">future 0.18.2</a>. </strong></em>La instalación es muy simple, tan solo escribir en el <em>cmd (símbolo del sistema) lo siguiente:</em></p>
<p style="text-align: center;"><strong>pip install future</strong></p>
<h3><em>Paso 1: Importar la librería
		<div id="paso-1-importar-la-libreria" data-title="Paso 1: Importar la librería" class="index-title"></div>
	</em></h3>
<p>El siguiente fragmento de código importa las librerías necesarias:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code># Importar la librería de Google OR-Tools
<span class="pln">from __future__ import print_function
from ortools.graph import pywrapgraph</span></code></pre>
</div>
<h3><em>Paso 2: Crear la data del modelo
		<div id="paso-2-crear-la-data-del-modelo" data-title="Paso 2: Crear la data del modelo" class="index-title"></div>
	</em></h3>
<p>Define cuatro matrices paralelas: nodos_fuente, nodos_destino, capacidades, y costos_unitarios, entre cada par. Por ejemplo, el arco desde el nodo 0 hacia el nodo 1 tiene una capacidad de 1 y un costo asociado de 4 (distancia). En su versión más básica el objetivo consiste en unir un nodo fuente y un nodo destino; por esta razón, en este ejemplo la capacidad de todos los nodos equivale a 1 y el flujo es binario, donde tomará valores de 1 en el caso en que el arco forme parte del conjunto solución y 0 en el caso contrario:</p>
<p>Pueden observarse las matrices perfectamente alineadas, ya que el orden es importante. El orden entre la matriz de nodos_fuente y nodos_destino definirá el valor de los arcos del modelo (distancia).</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln">nodos_fuente  = [ 0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6, 7, 7]
nodos_destino = [ 1, 2, 2, 3, 1, 3, 4, 5, 6, 3, 6, 7, 6, 8, 5, 7, 8, 6, 8]
capacidades   = [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
distancia     = [ 4, 2, 2, 7, 4, 9, 6, 1, 5, 2, 3, 2, 1, 5, 4, 3, 6, 2, 6]

suministros = [1, 0, 0, 0, 0, 0, 0, 0, -1]</span></code></pre>
</div>
<p>También definimos la matriz de suministros asociada a los nodos, donde los valores positivos corresponde a oferta y los valores positivos corresponde a demanda. Los ceros se asocian a los nodos de tránsito. En nuestro ejemplo solo existe un nodo de oferta = nodo 0, y un nodo de demanda = nodo 8.</p>
<h3><em>Paso 3: Declarar el solucionador y agregar los arcos del modelo
		<div id="paso-3-declarar-el-solucionador-y-agregar-los-arcos-del-modelo" data-title="Paso 3: Declarar el solucionador y agregar los arcos del modelo" class="index-title"></div>
	</em></h3>
<p>El siguiente fragmento define en primera instancia el solucionador (declara). Luego, define cada arco del problema, es decir, con base en las matrices definidas como datos de entrada (en su orden), establece el valor (costo = distancia) de cada arco. Posteriormente, define los suministros (o demandas) para cada nodo.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln"># Crea una instancia para el solucionador
min_cost_flow = pywrapgraph.SimpleMinCostFlow()

# Define cada arco del problema
for i in range(0, len(nodos_fuente)):
  min_cost_flow.AddArcWithCapacityAndUnitCost(nodos_fuente[i], nodos_destino[i],
                                              capacidades[i], distancia[i])

# Define los suministros para cada nodo.
for i in range(0, len(suministros)):
  min_cost_flow.SetNodeSupply(i, suministros[i])
</span></code></pre>
</div>
<h3><em>Paso 4: Invocar al solucionador y definir la información de salida del modelo
		<div id="paso-4-invocar-al-solucionador-y-definir-la-informacion-de-salida-del-modelo" data-title="Paso 4: Invocar al solucionador y definir la información de salida del modelo" class="index-title"></div>
	</em></h3>
<p>El siguiente fragmento de código utiliza las librerías predeterminadas de Google OR-Tools para abordar problemas de flujo de costo mínimo. Así mismo, se especifican las salidas del solucionador: arco / flujo / capacidad  / distancia, para cada arco; y distancia total de la red.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln"># Encuentra el costo mínimo entre el nodo 0 y el nodo 8
if min_cost_flow.Solve() == min_cost_flow.OPTIMAL:
  print('Distancia mínima:', min_cost_flow.OptimalCost())
  print('')
  print(' Arco Flujo / Capacidad Distancia')
  for i in range(min_cost_flow.NumArcs()):
    cost = min_cost_flow.Flow(i) * min_cost_flow.UnitCost(i)
    print('%1s -&gt; %1s %3s / %3s %3s' % (
        min_cost_flow.Tail(i),
        min_cost_flow.Head(i),
        min_cost_flow.Flow(i),
        min_cost_flow.Capacity(i),
        cost))
else:
  print('Hubo un problema con la entrada de flujo de distancia mínima.')
</span></code></pre>
</div>
<hr />
<p>Es posible que el desarrollo de los cuatro pasos anteriores demande algún grado de complejidad subyacente del uso de un lenguaje de programación; sin embargo, es preciso mencionar que, el modelo anterior queda perfectamente configurado, y puede replicarse con modificaciones menores en múltiples problemas de asignación.</p>
<h2>¿Cómo ejecutar el modelo?
		<div id="como-ejecutar-el-modelo" data-title="¿Cómo ejecutar el modelo?" class="index-title"></div>
	</h2>
<p><em><strong>Alternativa 1, ejecución en nuestro equipo:</strong></em></p>
<p>Lo primero que debemos considerar, en el caso de que queramos ejecutar este código en nuestro equipo, es que es preciso contar con la instalación de <em>Python </em>en nuestro equipo de cómputo, así mismo debemos contar con la última versión del comando <em>pip</em> y por supuesto, el software <em>OR-Tools.</em> Una guía detallada de la instalación de estos requerimientos la podrás encontrar en el siguiente enlace:</p>
<p style="text-align: center;"><a href="https://developers.google.com/optimization/install/python/windows" target="_blank" class="shortc-button medium blue">Instalación de OR-Tools para Python</a>
<p>Ahora, lo recomendable es trabajar con algún editor de código práctico (IDE), por ejemplo: <a href="https://www.sublimetext.com/3"><strong><em>Sublime Text</em></strong></a>, o <em><strong>Spyder</strong></em> (Una herramienta más completa y por ende más robusta y pesada).</p>
<p><em><strong>Alternativa 2, ejecución en un entorno virtual (Recomendado):</strong></em></p>
<p>Podemos utilizar del mismo modo, un entorno virtual. En este caso recomendamos el uso de <em><strong>Colaboratory de Google</strong></em>, un entorno que cuenta con todas las herramientas necesarias para nuestros desarrollos. No tendremos que instalar nada en nuestro equipo, y aprovecharemos la potencia de las máquinas de Google.</p>
<hr />
<p>El código completo de nuestro desarrollo lo presentamos a continuación. También puedes ver el cuaderno de este módulo en nuestro <em>Colaboratory: <a href="https://colab.research.google.com/drive/1EcLAl1bjQU9-j35-6TjMsTPWe_j1ykg-?usp=sharing"><strong>Ruta más corta</strong></a>.</em></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln">#Desde: Salazar, ingenieriaindustrialonline.com - Algoritmo de la ruta más corta

from __future__ import print_function
from ortools.graph import pywrapgraph

def main():
  """MinCostFlow adaptado a la ruta más corta - interfaz de ejemplo."""

  # Define cuatro matrices paralelas: nodos_fuente, nodos_destino, 
  # capacidades, y costos_unitarios entre cada par.

  nodos_fuente      = [ 0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6, 7, 7]
  nodos_destino     = [ 1, 2, 2, 3, 1, 3, 4, 5, 6, 3, 6, 7, 6, 8, 5, 7, 8, 6, 8]
  capacidades       = [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
  distancia         = [ 4, 2, 2, 7, 4, 9, 6, 1, 5, 2, 3, 2, 1, 5, 4, 3, 6, 2, 6]

  # Define una matriz con los suministros de cada nodo (valores positivos = 
  # suministros) y (valores negativos = demandas)

  suministros = [1, 0, 0, 0, 0, 0, 0, 0, -1]


  # Crea una instancia para el solucionador
  min_cost_flow = pywrapgraph.SimpleMinCostFlow()

  # Define cada arco del problema
  for i in range(0, len(nodos_fuente)):
    min_cost_flow.AddArcWithCapacityAndUnitCost(nodos_fuente[i], nodos_destino[i],
                                                capacidades[i], distancia[i])

  # Define los suministros para cada nodo.

  for i in range(0, len(suministros)):
    min_cost_flow.SetNodeSupply(i, suministros[i])


  # Encuentra el costo mínimo entre el nodo 0 y el nodo 8
  if min_cost_flow.Solve() == min_cost_flow.OPTIMAL:
    print('Distancia mínima:', min_cost_flow.OptimalCost())
    print('')
    print('  Arco    Flujo / Capacidad  Distancia')
    for i in range(min_cost_flow.NumArcs()):
      cost = min_cost_flow.Flow(i) * min_cost_flow.UnitCost(i)
      print('%1s -&gt; %1s    %3s   / %3s       %3s' % (
          min_cost_flow.Tail(i),
          min_cost_flow.Head(i),
          min_cost_flow.Flow(i),
          min_cost_flow.Capacity(i),
          cost))
  else:
    print('Hubo un problema con la entrada de flujo de distancia mínima.')

if __name__ == '__main__':
  main()
</span></code></pre>
</div>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/ruta_mas_corta_solucion.png" alt="ruta_mas_corta_solucion" width="611" height="397" class="aligncenter size-full wp-image-26354" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/ruta_mas_corta_solucion.png 611w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/ruta_mas_corta_solucion-300x195.png 300w" sizes="(max-width: 611px) 100vw, 611px" /></p>
<p>Y bien, tenemos la solución a este problema simple de asignación en menos de 1 segundo.</p>
<hr />
<p>Podemos observar que se ha obtenido la misma respuesta que la lograda mediante <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/algoritmo-de-la-ruta-mas-corta/"><b>programación lineal </b>y el módulo de <em>network modeling </em> de WinQSB</a>:</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Ruta_corta_minero_rojo.png" alt="Ruta_corta_minero_rojo" width="662" height="287" class="aligncenter size-full wp-image-25413" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Ruta_corta_minero_rojo.png 662w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Ruta_corta_minero_rojo-300x130.png 300w" sizes="(max-width: 662px) 100vw, 662px" /></p>
<hr />
<h2>Problema de la ruta más corta mediante Google OR-Tools, importando los datos desde Excel
		<div id="problema-de-la-ruta-mas-corta-mediante-google-or-tools-importando-los-datos-desde-excel" data-title="Problema de la ruta más corta mediante Google OR-Tools, importando los datos desde Excel" class="index-title"></div>
	</h2>
<p>Tal como lo planteamos al inicio del artículo, <span>abordaremos un <em>script </em>básico en <em>Python</em> que nos permita integrar al modelo de optimización, data de entrada proveniente de fuentes como un documento de Excel. Esto con el propósito de introducirnos en las bondades del modelamiento en lenguajes de programación.</span></p>
<h3><em>Paso 1: Construir una base de datos en Microsoft Excel
		<div id="paso-1-construir-una-base-de-datos-en-microsoft-excel" data-title="Paso 1: Construir una base de datos en Microsoft Excel" class="index-title"></div>
	</em></h3>
<p>En este caso, utilizaremos una hoja de cálculo haciendo uso de Microsoft Excel, en su extensión predeterminada <em>xlsx</em>, desde la cual, construiremos una base de datos que consignará toda la información de entrada relacionada con el modelo. Un consejo práctico, es que este archivo se guarde dentro del mismo directorio en el que se encuentra el modelo desarrollado en <em>Python.</em></p>
<figure id="attachment_25416" aria-describedby="caption-attachment-25416" style="width: 507px" class="wp-caption aligncenter"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/datos_excel_hoja1.png" alt="datos_excel_hoja1" width="507" height="638" class="wp-image-25416 size-full" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/datos_excel_hoja1.png 507w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/datos_excel_hoja1-238x300.png 238w" sizes="(max-width: 507px) 100vw, 507px" /><figcaption id="caption-attachment-25416" class="wp-caption-text">Hoja 1</figcaption></figure>
<p>Se puede apreciar la forma en la cual hemos consignado la información, desde luego, respetando el orden de los datos, ya que, tal como lo mencionamos, es muy importante, ya que de ello depende la asociación de cada arco con su distancia. La anterior información la hemos consignado en la <em>Hoja 1</em> del archivo, ya que en la <em>Hoja 2</em> consignaremos la información relacionada con el suministros (con el objetivo práctico de mostrar cómo se importan datos desde hojas específicas), de esta manera:</p>
<figure id="attachment_25417" aria-describedby="caption-attachment-25417" style="width: 390px" class="wp-caption aligncenter"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/datos_excel_hoja2.png" alt="datos_excel_hoja2" width="390" height="441" class="wp-image-25417 size-full" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/datos_excel_hoja2.png 390w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/datos_excel_hoja2-265x300.png 265w" sizes="(max-width: 390px) 100vw, 390px" /><figcaption id="caption-attachment-25417" class="wp-caption-text">Hoja 2</figcaption></figure>
<p>Del mismo modo, el orden de los suministros es muy importante, razón por la cual hemos construido una columna denominada <em><strong>nodos</strong></em> que puede servir como guía para consignar la información de los suministros de forma ordenada.</p>
<p>Es de vital importancia, nombrar cada columna y tener claridad sobre ello, desaconsejamos el uso de caracteres especiales y sugerimos utilizar nombres cortos relacionados con la información contenida.</p>
<p>Posterior a consignar la información, podemos guardar el documento, en este caso lo hemos nombrado: <em>data_flujo. </em>Este dato es importante, ya que lo utilizaremos desde el <em>script.</em></p>
<h3><em>Paso 2: Importar la librería Pandas
		<div id="paso-2-importar-la-libreria-pandas" data-title="Paso 2: Importar la librería Pandas" class="index-title"></div>
	</em></h3>

		<div class="box info  ">
			<div class="box-inner-block">
				<span class="fa tie-shortcode-boxicon"></span>Pandas es un paquete de Python que proporciona estructuras de datos rápidas, flexibles y expresivas diseñadas para que el trabajo con datos estructurados (tabulares, multidimensionales, potencialmente heterogéneos) y de series de tiempo sea fácil e intuitivo. (Fuente: https://pypi.org/project/pandas/)
			</div>
		</div>
	
<p>En caso de requerir la instalación de la librería, tan solo debes escribir el siguiente comando en el símbolo del sistema:</p>
<p style="text-align: center;"><span>pip install pandas</span></p>
<p>Esta paquete nos proporcionará la posibilidad de importar y trabajar con datos de fuentes como Microsoft Excel. El siguiente fragmento de código se adicionará al <em>script </em>e importará la librería:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code># Importar la librería de Google OR-Tools
# Importar la librería de Pandas
<span class="pln">from __future__ import print_function
from ortools.graph import pywrapgraph
import pandas as pd</span></code></pre>
</div>
<p>Una vez que importemos la librería, podemos trabajar con sus funciones.</p>
<h3><em>Paso 3: Importar datos desde Microsoft Excel
		<div id="paso-3-importar-datos-desde-microsoft-excel" data-title="Paso 3: Importar datos desde Microsoft Excel" class="index-title"></div>
	</em></h3>
<p>El desarrollo para importar la información desde Excel debe estar correctamente ordenada (correspondencia entre filas). Por ejemplo:</p>
<table border="0" cellpadding="0" cellspacing="0" width="400" style="border-collapse: collapse; width: 300pt;" class=" aligncenter">
<tbody>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl63" width="80" style="height: 15pt; width: 60pt; text-align: center;"><strong>fila</strong></td>
<td class="xl63" width="80" style="width: 60pt; text-align: center;"><strong>fuentes</strong></td>
<td class="xl63" width="80" style="width: 60pt; text-align: center;"><strong>destinos</strong></td>
<td class="xl63" width="80" style="width: 60pt; text-align: center;"><strong>capacidad</strong></td>
<td class="xl63" width="80" style="width: 60pt; text-align: center;"><strong>distancia</strong></td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl63" style="height: 15pt; text-align: center;">1</td>
<td class="xl63" style="text-align: center;">0</td>
<td class="xl63" style="text-align: center;">1</td>
<td class="xl63" style="text-align: center;">1</td>
<td class="xl63" style="text-align: center;">4</td>
</tr>
</tbody>
</table>
<p>La anterior fila representa el arco entre el nodo <em><strong>0 </strong></em>y el nodo <em><strong>1</strong></em> cuya capacidad es de <em><strong>1 </strong></em>y de dimensión (distancia) <em><strong>4</strong></em>.</p>
<p>En el caso de los suministros, debe considerarse el orden la columna en Excel. Por ejemplo:</p>
<table border="0" cellpadding="0" cellspacing="0" width="240" style="border-collapse: collapse; width: 180pt;" class=" aligncenter">
<tbody>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl66" width="80" style="height: 15.0pt; width: 60pt;"><strong>fila</strong></td>
<td class="xl66" width="80" style="width: 60pt;"><strong>nodos</strong></td>
<td class="xl66" width="80" style="width: 60pt;"><strong>suministros</strong></td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15.0pt;">1</td>
<td class="xl65">0</td>
<td class="xl65">1</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15pt;">5</td>
<td class="xl65">4</td>
<td class="xl65">0</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15pt;">9</td>
<td class="xl65">8</td>
<td class="xl65">-1</td>
</tr>
</tbody>
</table>
<p>En este caso, el nodo <em><strong>0 </strong></em>será de oferta, es decir, desde ahí se crea flujo; el nodo <em><strong>8</strong></em> será de demanda, es decir, en este nodo se consume (sumidero). El nodo <em><strong>4</strong></em> será un nodo de tránsito (no crea inicia flujo ni lo consume), solo es un nodo de paso.</p>
<p>El siguiente fragmento de código importará la data que se encuentra consignada en el documento de Excel:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def create_data():

    excel = pd.read_excel('data_flujo.xlsx')
    excel_1 = pd.read_excel('data_flujo.xlsx', sheet_name=1)

    data = {}

    data['fuentes'] = excel['fuentes'].tolist() 
    data['destinos'] = excel['destinos'].tolist() 
    data['capacidad'] = excel['capacidad'].tolist() 
    data['distancias'] = excel['distancia'].tolist() 
    data['suministro'] = excel_1['suministros'].tolist() 

    return data </code></pre>
</div>
<p>Los datos de entrada del modelo los trataremos dentro de una función <em>create_data</em>, desde ahí utilizaremos algunas funciones de la librería <em>Pandas (pd)</em> para disponer correctamente de la información contenida en el documento de Excel. Veamos cómo:</p>
<p style="text-align: center;"><strong>excel</strong> = pd.<strong>read_excel</strong>(&#8216;data_flujo.xlsx&#8217;)</p>
<p>En este caso, creamos la variable <em><strong>excel</strong></em> y dentro de ella utilizamos la función <em><strong>read_excel</strong></em> la cual permite leer el documento de Excel (en nuestro caso <em>data_flujo.xlsx</em>), e importarlo (en la variable <em>excel</em>) en formato <em>dataframe</em> (conjunto de columnas).</p>
<p>También creamos la variable <em><strong>excel_1</strong></em><strong> </strong>y dentro de ella utilizamos la función <em><strong>read_excel</strong></em> la cual permite leer el documento de Excel (en nuestro caso <em>data_flujo.xlsx</em>), e importarlo (en la variable <em>excel</em>). En este caso adicionamos el argumento <em>sheet_name</em>, el cual nos permite dentro del documento buscar la información en una hoja específica. Las hojas de Excel, en el caso de Python, se denominan desde el índice 0. Es decir que en nuestro caso, al expresar <em>sheet_name=1</em> indicamos que lea la <em>Hoja 2</em> del documento.</p>
<p>Creamos el directorio <em><strong>data</strong>, </em>temporalmente vacío, en el se consignarán posteriormente cada lista de datos junto a su nombre (índice).</p>
<p>Ahora detallaremos cómo creamos cada listado de datos:</p>
<p style="text-align: center;"><strong>data</strong>[&#8216;fuentes&#8217;] = <strong>excel</strong>[&#8216;fuentes&#8217;].<strong>tolist</strong>()</p>
<p>En este caso <strong>data</strong>[&#8216;fuentes&#8217;] indica que crearemos el índice &#8216;fuentes&#8217; dentro del directorio <strong>data </strong>(que se encontraba vacío). <strong>excel</strong>[&#8216;fuentes&#8217;] indica que dentro del <em>dataframe</em> Excel, queremos obtener la columna &#8216;fuentes&#8217;. Y la función <strong>tolist</strong>() convertirá esta columna en una lista (formato <em>list</em>). En defintiva, dentro de <strong>data</strong>[&#8216;fuentes&#8217;] tendremos el listado de fuentes obtenido desde Excel.</p>
<p>Este mismo procedimiento lo repetimos para los datos restantes. Al finalizar, dentro del directorio <strong>data</strong> quedará contenida toda la información de entrada del modelo. Como la función <em>create_data</em> retorna la variable <em>data</em>, esto quiere decir, que toda la información de entrada quedará contenida dentro de la función <em>create_data</em>, esto permitirá su uso posterior.</p>
<h3><em>Paso 4: Invocar la data del modelo en el main
		<div id="paso-4-invocar-la-data-del-modelo-en-el-main" data-title="Paso 4: Invocar la data del modelo en el main" class="index-title"></div>
	</em></h3>
<p>Para utilizar los listados o inputs obtenidos desde Excel, necesitamos invocar la función «<em>create_data</em>» esta retornará el directorio con todos los datos.</p>
<p>Recomendamos utilizar el mismo nombre «data» para crear el directorio dentro de esta función:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln">data = create_data()
</span></code></pre>
</div>
<p>Ahora el «data» de esta función (main), contiene el directorio de la función <em>«create_data</em>«. Ya podemos usar los listados.</p>
<p>Quiere decir esto, que para acceder específicamente al listado que contiene los destinos, por ejemplo, es necesario invocar a data[&#8216;destinos&#8217;].</p>
<hr />
<p>Básicamente, estas son las modificaciones que deben realizarse sobre el modelo inicialmente desarrollado. De esta forma quedará nuestro código completo, el cual importará la data de entrada desde un archivo de Excel:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln"># """From Salazar, ingenieriaindustrialonline.com - Algoritmo de la ruta más corta"""

from __future__ import print_function
from ortools.graph import pywrapgraph #Librería de Google Or Tools
import pandas as pd #Librería pandas para obtener data desde Excel

"""MinCostFlow adaptado a la ruta más corta - interfaz de ejemplo."""

#Este desarrollo requiere que la información extraida desde Excel esté ordenada
#de acuerdo a sus filas. Por ejemplo:

#fila     fuentes   destinos    capacidad   distancia
# 1          0          1           1           4

#Esta fila representa el arco de capacidad 1 y distancia 4 que conecta el nodo fuente 0
#al nodo destino 1

#En el caso de los suministros, debe considerarse el orden de la columna de Excel. Por ejemplo:

#fila      nodos   suministros
# 1          0          1
# 5          4          0
# 9          8         -1

#En este caso, el nodo 0 será de oferta (desde ahí se crea flujo) y el nodo 8 será de demanda (ahí se consume).
#El nodo 4 será un nodo de tránsito (no crea flujo y no consume), solo es un nodo de paso.


#Creamos la data del modelo (La extraemos desde Excel)
def create_data():

    #La variable "excel" traerá la información contenida en el archivo "data_flujo.xlsx" (crea un dataframe organizado en columnas)
    excel = pd.read_excel('data_flujo.xlsx')
    excel_1 = pd.read_excel('data_flujo.xlsx', sheet_name=1) #Función que permite leer una hoja en específico (hoja 2 de Excel - Inicia desde 0)

    data = {} #Crea un directorio llamado data, en él agregaremos cada lista de datos junto con su nombre (índice)

    #La información contenida en Excel viene dada en un dataframe con todos los datos en columnas.
    #A continuación, extraeremos cada columna en específico, desde el dataframe (todas las columnas) &gt; series (columna en específico)
    #Luego, el "tolist" convertirá cada serie en una lista. Esa lista se guardará en el directorio "data" y se etiqueta con el índice correspondiente

    data['fuentes'] = excel['fuentes'].tolist() #Columna en Excel = 'fuentes' &gt; lista en el directorio "data" con la etiqueta (índice) "fuentes"
    data['destinos'] = excel['destinos'].tolist() #Columna en Excel = 'destinos' &gt; lista en el directorio "data" con la etiqueta (índice) "destinos"
    data['capacidad'] = excel['capacidad'].tolist() #Columna en Excel = 'capacidad' &gt; lista en el directorio "data" con la etiqueta (índice) "capacidad"
    data['distancias'] = excel['distancia'].tolist() #Columna en Excel = 'distancia' &gt; lista en el directorio "data" con la etiqueta (índice) "distancia"
    data['suministro'] = excel_1['suministros'].tolist() #Columna en Excel = 'suministro' &gt; lista en el directorio "data" con la etiqueta (índice) "suministro"

    #Quiere decir esto, que para acceder específicamente al listado que contiene los destinos, es necesario invocar a data['destinos'].

    return data #En "create_data" quedará contenido el directorio "data" el cuál contiene todas las listas con la información del modelo.


def main():

  #Para utilizar los listados o inputs obtenidos desde Excel, necesitamos invocar la función "create_data" esta retornará el directorio con todos los datos.
  #Recomendamos utilizar el mismo nombre "data" para crear el directorio dentro de esta función:

  data = create_data() #Ahora el "data" de esta función, contiene el directorio de la función "create_data". Ya podemos usar los listados.

  # Crea una instancia para el solucionador
  min_cost_flow = pywrapgraph.SimpleMinCostFlow()

  # Define cada arco del problema
  for i in range(0, len(data['fuentes'])):
    min_cost_flow.AddArcWithCapacityAndUnitCost(data['fuentes'][i], data['destinos'][i],
                                                data['capacidad'][i], data['distancias'][i])

  # Define los suministros para cada nodo.
  for i in range(0, len(data['suministro'])):
    min_cost_flow.SetNodeSupply(i, data['suministro'][i])


  # Encuentra el costo mínimo entre el nodo 0 y el nodo 8
  if min_cost_flow.Solve() == min_cost_flow.OPTIMAL:
    print('Distancia mínima:', min_cost_flow.OptimalCost())
    print('')
    print('  Arco    Flujo / Capacidad  Distancia')
    for i in range(min_cost_flow.NumArcs()):
      cost = min_cost_flow.Flow(i) * min_cost_flow.UnitCost(i)
      print('%1s -&gt; %1s    %3s   / %3s       %3s' % (
          min_cost_flow.Tail(i),
          min_cost_flow.Head(i),
          min_cost_flow.Flow(i),
          min_cost_flow.Capacity(i),
          cost))
  else:
    print('Hubo un problema con la entrada de flujo de distancia mínima.')

if __name__ == '__main__':
  main()
</span></code></pre>
</div>
<p><em>Ejecutamos el modelo:</em></p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/ruta_mas_corta_solucion.png" alt="ruta_mas_corta_solucion" width="611" height="397" class="aligncenter size-full wp-image-26354" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/ruta_mas_corta_solucion.png 611w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/ruta_mas_corta_solucion-300x195.png 300w" sizes="(max-width: 611px) 100vw, 611px" /></p>
<hr />
<p>De esta manera hemos logrado integrar una base de datos que se encuentra en un archivo de Excel, el cual podemos modificar en cualquier momento con suma facilidad; un modelo de optimización flexible basado en el algoritmo de flujos de costo mínimo y un solucionador potente. Así entonces, podemos con suma eficiencia, modelar problemas relacionados con el <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/algoritmo-de-la-ruta-mas-corta/"><em><strong>Algoritmo de la Ruta más corta</strong></em></a>.</p>
<p>En próximos artículos abordaremos algunos <em>scripts </em>básicos en <em>Python </em>que nos permitan exportar los resultados del solucionador en algún formato específico, y de acuerdo a una estructura definida.</p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problema-de-la-ruta-mas-corta-en-google-or-tools/">Problema de la ruta más corta en Google OR-Tools</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problema-de-la-ruta-mas-corta-en-google-or-tools/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Planeación agregada mediante programación lineal</title>
		<link>https://ingenieriaindustrialonline.com/produccion/planeacion-agregada-mediante-programacion-lineal/</link>
					<comments>https://ingenieriaindustrialonline.com/produccion/planeacion-agregada-mediante-programacion-lineal/#comments</comments>
		
		<dc:creator><![CDATA[Bryan Salazar López]]></dc:creator>
		<pubDate>Sun, 16 Jun 2019 17:27:40 +0000</pubDate>
				<category><![CDATA[Investigación de operaciones]]></category>
		<category><![CDATA[Producción]]></category>
		<category><![CDATA[Google OR-Tools]]></category>
		<category><![CDATA[Microsoft Excel]]></category>
		<category><![CDATA[Planeación agregada]]></category>
		<category><![CDATA[Programación lineal]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Solver]]></category>
		<guid isPermaLink="false">http://contentlab.co/ingenieria/?p=1319</guid>

					<description><![CDATA[<p>Recordemos que la planeación agregada es un proceso utilizado para determinar una estrategia de forma anticipada que permita satisfacer los requerimientos (demanda) del sistema, al mismo tiempo que busca optimizar los recursos del mismo; cuyo desarrollo se lleva a cabo en el corto y mediano plazo. Variables y consideraciones A la hora de elaborar un plan agregado se &#8230;</p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/produccion/planeacion-agregada-mediante-programacion-lineal/">Planeación agregada mediante programación lineal</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Recordemos que la <a title="Planeación Agregada" href="https://ingenieriaindustrialonline.com/produccion/planeacion-agregada/" target="_blank" rel="noopener"><strong>planeación agregada</strong></a> es un proceso utilizado para determinar una estrategia de forma anticipada que permita satisfacer los requerimientos (demanda) del sistema, al mismo tiempo que busca optimizar los recursos del mismo; cuyo desarrollo se lleva a cabo en el corto y mediano plazo.</p>
<h2>Variables y consideraciones</h2>
<p>A la hora de elaborar un plan agregado se debe tener en cuenta que existen una serie de consideraciones que rigen la estrategia, ya sea por el horizonte de tiempo, por el criterio de las decisiones o por las restricciones que delimitan el sistema.</p>
<p><em>A continuación detallaremos estas consideraciones:</em></p>
<h3><em>Horizonte de tiempo</em></h3>
<p>Básicamente, la planeación agregada considera un horizonte de tiempo de corto y medio plazo, es decir que suele manejar un periodo entre 6 y 18 meses de planificación.</p>
<h3><em>Criterios de decisión</em></h3>
<p>El principal objetivo de la <strong>planeación agregada</strong> es aumentar la productividad, de manera que debe acercar a la organización a su meta económica. En este orden de ideas, la búsqueda de la <strong>maximización del beneficio</strong> se alinea con los objetivos del plan agregado, entendiéndose como la diferencia entre los ingresos y los gastos operativos, por ende es válido considerar la <strong>minimización de los <a href="https://ingenieriaindustrialonline.com/produccion/costos-de-produccion/" target="_blank" rel="noopener">costos totales </a></strong>(mientras no se afecten los ingresos) como <strong>criterio de decisión de la planeación agregada.</strong></p>
<p>Teniendo en cuenta lo anterior, es necesario evaluar con espíritu crítico y perspectiva sistémica, todas las relaciones entre los recursos disponibles para llevar a cabo el plan y sus implicaciones en los costos totales. De manera que pueden identificarse los costos asociados a los siguientes factores:</p>

		<div class="checklist tie-list-shortcode">
<ul>
<li>Mano de obra: Costo de tiempo normal.</li>
<li>Contratación: Costos asociados a la búsqueda de mano de obra, a la contratación misma y a las actividades de inducción.</li>
<li>Despidos: Costos legales (compensaciones e indemnizaciones) de despedir empleados.</li>
<li>Horas extras.</li>
<li>Subcontratación (Outsourcing).</li>
<li>Inventario: Costos de mantenimiento de inventario, incluso costos de oportunidad por lucro cesante.</li>
<li>Ruptura de inventario (faltantes).</li>
<li>Costos de financiación del plan.</li>
</ul>

		</div>
	
<h3><em>Restricciones</em></h3>
<p>Todos los sistemas objetos de planeación agregada se encuentran sujetos a restricciones y de diversos tipos, tales como:</p>

		<div class="checklist tie-list-shortcode">
<ul>
<li>Restricciones de demanda: P.ej: Requerimientos por periodo.</li>
<li>Restricciones laborales: P.ej: Máximo número de horas extras posibles.</li>
<li>Restricciones de espacio: P.ej: Máxima capacidad de almacenamiento.</li>
<li>Restricciones de la cadena de valor: P.ej: Capacidad máxima del proveedor.</li>
<li>Restricciones de eficiencia: P.ej: Curva de aprendiza en empleados nuevos.</li>
</ul>

		</div>
	

		<div class="clearfix"></div>
		<hr style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid">
	
<h2>Modelos de programación lineal en planeación agregada</h2>
<p>Existen diversos métodos empleados en la creación de un plan agregado, entre los que se destacan la <strong><a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/programacion-lineal/" target="_blank" rel="noopener noreferrer">programación lineal</a></strong>, reglas de decisión por búsqueda, programación por objetivos, programación dinámica, o métodos heurísticos (ensayo y error).</p>
<p>La programación lineal por sus características innatas de modelación libre, se constituye como una herramienta poderosa de resolución de planes agregados, de manera que puede considerar tantas restricciones como la realidad del sistema lo presenten, al mismo tiempo que se enfoca en soluciones óptimas, a diferencia de los <a title="Planeación Agregada" href="https://ingenieriaindustrialonline.com/produccion/planeacion-agregada/" target="_blank" rel="noopener"><strong>métodos heurísticos de comparación de alternativas</strong></a>.</p>

		<div class="box info  alignleft">
			<div class="box-inner-block">
				<span class="fa tie-shortcode-boxicon"></span>A lo largo del tiempo se han desarrollado modelos de programación lineal aplicados a la planeación agregada, tales como el método de transporte de Bowman, el método de Hanssman-Hess, que permite concluir que pueden desarrollarse tantos modelos como existan casos de estudio, y variarán de acuerdo a las restricciones que consideren, haciendo modelos más o menos robustos.
			</div>
		</div>
	
<p>A continuación, se detallará un modelo de programación lineal mixta propuesto por el autor, que considerará la mayor parte de los criterios de decisión contemplados en los métodos heurísticos de comparación de alternativas, en búsqueda de una solución óptima.</p>
<h3>Modelo de programación lineal mixta aplicado a Planeación Agregada</h3>
<p>En el modelo propuesto las decisiones se toman de acuerdo con una fuerza de trabajo flexible considerando tiempo extraordinario de trabajo y una tasa de producción que contempla un factor de eficiencia reducida para operarios nuevos en el periodo de contratación, simulando el impacto de la curva de aprendizaje.</p>
<p>Los costos que afectan la función objetivo se expresan en «costos por unidades agregadas» y «costos por operario», e incluye los componentes de:</p>

		<div class="checklist tie-list-shortcode">
<ul>
<li><em>Costos de nómina normal.</em></li>
<li><em>Costos de nómina en tiempo extraordinario.</em></li>
<li><em>Costos de contratación de personal.</em></li>
<li><em>Costos de despido de personal.</em></li>
<li><em>Costos de subcontratación y maquila.</em></li>
<li><em>Costos totales de inventario.</em></li>
</ul>

		</div>
	
<p>Además, los costos asociados al tiempo normal y extraordinario relacionados con los operarios nuevos también contemplan el factor de eficiencia reducida por periodo.</p>
<h2><em>Datos de entrada requeridos</em></h2>
<p><em>El modelo que hemos preparado requiere de los siguientes datos de entrada:</em></p>
<p><em><strong>m </strong></em>= Número de periodos del plan</p>
<p><em><strong>días </strong></em>= Número de días de cada periodo del plan</p>
<p><em><strong>requerimientos </strong></em>= Los requerimientos estimados de cada periodo del plan (unidades)</p>
<p><em><strong>Jornada laboral </strong></em>= Jornada laboral en términos de horas / día</p>
<p><em><strong>Tiempo unitario </strong></em>= Tiempo empleado en producir una unidad (horas / trabajador / unidad)</p>
<p><em><strong>Eficiencia </strong></em>= En este modelo consideramos esta variable como factor de eficiencia de los operarios nuevos (curva de aprendizaje) &#8211; Solo la consideramos durante el primer periodo de producción del operario</p>
<p><em><strong>Horas extras máx. </strong></em><em>= </em>Cantidad máxima de horas extras que puede emplear un operario por periodo</p>
<p><em><strong>Unidades subcontratadas máx. </strong>= </em>Cantidad máxima de unidades que se pueden subcontratar por periodo</p>
<p><em><strong>Inventario Inicial </strong>= </em>Inventario Inicial dado en unidades</p>
<p><em><strong>Costo normal </strong></em><em>= </em>Costo de tiempo normal ($ / hora)</p>
<p><em><strong>Costo extra </strong></em><em>= </em>Costo de tiempo extra ($ / hora)</p>
<p><em><strong>Costo de inventario </strong></em>= Costo de tener unidades en inventario ($ / unidad / periodo)</p>
<p><em><strong>Costo de subcontratar </strong></em><em>= </em>Costo de tercerizar la fabricación de unidades ($ / unidad)</p>
<p><em><strong>Costo de contratar </strong></em>= Costo de contratar un operario ($ / operario)</p>
<p><em><strong>Costo de despedir </strong></em>= Costo de despedir un operario ($ / operario)</p>
<p><strong><em>Operarios </em></strong>= Número inicial de operarios</p>
<hr />
<p>La siguiente formulación ha sido evaluada y validada en diversos programas solucionadores, como Solver, QM, WinQSB y Google Or-Tools.</p>
<h2><em>Variables de decisión</em></h2>
<p>&nbsp;</p>
<p><strong>x<sub>i</sub></strong> = Cantidad de unidades a producir en tiempo normal por operarios antiguos en el periodo <em>i</em></p>
<p><strong>xn<sub>i</sub></strong> = Cantidad de unidades a producir en tiempo normal por operarios nuevos en el periodo <em>i</em></p>
<p><strong>xz<sub>i</sub></strong> = Cantidad de unidades a producir en tiempo extra por operarios antiguos en el periodo <em>i</em></p>
<p><strong>xnz<sub>i</sub></strong> = Cantidad de unidades a producir en tiempo extra por operarios nuevos en el periodo <em>i</em></p>
<p><strong>c<sub>i</sub></strong> = Número de operarios a contratar en el periodo <em>i</em></p>
<p><strong>d<sub>i</sub></strong> = Número de operarios a despedir en el periodo <em>i</em></p>
<p><strong>oi<sub>i</sub></strong> = Número de operarios totales al inicio del periodo <em>i</em></p>
<p><strong>of<sub>i</sub></strong> = Número de operarios totales al final del periodo <em>i</em></p>
<p><strong>sub<sub>i</sub></strong> = Número de unidades a subcontratar en el periodo <em>i</em></p>
<p><strong><sub><span style="font-size: 16px;">inv</span>i</sub></strong> = Número de unidades en inventario al final del periodo <em>i</em></p>
<h3><em>Variables de cálculos intermedios</em></h3>
<p><strong>P<sub>i</sub></strong> = Cantidad de unidades que puede producir en tiempo normal un operario antiguo en el periodo <em>i</em></p>
<p><strong>Pn<sub>i</sub></strong> = Cantidad de unidades que puede producir en tiempo normal un operario nuevo en el periodo <em>i</em></p>
<p><strong>H<sub>i</sub></strong> = Cantidad máxima de unidades que puede producir en tiempo extra un operario antiguo en el periodo <em>i</em></p>
<p><strong>Hc<sub>i</sub></strong> = Cantidad máxima de unidades que puede producir en tiempo extra un operario nuevo en el periodo <em>i</em></p>
<p>&nbsp;</p>
<h3><em>Formulación de cálculos intermedios</em></h3>
<p><strong>P<sub>i</sub></strong> = (jornada laboral / tiempo unitario) * días del periodo <em>i</em></p>
<p><strong>Pn<sub>i</sub></strong> = P<sub>i</sub> * Eficiencia</p>
<p><strong>H<sub>i</sub></strong> = Horas extras máx. del periodo <em>i </em>/ tiempo unitario</p>
<p><strong>Hc<sub>i</sub></strong> = H<sub>i</sub> * Eficiencia</p>
<h3><em>Variables y entradas financieras</em></h3>
<p>&nbsp;</p>
<p><strong>Costo unitario normal</strong> = Costo normal * Tiempo unitario</p>
<p><strong>Costo unitario normal (operarios nuevos)</strong> = Costo normal * (Tiempo unitario / Eficiencia)</p>
<p><strong>Costo unitario extra</strong> = Costo extra * Tiempo unitario</p>
<p><strong>Costo unitario extra (operarios nuevos)</strong> = Costo extra * (Tiempo unitario / Eficiencia)</p>
<p>&nbsp;</p>
<h3><em>Restricciones de tiempo normal (Operarios antiguos)</em></h3>
<p style="text-align: center;">( P<sub>i</sub> * oi<sub>i</sub> ) &#8211; x<sub>i</sub> &gt;= 0</p>
<p>&nbsp;</p>
<h3><em>Restricciones de tiempo normal (Operarios nuevos)</em></h3>
<p style="text-align: center;">( Pn<sub>i</sub> * c<sub>i</sub> ) &#8211; xn<sub>i</sub> &gt;= 0</p>
<p>&nbsp;</p>
<h3><em>Restricciones de balance de operarios</em></h3>
<p><em>Esta restricción define que los operarios al final de un periodo i = operarios inicial periodo i + 1</em></p>
<p>Para todos los <em>i &gt;= 1:</em></p>
<p style="text-align: center;">oi<sub>i</sub> &#8211; of<sub>i-1</sub> = 0</p>
<p>&nbsp;</p>
<h3><em>Restricciones de balance para la contratación de operarios</em></h3>
<p>Para todos los <em>i &gt;= 1:</em></p>
<p style="text-align: center;">of<sub>i</sub> &#8211; oi<sub>i</sub> &#8211; c<sub>i</sub> + d<sub>i</sub> = 0</p>
<p>&nbsp;</p>
<h3><em>Restricciones de cantidad inicial de operarios</em></h3>
<p><em>Esta restricción nos indica que la cantidad inicial de operarios, es decir <strong>oi</strong> en periodo <strong>0</strong></em>, es equivalente al dato de entrada <em><strong>operarios </strong></em>(El cual nos indica la cantidad inicial de operarios del problema). Esta restricción parece demasiado lógica, sin embargo es vital considerarla de acuerdo a los solucionadores que utilicemos.</p>
<p style="text-align: center;">oi<sub>0</sub> &#8211; operarios = 0</p>
<h3><em>Restricciones de balance de operarios en el periodo 0 (periodo inicial)</em></h3>
<p style="text-align: center;">of<sub>0</sub> &#8211; oi<sub>0</sub> &#8211; c<sub>0</sub> + d<sub>0</sub> = 0</p>
<p>&nbsp;</p>
<h3><em>Restricciones de límites de horas extras (operarios antiguos)</em></h3>
<p style="text-align: center;">xz<sub>i</sub> &#8211; ( H<sub>i</sub>  * oi<sub>i</sub> ) &lt;= 0</p>
<p>&nbsp;</p>
<h3><em>Restricciones de límites de horas extras (operarios nuevos)</em></h3>
<p style="text-align: center;">xnz<sub>i</sub> &#8211; ( Hc<sub>i</sub>  * c<sub>i</sub> ) &lt;= 0</p>
<p>&nbsp;</p>
<h3><em>Restricciones de límites de unidades a subcontratar</em></h3>
<p style="text-align: center;"><sub><span style="font-size: 16px;">sub</span>i</sub> &#8211; unidades subcontratadas máx. en el periodo <em>i</em> &lt;= 0</p>
<p>&nbsp;</p>
<h3><em>Restricciones de balance de inventarios (periodo 1 en adelante)</em></h3>
<p>Para todos los <em>i &gt;= 1:</em></p>
<div style="text-align: center;">inv<sub>i &#8211; 1</sub> + x<sub>i</sub>  + xn<sub>i</sub> + xz<sub>i</sub>  + xnz<sub>i</sub> + sub<sub>i</sub>  &#8211; requerimientos del periodo <em>i &#8211; </em>inv<sub>i</sub><em>  = </em>0</div>
<p>&nbsp;</p>
<h3><em>Restricciones de balance de inventarios (periodo 0)</em></h3>
<div style="text-align: center;">inventario inicial + x<sub>0</sub>  + xn<sub>0</sub> + xz<sub>0</sub>  + xnz<sub>0</sub> + sub<sub>0</sub>  &#8211; requerimientos del periodo <em>0 &#8211; </em>inv<sub>0</sub><em>  = </em>0</div>
<p>&nbsp;</p>
<h3>Restricciones de satisfacción de requerimientos (periodo 1 en adelante)</h3>
<p>Para todos los <em>i &gt;= 1</em></p>
<p style="text-align: center;">x<sub>i</sub>  + xn<sub>i</sub> + xz<sub>i</sub>  + xnz<sub>i</sub> + sub<sub>i</sub>  +<em> </em>inv<sub>i &#8211; 1</sub><em> &#8211; </em>requerimientos del periodo<em> i &gt;= </em>0</p>
<p>&nbsp;</p>
<h3><em>Restricciones de satisfacción de requerimientos (periodo 0)</em></h3>
<p style="text-align: center;">x<sub>0</sub>  + xn<sub>0</sub> + xz<sub>0</sub>  + xnz<sub>0</sub> + sub<sub>0</sub>  +<em> </em>inventario inicial<em> &#8211; </em>requerimientos del periodo<em> 0 &gt;= </em>0</p>
<p>&nbsp;</p>
<h3><em>Restricciones de no negatividad</em></h3>
<p>Todas las variables pertenecen a los números reales enteros y sus valores deberán ser mayores o iguales a cero.</p>
<h3><em>Función objetivo</em></h3>
<p style="text-align: center;">Zmin = (costo unitario normal * x<sub>i</sub> ) + (costo unitario normal de operarios nuevos * xn<sub>i</sub> ) + (costo unitario extra * xz<sub>i</sub> ) + (costo unitario extra operarios nuevos * xnz<sub>i</sub> ) + (costo de subcontratar * sub<sub>i</sub> ) + (costo de contratar * c<sub>i</sub> ) + (costo de despedir * d<sub>i</sub> ) + (costo de inventarios * inv<sub>i</sub> )</p>
<p><em><strong><span class="tie-highlight tie-highlight-green">La aplicación del anterior modelo arrojará la solución óptima por medio del algoritmo «branch and bound».</span></strong></em></p>

		<div class="clearfix"></div>
		<hr style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid">
	
<h2>Caso de estudio: Aplicación de programación lineal en planeación agregada</h2>
<blockquote class="aligncenter quote-simple "><p>Una compañía desea determinar su plan agregado de producción para los próximos 6 meses. Una vez utilizado el modelo de pronóstico más adecuado se establece el siguiente tabulado de requerimientos (no se cuenta con inventario inicial, y no se requiere de inventarios de seguridad).</p></blockquote>
<div id="cc-m-5869678913" class="j-module n j-imageSubtitle ">
<figure class="cc-imagewrapper cc-m-image-align-3"><img decoding="async" class="aligncenter size-full wp-image-2545" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-187.png" alt="" width="294" height="149" /></figure>
<p><em>Información relacionada con el negocio:</em></p>
<p>Jornada laboral: 8 horas / trabajador / día</p>
<p>Costo de contratar: $ 350 / trabajador</p>
<p>Costo de despedir: $ 420 / trabajador</p>
<p>Costo de tiempo normal (mano de obra): $ 6 / hora</p>
<p>Costo de tiempo extra (mano de obra): $8 / hora</p>
<p>Costo de mantenimiento de inventarios: $ 3 /unidad/ mes</p>
<p>Costo de subcontratar: $ 50 / unidad</p>
<p>Tiempo de procesamiento: 5 horas / trabajador / unidad</p>
<p>Jornada laboral: 8 horas / día</p>
<p>Número inicial de trabajadores: 20</p>
<p><strong>Eficiencia de un trabajador nuevo el primer periodo:</strong> 90%</p>
<p><strong>Cantidad máxima de horas extras por operario por mes: </strong>8 horas/trabajador/mes</p>
<p>Capacidad máxima de suministro de unidades de subcontratación: 200 unidades/mes</p>
<p><strong>Unidades: </strong>Toneladas.</p>
<hr />
<p>Ya en la introducción del modelo hemos descrito la definición de las variables, la formulación de los cálculos intermedios, así mismo la formulación de las restricciones. Estas pueden utilizarse básicamente en cualquier solucionador.</p>
<p>En este caso vamos a utilizar un solucionador de programación basada en restricciones: <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/que-es-y-para-que-sirve-google-or-tools/" target="_blank" rel="noopener"><em><strong>Google OR-Tools</strong></em></a>. Para eso, vamos a programar nuestro modelo utilizando Python. No se preocupe si no cuenta con este programa, la idea es introducirnos de a poco en estas nuevas soluciones, por lo tanto utilizaremos un entorno virtual que podrás ejecutar sin la necesidad de realizar ninguna instalación.</p>
<p>Vamos a asumir que utilizarán el entorno virtual de <em>Colaboratory</em>, así que vayamos allá: <a href="https://colab.research.google.com/#create=true" target="_blank" rel="noopener"><em><strong>Abrir cuaderno nuevo</strong></em></a>.</p>
<h3><em>Paso 1: Instalar las librerías de Google Or Tools</em></h3>
<p>Este paso debe realizarse solo si vamos a utilizar el cuaderno de <em>Colaboratory</em>:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>!pip install ortools</code></pre>
</div>
<h3><em>Paso 2: Importar las librerías necesarias y declarar el solucionador</em></h3>
<p>En este caso, solo importaremos la librería correspondiente al módulo de programación lineal de Google Or-Tools. Además, utilizaremos el solucionador<span> SCIP </span><em>(Solving Constraint Integer Programs), un solucionador de código abierto disponible que permite resolver problemas lineales mixtos (Google OR-Tools posee múltiples solucionadores):</em></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>from ortools.linear_solver import pywraplp

solver = pywraplp.Solver.CreateSolver('SCIP')</code></pre>
</div>
<h3><em>Paso 3: Datos de entrada</em></h3>
<p>De acuerdo a los datos que nos plantea el problema, registramos las entradas del modelo:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#DATOS DE ENTRADA

periodos = [0, 1, 2, 3, 4, 5] #Periodos del plan, empezando desde el periodo 0
demanda = [2500, 1500, 3000, 1000, 2500, 2200] #Requerimintos de cada periodo
dias = [22, 19, 21, 21, 22, 20] #Días laborales de cada periodo
jornada_laboral = 8 #horas / trabajador / día
tiempo_unitario = 5 #horas / unidad
Eficiencia = 0.9 #Eficiencia de un trabajador nuevo periodo 1
horas_extras_max = [8, 8, 8, 8, 8, 8] #Cantidad máxima de horas extras por periodo
unidades_sub_max = [200, 200, 200, 200, 200, 200] #Cantidad máxima de unidades que se pueden subcontratar en el periodo i
inv_inicial = 0 #Unidades
costo_normal = 6 #Unidades monetarias / hora
costo_extra = 8 #Unidades monetarias / hora
costo_inventario = 3 #Unidades monetarias / periodo
costo_sub = 50 #Unidades monetarias / unidad
costo_contratar = 350 #Unidades monetarias / trabajador
costo_despedir = 420 #Unidades monetarias / trabajador
operarios = 20 #Número inicial de trabajadores</code></pre>
</div>
<h3><em>Paso 4: Definir y formular los cálculos intermedios</em></h3>
<p>De acuerdo a la formulación que ya mostramos de nuestro modelo, se definirán aquellos cálculos intermedios necesarios. Recordemos la formulación que teníamos definida:</p>
<p><strong>P<sub>i</sub></strong> = (jornada laboral / tiempo unitario) * días del periodo <em>i</em></p>
<p><strong>Pn<sub>i</sub></strong> = P<sub>i</sub> * Eficiencia</p>
<p><strong>H<sub>i</sub></strong> = Horas extras máx. del periodo <em>i </em>/ tiempo unitario</p>
<p><strong>Hc<sub>i</sub></strong> = H<sub>i</sub> * Eficiencia</p>
<p>Si realizamos los cálculos de forma manual, podemos completar una tabla como la siguiente:</p>
<figure class="cc-imagewrapper cc-m-image-align-3"><img decoding="async" class="aligncenter size-full wp-image-2547" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-189.png" alt="" width="485" height="147" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-189.png 485w, https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-189-300x91.png 300w" sizes="(max-width: 485px) 100vw, 485px" /></figure>
<p>Veamos por ejemplo, el periodo 0 (periodo inicial).</p>
<p><strong>P<sub>0</sub></strong> (mes 1) = (8 / 5) * 22</p>
<p><strong>P<sub>0</sub></strong> (mes 1) = 35,20</p>
<p>Ahora veamos cómo podemos programar este cálculo en nuestro modelo (mediante un ciclo):</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>P= [] #Cantidad de unidades que puede producir un operario en el periodo i
for i in range(len(periodos)):
    P.append((jornada_laboral / tiempo_unitario) * dias[i])</code></pre>
</div>
<p>De esta forma efectuamos el cálculo para cada período. Veamos lo que pasaría imprimimos la variable P:</p>
</div>
<div class="j-module n j-imageSubtitle "><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/P.png" alt="" width="649" height="165" class="size-full wp-image-28335 aligncenter" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/P.png 649w, https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/P-300x76.png 300w" sizes="(max-width: 649px) 100vw, 649px" />Podemos corroborar cómo obtenemos los mismos resultados que se encuentran consignados en el tabulado. Ahora completaremos los cálculos intermedios restantes:</div>
<div>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>Pn= []#Cantidad de unidades que puede producir un operario nuevo en el periodo i
for i in range(len(periodos)):
    Pn.append(P[i] * Eficiencia)
    
H= []#Cantidad máxima de unidades que puede producir un operario en el periodo i con horas extras
for i in range(len(periodos)):
    H.append(horas_extras_max[i] / tiempo_unitario)
    
Hc= []#Cantidad máxima de unidades que puede producir un operario en el periodo i con horas extras
for i in range(len(periodos)):
    Hc.append(H[i] * Eficiencia)</code></pre>
</div>
</div>
<h3 class="j-module n j-imageSubtitle "><em>Paso 5: Definir las variables de decisión</em></h3>
<p>En este paso definiremos cada variable de decisión del modelo, definiremos también su naturaleza y su rango de valores: <em>entera, mayor o iguala cero (desde 0 hasta infinito).</em></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Variables de decisión

x= {} #Unidades a producir en el periodo i
for i in range(len(periodos)):
    x[i] = solver.IntVar(0, solver.infinity(), '')
    
xn= {} #Unidades a producir por operarios nuevos en el periodo i
for i in range(len(periodos)):
    xn[i] = solver.IntVar(0, solver.infinity(), '')
    
xz= {} #Unidades a producir en tiempo extra en el periodo i
for i in range(len(periodos)):
    xz[i] = solver.IntVar(0, solver.infinity(), '')
    
xnz= {} #Unidades a producir en tiempo extra por operarios nuevos en el periodo i
for i in range(len(periodos)):
    xnz[i] = solver.IntVar(0, solver.infinity(), '')
    
c= {} #Operarios a contratar en el período i
for i in range(len(periodos)):
    c[i] = solver.IntVar(0, solver.infinity(), '')

d= {} #Operarios a despedir en el período i
for i in range(len(periodos)):
    d[i] = solver.IntVar(0, solver.infinity(), '')
    
sub= {} #Unidades a subcontratar en el periodo i
for i in range(len(periodos)):
    sub[i] = solver.IntVar(0, solver.infinity(), '') 
    
inv= {} #Unidades en inventario al final del periodo i
for i in range(len(periodos)):
    inv[i] = solver.IntVar(0, solver.infinity(), '') 
    
oi= {} #Número de operarios totales al inicio del periodo i
for i in range(len(periodos)):
    oi[i] = solver.IntVar(0, solver.infinity(), '') 
    
of= {} #Número de operarios totales al final del periodo i
for i in range(len(periodos)):
    of[i] = solver.IntVar(0, solver.infinity(), '') </code></pre>
</div>
<p>En el código anterior, y ya que cada variable debe definirse para cada periodo del plan agregado, utilizamos ciclos para su definición. Este ciclo tiene un rango determinado por el número de periodos, de esta manera, ya que tenemos 6 periodos, por ejemplo, en la definición de la variable <strong>Xi</strong>, tendremos las siguientes variables definidas: <strong>x<sub>0</sub></strong>, <strong>x<sub>1</sub></strong>, <strong>x<sub>2</sub></strong>, <strong>x<sub>3</sub></strong>, <strong>x<sub>4</sub></strong>, <strong>x<sub>5</sub></strong> . Así mismo sucede con las variables restantes.</p>
<p>Desde luego, existe la posibilidad de definir cada una de las variables de forma individual, sin embargo el proceso sería algo tedioso, y sería algo así:</p>
<p><em>x[0] = solver.IntVar(0, solver.infinity(), »)</em></p>
<p><em>x[1] = solver.IntVar(0, solver.infinity(), »)</em></p>
<p><em>x[2] = solver.IntVar(0, solver.infinity(), »)</em></p>
<p><em>x[3] = solver.IntVar(0, solver.infinity(), »)</em></p>
<p><em>x[4] = solver.IntVar(0, solver.infinity(), »)</em></p>
<p><em>x[5] = solver.IntVar(0, solver.infinity(), »)</em></p>
<p>Por esta razón utilizamos ciclos para tal efecto.</p>
<h3><em>Paso 6: Formulación de variables de costo</em></h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Formulación de variables financieras (Costos)
costo_unitario_normal = costo_normal * tiempo_unitario
costo_unitario_normal_nuevo = costo_normal * (tiempo_unitario / Eficiencia) 
costo_unitario_extra = costo_extra * tiempo_unitario
costo_unitario_extra_nuevo = costo_extra * (tiempo_unitario / Eficiencia) </code></pre>
</div>
<h3><em>Paso 7: Restricciones del modelo</em></h3>
<p>Utilizando la misma metodología de ciclos para recorrer los periodos del plan, formulamos nuestras restricciones. Estas restricciones son exactamente las mismas que formulamos de manera algebraica en el planteamiento general de nuestro modelo.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#RESTRICCIONES DEL MODELO

# Restricciones de tiempo normal (Producción normal)
for i in range(len(periodos)):
    solver.Add(((P[i] * oi[i]) - x[i]) &gt;= 0)

# Restricciones de tiempo normal (Producción normaml - operarios nuevos)
for i in range(len(periodos)):
    solver.Add(((Pn[i] * c[i]) - xn[i]) &gt;= 0)
    
# Restricciones de balance (operarios al final de un periodo i = operarios inicial periodo i + 1)
for i in range(1, len(periodos)):
    solver.Add(oi[i] - of[i-1] == 0)
    
# Restricciones de balance para la contratación de operarios (periodo i en adelante)
for i in range(1, len(periodos)):
    solver.Add(of[i] - oi[i] - c[i] + d[i] == 0)
    
# Restricción de cantidad inicial de operarios
solver.Add(oi[0] - operarios == 0)
    
# Restricciones de balance de operarios periodo 0
solver.Add(of[0] - oi[0] - c[0] + d[0] == 0)
    
# Restricciones límite de horas extras operarios antiguos
for i in range(len(periodos)):
    solver.Add(xz[i] -(H[i] * oi[i]) &lt;= 0)
    
# Restricciones límite de horas extras operarios nuevos
for i in range(len(periodos)):
    solver.Add(xnz[i] -(Hc[i] * c[i]) &lt;= 0)
    
# Restricciones límite de unidades a subcontratar
for i in range(len(periodos)):
    solver.Add(sub[i] - unidades_sub_max[i] &lt;= 0) 

# Restricciones límite de balance de inventarios periodo 1 en adelante 
for i in range(1, len(periodos)): 
    solver.Add(inv[i-1] + x[i] + xn[i] + xz[i] + xnz[i] + sub[i] - demanda[i] - inv[i] == 0) 

# Restricciones límite de balance de inventarios periodo 0 
solver.Add(inv_inicial + x[0] + xn[0] + xz[0] + xnz[0] + sub[0] - demanda[0] - inv[0] == 0) 

# Restricciones de satisfacción de demanda periodo 1 en adelante 
for i in range(1, len(periodos)): 
    solver.Add(x[i] + xn[i] + xz[i] + xnz[i] + sub[i] + inv[i-1] - demanda[i] &gt;= 0)
    
# Restricciones límite de balance de inventarios periodo 0
solver.Add(x[0] + xn[0] + xz[0] + xnz[0] + sub[0] + inv_inicial - demanda[i] &gt;= 0)</code></pre>
</div>
<h3><em>Paso 8: Definir la función objetivo</em></h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#FUNCIÓN OBJETIVO MINIMIZAR
objective_terms = []
for i in range(len(periodos)):
    objective_terms.append((costo_unitario_normal * x[i]) + (costo_unitario_normal_nuevo * xn[i]) + (costo_unitario_extra * xz[i]) + (costo_unitario_extra_nuevo * xnz[i]) + (costo_sub * sub[i]) + (costo_contratar * c[i]) + (costo_despedir * d[i]) + (costo_inventario * inv[i]))
solver.Minimize(solver.Sum(objective_terms))  </code></pre>
</div>
<h3><em>Paso 9: Invocar al solucionador</em></h3>
<p>A continuación daremos la orden al programa de resolver el modelo.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Invocar al solucionador
status = solver.Solve()</code></pre>
</div>
<h3><em>Paso 10: Configurar las salidas del modelo</em></h3>
<p>Este paso lo verán quizá confuso; y la verdad no lo es tanto. Toda vez que el modelo está resuelto, podemos configurar la salida que queramos en cualquier momento; eso sí, hemos desarrollado unas líneas de código que pueden resultar extensas, con el fin de organizar los datos de salida. Por ejemplo, hemos definido encabezados para los datos, y otras cuestiones de forma.</p>
<p>Si usted desea, por ejemplo, tan solo conocer el valor total del plan agregado, puede introducir las siguientes líneas:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>print('\nValor total del plan (FO) =', solver.Objective().Value())</code></pre>
</div>
<p>Al ejecutar esta línea tendremos el siguiente resultado:</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/valor-total.png" alt="valor total" width="591" height="106" class="size-full wp-image-28337 aligncenter" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/valor-total.png 591w, https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/valor-total-300x54.png 300w" sizes="(max-width: 591px) 100vw, 591px" /></p>
<p>Las siguientes líneas nos proporcionarán la información relevante de forma organizada:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>if status == pywraplp.Solver.OPTIMAL:
    print('\nSOLUCIÓN: ')        
    print('\nPLAN DE PRODUCCIÓN \n')
    print('|{:^20}|{:^20}|{:^20}|{:^20}|{:^20}|{:^20}|'.format(
      'Período',
      'Producción (TN)',
      'TN (Op. Nuevos)',
      'Producción (HE)',
      'HE (Op. Nuevos)',
      'Uni. Subcon'))
    
    for i in range(len(periodos)):
        print('|{:^20}|{:^20}|{:^20}|{:^20}|{:^20}|{:^20}|'.format(
          i,
          x[i].solution_value(),
          xn[i].solution_value(),
          xz[i].solution_value(),
          xnz[i].solution_value(),
          sub[i].solution_value()))
    print('\nProduccción (TN): Cantidad de unidades a producir en tiempo normal')
    print('TN (Op. Nuevos): Cantidad de unidades a producir en tiempo normal con operarios nuevos')
    print('Producción (HE): Cantidad de unidades a producir en tiempo extra')
    print('HE (Op. Nuevos): Cantidad de unidades a producir en tiempo extra con operarios nuevos')
    print('Uni. Subcon: Cantidad de unidades a subcontratar')
    print('\nCOSTOS \n')
    print('|{:^20}|{:^20}|'.format(
          'Período',
          'Costo'))  
    costo = []
    for i in range(len(periodos)):
        costo.append((costo_unitario_normal * x[i].solution_value()) + (costo_unitario_normal_nuevo * xn[i].solution_value()) + (costo_unitario_extra * xz[i].solution_value()) + (costo_unitario_extra_nuevo * xnz[i].solution_value()) + (costo_sub * sub[i].solution_value()) + (costo_contratar * c[i].solution_value()) + (costo_despedir * d[i].solution_value()) + (costo_inventario * inv[i].solution_value()))
        print('|{:^20}|{:^20.2f}|'.format(
          i,
          costo[i]))
    print('\nValor total del plan (FO) =', solver.Objective().Value())
    print('Costo unitario del tiempo normal = {0} unidades monetarias / h'.format(costo_unitario_normal))
    print('Costo unitario del tiempo normal (Operarios nuevos)= {0:.2f} unidades monetarias / h'.format(costo_unitario_normal_nuevo))
    print('Costo unitario del tiempo extra= {0} unidades monetarias / h'.format(costo_unitario_extra))
    print('Costo unitario del tiempo extra (Operarios nuevos)= {0:.2f} unidades monetarias / h'.format(costo_unitario_extra_nuevo))
    print('\nINVENTARIOS \n')
    print('|{:^20}|{:^20}|'.format(
          'Período', 
          'Inv. final'))  
    for i in range(len(periodos)):    
        print('|{:^20}|{:^20}|'.format(
          i,
          inv[i].solution_value()))
    print('\nFUERZA LABORAL (OPERARIOS)\n')
    print('|{:^20}|{:^20}|{:^20}|{:^20}|{:^20}|'.format(
          'Período', 
          'Cantidad inicial',
          'Contrataciones',
          'Despidos',
          'Cantidad final'))  
    for i in range(len(periodos)):    
        print('|{:^20}|{:^20}|{:^20}|{:^20}|{:^20}|'.format(
          i,
          oi[i].solution_value(),
          c[i].solution_value(),
          d[i].solution_value(),
          of[i].solution_value()))
   
    vector_i=[]
    vector_x=[]
    vector_xn=[]
    vector_xz=[]
    vector_xnz=[]
    vector_sub=[]    
    
    for i in range (len(periodos)):
        vector_i.append(i)
        vector_x.append(x[i].solution_value())
        vector_xn.append(xn[i].solution_value())
        vector_xz.append(xz[i].solution_value())
        vector_xnz.append(xnz[i].solution_value())
        vector_sub.append(sub[i].solution_value())    
    
    datos = {}
    
    datos['Periodo'] = vector_i
    datos['Producción (TN)'] = vector_x
    datos['TN (Op. Nuevos)'] = vector_xn
    datos['Producción (HE)'] = vector_xz
    datos['HE (Op. Nuevos)'] = vector_xnz
    datos['Uni. Subcon'] = vector_sub
      
else:
  if status == solver.FEASIBLE:
    print('Se encontró una solución potencialmente subóptima.')
  else:
    print('El problema no tiene solución óptima.')</code></pre>
</div>
<p>Al ejecutar el modelo tendremos:</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/planeacion-agregada-solucion-python.png" alt="planeacion agregada solucion python" width="825" height="718" class="alignnone size-full wp-image-28338" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/planeacion-agregada-solucion-python.png 825w, https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/planeacion-agregada-solucion-python-300x261.png 300w, https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/planeacion-agregada-solucion-python-768x668.png 768w" sizes="(max-width: 825px) 100vw, 825px" /></p>
<p>Puedes acceder y ejecutar el modelo desde <em><strong><a href="https://colab.research.google.com/drive/1Gvk-1B_0KBvGrwKdMX-LJNMbGFiiLnXG?usp=sharing" target="_blank" rel="noopener">Planeación Agregada mediante Programación Lineal</a></strong>.</em></p>
<p>El tiempo empleado por el solucionador es inferior a 1 segundo de procesamiento. Dada la potencia del solucionador Google Or Tools y la capacidad de procesamiento del entorno virtual.</p>
<p>El valor objetivo hallado es: <strong>408074,33</strong></p>
<hr />
<p>Parte de la utilidad de este desarrollo es la posibilidad de integrarse con diversas fuentes de información. Puede, por ejemplo, tomar datos desde diversos archivos en entornos locales o externos. De igual forma puede exportar la información obtenida.</p>
<h2>Utilizando este modelo y su formulación en Solver de Microsoft Excel</h2>
<p>El mismo modelo que hemos desarrollado en <em>Python </em>ha sido formulado en Microsoft Excel para su resolución mediante <em>Solver</em>. De acuerdo a los parámetros genéricos de <em>Solver</em>, sin modificar límites de búsqueda, hemos obtenido una solución equivalente a: <strong>410498</strong>, empleando aproximadamente 50 segundos para llegar a ella.</p>
<p>Podemos observar entonces que la solución obtenida mediante Google Or-Tools satisface mucho más el criterio de optimización, así mismo alcanza la solución en un tiempo mucho menor.</p>
<p><em>¿A qué puede deberse esa diferencia entre las soluciones obtenidas mediante ambos solucionadores? </em>Principalmente a la tolerancia predeterminada de los solucionadores de <em>Solver </em>para las restricciones de variables enteras. Es posible incluso, en el cuadro de opciones de <em>Solver </em>configurar esta tolerancia como 0 para obtener mejores resultados. Lo hemos hecho y hemos alcanzado <strong>419601</strong>. Sin embargo, el tiempo de búsqueda puede tardar minutos.</p>

		<div class="box download  ">
			<div class="box-inner-block">
				<span class="fa tie-shortcode-boxicon"></span>Te dejamos el documento en Microsoft Excel para su descarga: <a href="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Planeacion_agregada.xlsx" target="_blank" rel="noopener"><strong>Planeación Agregada mediante Programación Lineal &#8211; Excel (Solver)</strong></a>
			</div>
		</div>
	
<figure class="cc-imagewrapper cc-m-image-align-3"></figure>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/produccion/planeacion-agregada-mediante-programacion-lineal/">Planeación agregada mediante programación lineal</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ingenieriaindustrialonline.com/produccion/planeacion-agregada-mediante-programacion-lineal/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
	</channel>
</rss>
