<?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>Routing archivos &#187; Ingenieria Industrial Online</title>
	<atom:link href="https://ingenieriaindustrialonline.com/tag/routing/feed/" rel="self" type="application/rss+xml" />
	<link>https://ingenieriaindustrialonline.com/tag/routing/</link>
	<description>ingenieriaindustriaonline.com</description>
	<lastBuildDate>Fri, 20 Jan 2023 18:31:04 +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>Routing archivos &#187; Ingenieria Industrial Online</title>
	<link>https://ingenieriaindustrialonline.com/tag/routing/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>¿Cómo calcular una matriz de distancias para modelar un VRP?</title>
		<link>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/como-calcular-una-matriz-de-distancias-para-modelar-un-vrp/</link>
					<comments>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/como-calcular-una-matriz-de-distancias-para-modelar-un-vrp/#comments</comments>
		
		<dc:creator><![CDATA[Bryan Salazar López]]></dc:creator>
		<pubDate>Tue, 16 Nov 2021 21:44:14 +0000</pubDate>
				<category><![CDATA[Investigación de operaciones]]></category>
		<category><![CDATA[Logística]]></category>
		<category><![CDATA[Google OR-Tools]]></category>
		<category><![CDATA[Haversine]]></category>
		<category><![CDATA[Matriz de distancias]]></category>
		<category><![CDATA[Optimización]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Routing]]></category>
		<category><![CDATA[Ruteo]]></category>
		<category><![CDATA[VRP]]></category>
		<guid isPermaLink="false">https://ingenieriaindustrialonline.com/?p=28858</guid>

					<description><![CDATA[<p>Tal como lo hemos abordado ampliamente, una de las aplicaciones más importantes del modelamiento de Cadenas de Suministro, es el diseño de red de abastecimiento, y dentro de esta categoría, el diseño de rutas de transporte (enrutamiento de vehículos). Los problemas de enrutamiento de vehículos (routing), se encuentran clasificados como problemas de optimización combinatoria, y &#8230;</p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/como-calcular-una-matriz-de-distancias-para-modelar-un-vrp/">¿Cómo calcular una matriz de distancias para modelar un VRP?</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>Tal como lo hemos abordado ampliamente, u<span>na de las aplicaciones más importantes del modelamiento de Cadenas de Suministro, es el diseño de red de abastecimiento, y dentro de esta categoría, el diseño de rutas de transporte (enrutamiento de vehículos).</span></p>
<p>Los problemas de enrutamiento de vehículos (<em>routing</em>), se encuentran clasificados como problemas de optimización combinatoria, y esto producto de que la cantidad de rutas posibles en un modelo básico, se encuentra determinado por la <span>ecuación (</span><em>n</em><span> – 1)!, donde </span><em>n</em><span>, es igual al número de ubicaciones que componen el problema de enrutamiento.</span></p>
<p>Ahora bien, no solo la resolución de los problemas de enrutamiento es considerada como compleja, también lo es la fase preliminar en la que debemos levantar la información de entrada del modelo (<em>inputs</em>), específicamente, la <strong><em>matriz de distancias</em></strong>.</p>
<p>La forma más básica de un modelo <em>VRP</em>, requiere para su resolución la siguiente información mínima de entrada:</p>

		<div class="plus tie-list-shortcode">
<ul>
<li>Número de vehículos</li>
<li>Un depósito</li>
<li>Matriz de distancias</li>
</ul>

		</div>
	
<p>La matriz de distancias es un tabulado que registra la longitud entre cada uno de los nodos del modelo, incluido el nodo depósito. Su tamaño será <em>n x n </em>(siendo <em>n</em> el número de nodos del modelo). Asumiendo, por ejemplo, que tenemos un problema que se compone de 1 depósito y 19 ubicaciones, el modelo requerirá de una <em>matriz de distancias</em> con 400 datos.</p>

		<div class="box note  ">
			<div class="box-inner-block">
				<span class="fa tie-shortcode-boxicon"></span>
<p>Recuerdo que cuando abordamos por primera vez este tema en la <em>Universidad</em>, en mi etapa de estudiante, se nos mencionó enfáticamente la complejidad que subyace en este tipo de modelos: El procesamiento, el levantamiento de datos, la consideración de ubicaciones reales, etc.</p>
<p>Pues bien, han pasado algunos años desde entonces, y todo cambió. El modelamiento de este tipo de problemas, la integración con sistemas de información geográfica, la posibilidad de automatizar procesos de captura de información. <em>¡Todo!</em></p>

			</div>
		</div>
	
<p>En artículos anteriores hemos abordado modelos robustos para la <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problema-de-enrutamiento-de-vehiculos-vrp-con-google-or-tools/"><strong>resolución de problemas VRP</strong></a> y algunas de sus extensiones (<a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problema-de-enrutamiento-de-vehiculos-capacitados-cvrp-con-google-or-tools/"><strong>CVRP</strong></a>, por ejemplo). El objetivo de este artículo será la de utilizar funciones espaciales de <em>Python</em>, para obtener una matriz de distancias de acuerdo a ubicaciones reales, con el propósito de resolver un modelo VRP básico.</p>
<hr />
<h2>Caso de aplicación</h2>

		<div id="caso-de-aplicacion" data-title="Caso de aplicación" class="index-title"></div>
	
<blockquote class=" quote-simple "><p>La Secretaría de Educación de Santiago de Cali, tiene dentro de sus funciones, distribuir el material pedagógico a las diferentes instituciones de formación de la ciudad. La distribución del material se realiza todos los lunes, y para ello, la Secretaría cuenta con dos vehículos con capacidad suficiente para transportar todo el material. El depósito del material (lugar desde donde salen y deben regresar los vehículos), se encuentra en la Alcaldía de Cali; y las instituciones que deben visitarse son 60. Tal como podemos apreciar en la siguiente tabla:</p></blockquote>
<table width="576">
<tbody>
<tr>
<td width="80" style="text-align: center;"><strong>Nodo</strong></td>
<td width="224" style="text-align: center;"><strong>Lugar</strong></td>
<td width="127" style="text-align: center;"><strong>Latitud</strong></td>
<td width="145" style="text-align: center;"><strong>Longitud</strong></td>
</tr>
<tr>
<td style="text-align: center;">0</td>
<td style="text-align: center;">Secretaría de Educación &#8211; Alcaldía</td>
<td style="text-align: center;">3,454431875</td>
<td style="text-align: center;">-76,53421433</td>
</tr>
<tr>
<td style="text-align: center;">1</td>
<td style="text-align: center;"> Comfandi San Nicolás</td>
<td style="text-align: center;">3,453591118</td>
<td style="text-align: center;">-76,52254886</td>
</tr>
<tr>
<td style="text-align: center;">2</td>
<td style="text-align: center;"> Mayor de Santiago de Cali</td>
<td style="text-align: center;">3,451577758</td>
<td style="text-align: center;">-76,51023216</td>
</tr>
<tr>
<td style="text-align: center;">3</td>
<td style="text-align: center;"> Municipal Comfandi</td>
<td style="text-align: center;">3,448107915</td>
<td style="text-align: center;">-76,51074714</td>
</tr>
<tr>
<td style="text-align: center;">4</td>
<td style="text-align: center;"> Internado San Carlos</td>
<td style="text-align: center;">3,446994135</td>
<td style="text-align: center;">-76,51525325</td>
</tr>
<tr>
<td style="text-align: center;">5</td>
<td style="text-align: center;"> León de Greiff</td>
<td style="text-align: center;">3,447979402</td>
<td style="text-align: center;">-76,49993247</td>
</tr>
<tr>
<td style="text-align: center;">6</td>
<td style="text-align: center;"> Nuestra Señora de la Anunciación</td>
<td style="text-align: center;">3,445152112</td>
<td style="text-align: center;">-76,49641342</td>
</tr>
<tr>
<td style="text-align: center;">7</td>
<td style="text-align: center;"> Fernando de Aragón</td>
<td style="text-align: center;">3,437355603</td>
<td style="text-align: center;">-76,51383704</td>
</tr>
<tr>
<td style="text-align: center;">8</td>
<td style="text-align: center;"> Casa Evangélica</td>
<td style="text-align: center;">3,437955337</td>
<td style="text-align: center;">-76,52299947</td>
</tr>
<tr>
<td style="text-align: center;">9</td>
<td style="text-align: center;"> San Alberto Magno</td>
<td style="text-align: center;">3,433028941</td>
<td style="text-align: center;">-76,52707643</td>
</tr>
<tr>
<td style="text-align: center;">10</td>
<td style="text-align: center;"> Santa María Goretty</td>
<td style="text-align: center;">3,433414486</td>
<td style="text-align: center;">-76,50720662</td>
</tr>
<tr>
<td style="text-align: center;">11</td>
<td style="text-align: center;"> San Alberto Magno</td>
<td style="text-align: center;">3,433157456</td>
<td style="text-align: center;">-76,5267331</td>
</tr>
<tr>
<td style="text-align: center;">12</td>
<td style="text-align: center;"> San Ignacio de Loyola</td>
<td style="text-align: center;">3,431786629</td>
<td style="text-align: center;">-76,51733464</td>
</tr>
<tr>
<td style="text-align: center;">13</td>
<td style="text-align: center;"> Nuestro Futuro</td>
<td style="text-align: center;">3,430629992</td>
<td style="text-align: center;">-76,50360174</td>
</tr>
<tr>
<td style="text-align: center;">14</td>
<td style="text-align: center;"> Sabio Caldas</td>
<td style="text-align: center;">3,429087807</td>
<td style="text-align: center;">-76,51660508</td>
</tr>
<tr>
<td style="text-align: center;">15</td>
<td style="text-align: center;"> CREAD</td>
<td style="text-align: center;">3,425060978</td>
<td style="text-align: center;">-76,51488847</td>
</tr>
<tr>
<td style="text-align: center;">16</td>
<td style="text-align: center;"> Licomtec</td>
<td style="text-align: center;">3,416664559</td>
<td style="text-align: center;">-76,51673383</td>
</tr>
<tr>
<td style="text-align: center;">17</td>
<td style="text-align: center;">  Nuestra Señora De La Providencia</td>
<td style="text-align: center;">3,419534772</td>
<td style="text-align: center;">-76,49591989</td>
</tr>
<tr>
<td style="text-align: center;">18</td>
<td style="text-align: center;"> Real Suizo</td>
<td style="text-align: center;">3,415208029</td>
<td style="text-align: center;">-76,49323768</td>
</tr>
<tr>
<td style="text-align: center;">19</td>
<td style="text-align: center;"> Nuevo Edén</td>
<td style="text-align: center;">3,415722099</td>
<td style="text-align: center;">-76,53383559</td>
</tr>
<tr>
<td style="text-align: center;">20</td>
<td style="text-align: center;"> Católico</td>
<td style="text-align: center;">3,413066071</td>
<td style="text-align: center;">-76,53984374</td>
</tr>
<tr>
<td style="text-align: center;">21</td>
<td style="text-align: center;"> Santa María Stella</td>
<td style="text-align: center;">3,427031556</td>
<td style="text-align: center;">-76,55134505</td>
</tr>
<tr>
<td style="text-align: center;">22</td>
<td style="text-align: center;"> Santa Isabel</td>
<td style="text-align: center;">3,40805355</td>
<td style="text-align: center;">-76,50817223</td>
</tr>
<tr>
<td style="text-align: center;">23</td>
<td style="text-align: center;"> Compartir</td>
<td style="text-align: center;">3,431957663</td>
<td style="text-align: center;">-76,47495575</td>
</tr>
<tr>
<td style="text-align: center;">24</td>
<td style="text-align: center;"> Lancaster</td>
<td style="text-align: center;">3,400770816</td>
<td style="text-align: center;">-76,55177421</td>
</tr>
<tr>
<td style="text-align: center;">25</td>
<td style="text-align: center;"> Parroquial Divino Salvador</td>
<td style="text-align: center;">3,397086588</td>
<td style="text-align: center;">-76,54259033</td>
</tr>
<tr>
<td style="text-align: center;">26</td>
<td style="text-align: center;"> Reyes Católicos</td>
<td style="text-align: center;">3,393316667</td>
<td style="text-align: center;">-76,53735466</td>
</tr>
<tr>
<td style="text-align: center;">27</td>
<td style="text-align: center;"> Liceo Anglo del Valle</td>
<td style="text-align: center;">3,387318719</td>
<td style="text-align: center;">-76,51975937</td>
</tr>
<tr>
<td style="text-align: center;">28</td>
<td style="text-align: center;"> Laurence</td>
<td style="text-align: center;">3,383420238</td>
<td style="text-align: center;">-76,52078934</td>
</tr>
<tr>
<td style="text-align: center;">29</td>
<td style="text-align: center;"> Los Almendros</td>
<td style="text-align: center;">3,381278208</td>
<td style="text-align: center;">-76,52023144</td>
</tr>
<tr>
<td style="text-align: center;">30</td>
<td style="text-align: center;"> Bautista</td>
<td style="text-align: center;">3,37720834</td>
<td style="text-align: center;">-76,52327843</td>
</tr>
<tr>
<td style="text-align: center;">31</td>
<td style="text-align: center;"> Lacordaire</td>
<td style="text-align: center;">3,378150837</td>
<td style="text-align: center;">-76,54460736</td>
</tr>
<tr>
<td style="text-align: center;">32</td>
<td style="text-align: center;"> General José María Córdoba</td>
<td style="text-align: center;">3,393573314</td>
<td style="text-align: center;">-76,54932805</td>
</tr>
<tr>
<td style="text-align: center;">33</td>
<td style="text-align: center;"> El Hogar</td>
<td style="text-align: center;">3,390745864</td>
<td style="text-align: center;">-76,5503151</td>
</tr>
<tr>
<td style="text-align: center;">34</td>
<td style="text-align: center;"> Americano</td>
<td style="text-align: center;">3,379093255</td>
<td style="text-align: center;">-76,54688187</td>
</tr>
<tr>
<td style="text-align: center;">35</td>
<td style="text-align: center;"> Santa Filomena</td>
<td style="text-align: center;">3,401969935</td>
<td style="text-align: center;">-76,51345082</td>
</tr>
<tr>
<td style="text-align: center;">36</td>
<td style="text-align: center;"> Tomás Vasconi</td>
<td style="text-align: center;">3,403040928</td>
<td style="text-align: center;">-76,5173132</td>
</tr>
<tr>
<td style="text-align: center;">37</td>
<td style="text-align: center;"> República del Salvador</td>
<td style="text-align: center;">3,404454636</td>
<td style="text-align: center;">-76,52143308</td>
</tr>
<tr>
<td style="text-align: center;">38</td>
<td style="text-align: center;"> Los Andes</td>
<td style="text-align: center;">3,429601077</td>
<td style="text-align: center;">-76,53761216</td>
</tr>
<tr>
<td style="text-align: center;">39</td>
<td style="text-align: center;">Villacolombia</td>
<td style="text-align: center;">3,445493943</td>
<td style="text-align: center;">-76,50169202</td>
</tr>
<tr>
<td style="text-align: center;">40</td>
<td style="text-align: center;">Las Américas</td>
<td style="text-align: center;">3,449220822</td>
<td style="text-align: center;">-76,50594064</td>
</tr>
<tr>
<td style="text-align: center;">41</td>
<td style="text-align: center;">Santa Fe</td>
<td style="text-align: center;">3,442238267</td>
<td style="text-align: center;">-76,50988885</td>
</tr>
<tr>
<td style="text-align: center;">42</td>
<td style="text-align: center;">Evaristo García</td>
<td style="text-align: center;">3,440781776</td>
<td style="text-align: center;">-76,51752778</td>
</tr>
<tr>
<td style="text-align: center;">43</td>
<td style="text-align: center;">Alfredo Vásquez Cobo</td>
<td style="text-align: center;">3,435598366</td>
<td style="text-align: center;">-76,5164549</td>
</tr>
<tr>
<td style="text-align: center;">44</td>
<td style="text-align: center;">Ciudad de Cali</td>
<td style="text-align: center;">3,431143181</td>
<td style="text-align: center;">-76,51272126</td>
</tr>
<tr>
<td style="text-align: center;">45</td>
<td style="text-align: center;">INEM</td>
<td style="text-align: center;">3,482761991</td>
<td style="text-align: center;">-76,49976083</td>
</tr>
<tr>
<td style="text-align: center;">46</td>
<td style="text-align: center;">Olaya Herrera</td>
<td style="text-align: center;">3,478178519</td>
<td style="text-align: center;">-76,51280709</td>
</tr>
<tr>
<td style="text-align: center;">47</td>
<td style="text-align: center;">Guillermo Valencia</td>
<td style="text-align: center;">3,47449459</td>
<td style="text-align: center;">-76,5136654</td>
</tr>
<tr>
<td style="text-align: center;">48</td>
<td style="text-align: center;">José Ignacio Rengifo</td>
<td style="text-align: center;">3,471624543</td>
<td style="text-align: center;">-76,5136654</td>
</tr>
<tr>
<td style="text-align: center;">49</td>
<td style="text-align: center;">Santo Tomás</td>
<td style="text-align: center;">3,45830227</td>
<td style="text-align: center;">-76,5164549</td>
</tr>
<tr>
<td style="text-align: center;">50</td>
<td style="text-align: center;">La Merced</td>
<td style="text-align: center;">3,46271449</td>
<td style="text-align: center;">-76,5024645</td>
</tr>
<tr>
<td style="text-align: center;">51</td>
<td style="text-align: center;">Pedro Antonio Molina</td>
<td style="text-align: center;">3,482804827</td>
<td style="text-align: center;">-76,48761579</td>
</tr>
<tr>
<td style="text-align: center;">52</td>
<td style="text-align: center;">Santa Librada</td>
<td style="text-align: center;">3,46228612</td>
<td style="text-align: center;">-76,52302095</td>
</tr>
<tr>
<td style="text-align: center;">53</td>
<td style="text-align: center;">República de Israel</td>
<td style="text-align: center;">3,463656904</td>
<td style="text-align: center;">-76,51053258</td>
</tr>
<tr>
<td style="text-align: center;">54</td>
<td style="text-align: center;">San Vicente Paul</td>
<td style="text-align: center;">3,466227117</td>
<td style="text-align: center;">-76,50950261</td>
</tr>
<tr>
<td style="text-align: center;">55</td>
<td style="text-align: center;">Manuel María Mallarino</td>
<td style="text-align: center;">3,456760129</td>
<td style="text-align: center;">-76,48851701</td>
</tr>
<tr>
<td style="text-align: center;">56</td>
<td style="text-align: center;">Sebastián de Belalcazar</td>
<td style="text-align: center;">3,460229941</td>
<td style="text-align: center;">-76,48521253</td>
</tr>
<tr>
<td style="text-align: center;">57</td>
<td style="text-align: center;">Liceo Departamental</td>
<td style="text-align: center;">3,423860462</td>
<td style="text-align: center;">-76,5385563</td>
</tr>
<tr>
<td style="text-align: center;">58</td>
<td style="text-align: center;">Libardo Madrid</td>
<td style="text-align: center;">3,422061154</td>
<td style="text-align: center;">-76,54383489</td>
</tr>
<tr>
<td style="text-align: center;">59</td>
<td style="text-align: center;">Metropolitano Santa Anita</td>
<td style="text-align: center;">3,401691038</td>
<td style="text-align: center;">-76,54218265</td>
</tr>
<tr>
<td style="text-align: center;">60</td>
<td style="text-align: center;">San José</td>
<td style="text-align: center;">3,396935816</td>
<td style="text-align: center;">-76,55031511</td>
</tr>
</tbody>
</table>
<p>Se desea desarrollar un plan de rutas para cada uno de los vehículos que logre minimizar la distancia total recorrida, al tiempo que asegure la visita de todos los colegios.</p>
<h3>¿Qué necesitaremos?</h3>

		<div id="que-necesitaremos" data-title="¿Qué necesitaremos?" class="index-title"></div>
	
<p>En el desarrollo de este ejercicio emplearemos:</p>

		<div class="plus tie-list-shortcode">
<div class="plus tie-list-shortcode">
<ul>
<li><em><strong>Colaboratory</strong>:<span> </span></em>Este es un entorno de programación y ejecución virtual de Python desarrollado por Google. Nos permitirá no tener la necesidad de realizar ninguna instalación en nuestros equipos. Todo lo que desarrollemos lo ejecutaremos en un cuaderno virtual.</li>
<li><strong><em>Python</em></strong>: Este será el lenguaje de programación que vamos a utilizar, y advertimos: No es necesario tener conocimientos previos, y el objetivo del artículo no es convertirnos en programadores expertos. Utilizaremos fragmentos de códigos, librerías disponibles, y explicaremos lo necesario para configurar nuestro desarrollo de acuerdo a los objetivos específicos de nuestros modelos.</li>
<li><strong><em>SkLearn</em></strong>: Las librerías son a <em>Python</em>, lo que las <em>apps<span> </span></em>son a un teléfono celular. Esta es quizá una de las características más a tractivas de este lenguaje: Casi que existe una librería para cada necesidad. En este caso, <em>SKLearn</em>, es una librería que integra un conjunto de métodos de aprendizaje automático y minería de datos. En este caso utilizaremos sus funciones para cálculo de distancias entre pares.</li>
<li><strong><em>Pandas</em></strong>:<span> </span><span>Es un paquete de Python que proporciona estructuras de datos rápidas, y flexibles, diseñadas para que el trabajo con datos estructurados (tabulares, multidimensionales, potencialmente heterogéneos) y de series de tiempo sea fácil e intuitivo.</span></li>
<li><em><strong>Numpy</strong>: </em>Es una librería que nos permitirá efectuar operaciones matriciales en Python.</li>
<li><strong><em>Math: </em></strong>Es una librería que contiene un conjunto de funciones matemáticas básicas.</li>
</ul>

		</div>
	
</div>
<h3>Paso 1: Crear el entorno de trabajo en Colaboratory</h3>

		<div id="paso-1-crear-el-entorno-de-trabajo-en-colaboratory" data-title="Paso 1: Crear el entorno de trabajo en Colaboratory" class="index-title"></div>
	
<div id="paso-1-crear-el-entorno-de-trabajo-en-colaboratory" data-title="Paso 1: Crear el entorno de trabajo en Colaboratory" class="index-title visible full-visible"></div>
<p><span>Lo primero que vamos a hacer consiste en crear un entorno de trabajo en <em>Google</em> </span><em>Colaboratory</em><span>, así que vayamos allá: </span><a href="https://colab.research.google.com/#create=true" target="_blank" rel="noopener"><em><strong>Abrir cuaderno nuevo</strong></em></a><span>.</span></p>
<p>Verán que tienen un lienzo para programar el modelo, así que en este cuaderno podemos ir generando las líneas de código que explicaremos en los pasos siguientes.</p>
<h3>Paso 2: Importar las librerías necesarias</h3>

		<div id="paso-2-importar-las-librerias-necesarias" data-title="Paso 2: Importar las librerías necesarias" class="index-title"></div>
	
<div id="paso-2-importar-las-librerias-necesarias" data-title="Paso 2: Importar las librerías necesarias" class="index-title visible full-visible"></div>
<p>Respecto a las librerías, en la introducción del artículo hicimos una descripción de la funcionalidad de cada una, veamos como importarlas en nuestro entorno:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>from sklearn.neighbors import DistanceMetric
from math import radians
import pandas as pd
import numpy as np</code></pre>
</div>
<p><span>De esta manera, tenemos todo lo necesario para empezar a desarrollar nuestro código.</span></p>
<h3>Paso 3: Importar los datos desde Excel</h3>

		<div id="paso-3-importar-los-datos-desde-excel" data-title="Paso 3: Importar los datos desde Excel" class="index-title"></div>
	
<div id="paso-3-importar-los-datos-desde-excel" data-title="Paso 3: Importar los datos desde Excel" class="index-title visible full-visible"></div>
<p>De acuerdo a las necesidades del modelo, podemos desarrollar un código que permita la entrada manual de la información, la captura de los datos desde entornos digitales (Internet, por ejemplo), o podemos, desde luego, alimentar nuestro modelo con información contenida en documentos externos, como es el caso de un archivo de Microsoft Excel.</p>
<p>Esta puede considerarse como una de las ventajas de utilizar <em>Python, </em>su capacidad de integrarse con cualquier fuente de datos. En nuestro caso, toda la información se encuentra contenida en un documento de Excel, el cual presenta el siguiente formato:</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/11/datos_colegio.png" alt="datos_colegio" width="577" height="241" class="size-full wp-image-28867 aligncenter" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/11/datos_colegio.png 577w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/11/datos_colegio-300x125.png 300w" sizes="(max-width: 577px) 100vw, 577px" /></p>
<p>Utilizaremos ubicaciones reales, y para eso emplearemos las coordenadas de <em>latitud </em>y <em>longitud</em>.</p>

		<div class="box download  ">
			<div class="box-inner-block">
				<span class="fa tie-shortcode-boxicon"></span><span>Puedes descargar el documento de Excel que utilizamos en este ejemplo: </span><a href="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/11/colegios.xlsx" target="_blank" rel="noopener"><strong>Base de datos</strong></a>
			</div>
		</div>
	
<p><span>En </span><em>Colaboratory</em><span>, el siguiente fragmento permitirá cargar un archivo al entorno de ejecución:</span></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>from google.colab import files

uploaded = files.upload()

for fn in uploaded.keys():
  print('User uploaded file "{name}" with length {length} bytes'.format(
      name=fn, length=len(uploaded[fn])))</code></pre>
</div>
<p>Al ejecutar este fragmento de código, se abrirá una ventana emergente del explorador que permitirá cargar nuestra base de datos, en nuestro caso el archivo tienen el nombre de <em>colegios.xlsx.</em></p>
<p>La siguiente línea de código permitirá almacenar los datos contenidos en el documento en un <em>Dataframe</em><span> </span>de nuestro entorno, dentro de la variable <em>data</em>.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Leer el documento de Excel y almacenar los datos en la variable data
data = pd.read_excel('colegios.xlsx')</code></pre>
</div>
<p><span>Podemos en cualquier momento confirmar si la carga de los datos se ha realizado correctamente, para eso imprimiremos las primeras cinco filas del  </span><em>DataFrame:</em></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>data.head()</code></pre>
</div>
<p><span>Al ejecutar esta instrucción tenemos la siguiente salida (Una vista de las 5 primeras filas del marco de datos):</span></p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/11/data_head.png" alt="data_head" width="534" height="257" class="size-full wp-image-28869 aligncenter" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/11/data_head.png 534w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/11/data_head-300x144.png 300w" sizes="(max-width: 534px) 100vw, 534px" /></p>
<p>Podemos observar que los datos han sido perfectamente cargados, y que ahora se encuentran almacenados en la variable (<em>DataFrame</em>): <em><strong>data</strong></em>.</p>
<h3>Paso 4: Convertir las coordenadas de latitud y longitud en radianes</h3>

		<div id="paso-4-convertir-las-coordenadas-de-latitud-y-longitud-en-radianes" data-title="Paso 4: Convertir las coordenadas de latitud y longitud en radianes" class="index-title"></div>
	
<p>La mayor parte de las funciones de cálculo de distancias de <em>Sklearn</em> (<em>Scipy</em>) toman las entradas como radianes. Por esta razón, debemos convertir las coordenadas en radianes.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>data['Latitud'] = np.radians(data['Latitud'])
data['Longitud'] = np.radians(data['Longitud'])

#Creamos una matriz bidimensional con la latitud y la longitud en radianes
data[['Latitud','Longitud']].to_numpy()</code></pre>
</div>
<p>Al ejecutar estas líneas, las coordenadas quedarán convertidas en radianes.</p>
<h3>Paso 5: Declarar el tipo de métrica de distancias que se utilizará</h3>

		<div id="paso-5-declarar-el-tipo-de-metrica-de-distancias-que-se-utilizara" data-title="Paso 5: Declarar el tipo de métrica de distancias que se utilizará" class="index-title"></div>
	
<p>En este punto quiero detenerme para mencionar que existen decenas de funciones métricas de distancia rápida. Algunas de las más utilizadas son destinadas a espacios vectoriales de valor real, como: <em>distancias euclidianas, distancias de Manhattan, </em>etc. Prácticas cuando se emplean con coordenadas cartesianas.</p>
<p>En nuestro caso, ya que utilizamos ubicaciones reales y contamos con coordenadas de latitud y de longitud, podemos emplear una función de distancia de vectores bidimensionales que considere la curvatura de la tierra; tal es el caso de las <em>Distancias Haversine (Semiverseno)</em>.</p>

		<div class="box note  ">
			<div class="box-inner-block">
				<span class="fa tie-shortcode-boxicon"></span>La distancia de Haversine (o gran círculo) es la distancia angular entre dos puntos en la superficie de una esfera. Se supone que la primera coordenada de cada punto es la latitud, la segunda es la longitud, expresada en radianes.</p>
<p>Como la Tierra es casi esférica, la fórmula Haversine proporciona una buena aproximación de la distancia entre dos puntos de la superficie terrestre, con un error de menos del 1% en promedio.
			</div>
		</div>
	
<p>Si quieren conocer la fórmula empleada para el cálculo de cada distancia <em>haversine</em>:</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/11/haversine.png" alt="haversine" width="596" height="54" class="size-full wp-image-28870 aligncenter" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/11/haversine.png 596w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/11/haversine-300x27.png 300w" sizes="(max-width: 596px) 100vw, 596px" /></p>
<p>Para efectos de nuestro desarrollo, utilizaremos la librería <em>SKLearn</em> para calcular nuestras distancias. Veamos:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>dist = DistanceMetric.get_metric('haversine')

dist.pairwise(data[['Latitud','Longitud']].to_numpy())*6373</code></pre>
</div>
<p>El fragmento anterior crea un objeto con métricas <em>haversine</em>, y luego utiliza la función <em>pairwise()</em>, para calcular la distancia entre cada uno de los elementos de la matriz (nodos). Cada distancia calculada multiplica el escalar 6373 (radio esférico de la tierra), para calcular las distancias en kilómetros (Para millas multiplicar por 3798).</p>
<h3>Paso 6: Crear un marco de datos (tabulado) de matriz de distancias</h3>

		<div id="paso-6-crear-un-marco-de-datos-tabulado-de-matriz-de-distancias" data-title="Paso 6: Crear un marco de datos (tabulado) de matriz de distancias" class="index-title"></div>
	
<p>Una vez que ejecutemos el paso anterior, tendremos nuestras distancias entre nodos calculadas; lo que haremos en este paso será organizar dichos valores en forma de marco de datos o tabulado, obteniendo nuestra matriz de distancias.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>distance_matrix = pd.DataFrame(dist.pairwise(data[['Latitud','Longitud']].to_numpy())*6373)

distance_matrix.head()</code></pre>
</div>
<p><span>Al ejecutar esta instrucción tenemos la siguiente salida (Una vista de las 5 primeras filas del marco de datos):</span></p>
<figure id="attachment_28871" aria-describedby="caption-attachment-28871" style="width: 601px" class="wp-caption aligncenter"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/11/matriz_distancias.png" alt="matriz_distancias" width="601" height="274" class="size-full wp-image-28871" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/11/matriz_distancias.png 601w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/11/matriz_distancias-300x137.png 300w" sizes="(max-width: 601px) 100vw, 601px" /><figcaption id="caption-attachment-28871" class="wp-caption-text">Vista recortada</figcaption></figure>
<p>&nbsp;</p>
<p>Podemos imprimir la matriz completa:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>print(distance_matrix)</code></pre>
</div>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/11/matriz_distancias_2.png" alt="matriz_distancias_2" width="624" height="304" class="size-full wp-image-28872 aligncenter" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/11/matriz_distancias_2.png 624w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/11/matriz_distancias_2-300x146.png 300w" sizes="(max-width: 624px) 100vw, 624px" /></p>

		<div class="box note  ">
			<div class="box-inner-block">
				<span class="fa tie-shortcode-boxicon"></span>Puedes utilizar Google Maps para validar la diferencia entre las distancias obtenidas mediante Haversine y las obtenidas mediante Google.
			</div>
		</div>
	
<hr />
<p>Hasta este punto hemos logrado nuestro objetivo principal, que era <strong>obtener una matriz de distancias</strong> de forma automática tomando como base las coordenadas de longitud y latitud de un conjunto de nodos. Específicamente hemos obtenido 3721 valores de distancia en cuestión de segundos (empleando la métrica <em>haversine</em>).</p>
<p>Lógicamente, estos valores de referencia en la práctica presentan algunas desventajas, como, por ejemplo, la dificultad subyacente de atravesar las ciudades por lugares diferentes que las vías dispuestas para ello. Sin embargo, estos valores pueden ser muy útiles como datos de entrada de un modelo VRP para obtener la secuencia del plan de rutas.</p>

		<div class="box info  ">
			<div class="box-inner-block">
				<span class="fa tie-shortcode-boxicon"></span>Un método más preciso para obtener una matriz de distancias y tiempos consiste en utilizar la API Distance Matrix de Google Maps. Sin embargo, este es un servicio de pago que abordaremos en artículos posteriores
			</div>
		</div>
	
<hr />
<p>Lo siguiente que haremos, para finalizar nuestro caso de aplicación, será utilizar la matriz de distancias obtenida, en un <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problema-de-enrutamiento-de-vehiculos-vrp-con-google-or-tools/"><strong>modelo VRP básico</strong></a> (Si desea profundizar en el modelo, visite el artículo).</p>
<h3>Paso 7: Instalar Google Or Tools</h3>

		<div id="paso-7-instalar-google-or-tools" data-title="Paso 7: Instalar Google Or Tools" class="index-title"></div>
	
<p>Es necesario instalar la librería de Google Or Tools en nuestro entorno de <em>Colaboratory</em> para poder utilizar nuestro modelo VRP.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>!pip install ortools</code></pre>
</div>
<p>Al ejecutar esta instrucción instalaremos nuestro solucionador del modelo de enrutamiento.</p>
<h3>Paso 8: Incorporar el modelo VRP (Previamente formulado)</h3>

		<div id="paso-8-incorporar-el-modelo-vrp-previamente-formulado" data-title="Paso 8: Incorporar el modelo VRP (Previamente formulado)" class="index-title"></div>
	
<p>Como ya lo mencionamos, contamos con un modelo debidamente formulado para resolver problemas VRP básicos. Lo único que modificaremos serán los datos de entrada: <em>Matriz de distancias (distance_matrix), Número de vehículos (2), Depósito (Nodo 0).</em></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>"""Problema de enrutamiento de vehículos simple (VRP)

Autor: MSc. Ing. Bryan Salazar López 2021

"""

from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp


def create_data_model():
    """Datos de entrada del modelo"""
    #Llamaremos la matriz de distancia previamente obtenida
    #Emplearemos dos vehículos como lo indica el problema
    #Definiremos el nodo 0 como el depósito (Secretaría)
    data = {}
    data['matriz_distancias'] = distance_matrix
    data['num_vehiculos'] = 2
    data['deposito'] = 0
    return data


def print_solution(data, manager, routing, solution):
    """Imprime la solución sobre la consola"""
    max_route_distance = 0
    for vehicle_id in range(data['num_vehiculos']):
        index = routing.Start(vehicle_id)
        plan_output = 'Ruta para el vehículo {}:\n'.format(vehicle_id)
        route_distance = 0
        while not routing.IsEnd(index):
            plan_output += ' {} -&gt; '.format(manager.IndexToNode(index))
            previous_index = index
            index = solution.Value(routing.NextVar(index))
            route_distance += routing.GetArcCostForVehicle(
                previous_index, index, vehicle_id)
        plan_output += '{}\n'.format(manager.IndexToNode(index))
        plan_output += 'Distancia de la ruta: {}km\n'.format(route_distance)
        print(plan_output)
        max_route_distance += route_distance
        max_route_distance = max(route_distance, max_route_distance)
    print('Distancia total de todas las rutas: {}km'.format(max_route_distance))



def main():
    """Punto de entrada del programa"""
    # Invocar la data de entrada.
    data = create_data_model()

    # Crea el administrador del índice de rutas.
    manager = pywrapcp.RoutingIndexManager(len(data['matriz_distancias']),
                                           data['num_vehiculos'], data['deposito'])

    # Crea el modelo de enrutamiento.
    routing = pywrapcp.RoutingModel(manager)


    # Crea y registra una devolución de llamada de distancia.
    def distance_callback(from_index, to_index):
        """Retorna la distancia entre dos nodos."""
        # Convierte desde la variable de ruta Index hasta la matriz de distancia NodeIndex.
        from_node = manager.IndexToNode(from_index)
        to_node = manager.IndexToNode(to_index)
        return data['matriz_distancias'][from_node][to_node]

    transit_callback_index = routing.RegisterTransitCallback(distance_callback)

    # Define el costo de cada arco.
    routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)

    # Adhiere la dimensión de distancia.
    dimension_name = 'Distancia'
    routing.AddDimension(
        transit_callback_index,
        0,  # Sin holgura
        3000,  # Distancia máxima de viaje del vehículo
        True,  # Iniciar el acumulador en cero
        dimension_name)
    distance_dimension = routing.GetDimensionOrDie(dimension_name)
    distance_dimension.SetGlobalSpanCostCoefficient(100)

    # Configurar los parámetros de búsqueda.
    search_parameters = pywrapcp.DefaultRoutingSearchParameters()
    search_parameters.first_solution_strategy = (
        routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)

    # Solucionador del problema.
    solution = routing.SolveWithParameters(search_parameters)

    # Imprimir la solución en la consola.
    if solution:
        print_solution(data, manager, routing, solution)
    else:
        print('No se encuentra solución !')


if __name__ == '__main__':
    main()</code></pre>
</div>
<p>Al ejecutar el modelo tendremos:</p>
<figure id="attachment_28873" aria-describedby="caption-attachment-28873" style="width: 1736px" class="wp-caption aligncenter"><a href="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/11/vrp_rutas.png" target="_blank" rel="noopener"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/11/vrp_rutas.png" alt="vrp_rutas" width="1736" height="176" class="wp-image-28873 size-full" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/11/vrp_rutas.png 1736w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/11/vrp_rutas-300x30.png 300w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/11/vrp_rutas-1024x104.png 1024w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/11/vrp_rutas-768x78.png 768w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/11/vrp_rutas-1536x156.png 1536w" sizes="(max-width: 1736px) 100vw, 1736px" /></a><figcaption id="caption-attachment-28873" class="wp-caption-text">Clic para ver en una pestaña nueva</figcaption></figure>
<p>&nbsp;</p>
<p>Un plan de rutas para los dos vehículos que parten desde los depósitos (ahí mismo finalizan sus recorridos), y visitan la totalidad de los nodos. La distancia total optimizada es equivalente a 33 km.</p>
<p>Por último, también es posible exportar la <strong>matriz de distancias</strong> que hemos obtenido, para eso utilizaremos el siguiente fragmento:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>distance_matrix.to_csv('distance_matrix.csv')
files.download('distance_matrix.csv')</code></pre>
</div>

		<div class="box info  ">
			<div class="box-inner-block">
				<span class="fa tie-shortcode-boxicon"></span>
<p><span>Los contribuciones de la comunidad han revelado que el modelo VRP de Or Tools maneja valores enteros para las distancias en la matriz. Esto significa que, por ejemplo, una distancia de 0.58 km se trataría como un entero, lo que puede causar pérdida de precisión en los resultados. Por esta razón, recomendamos trabajar con la unidad más pequeña de longitud posible, como metros, y convertir las distancias antes de ejecutar el modelo de ruteo. Por ejemplo, 0.58 km se convertiría en 580 metros, evitando pérdida de precisión. Además, es importante tener en cuenta que esto afectaría la distancia máxima del viaje del vehículo, la cual debemos modificar, en consecuencia.</span></p>

			</div>
		</div>
	
<hr />
<p>El código completo de este desarrollo lo puedes encontrar en nuestro cuaderno: <a href="https://colab.research.google.com/drive/1YZIpYWOrTGBeGwRl9iOhWAwNH5WAVpWw?usp=sharing" target="_blank" rel="noopener"><em><strong>Matriz de distancias para modelar un VRP</strong></em></a>.</p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/como-calcular-una-matriz-de-distancias-para-modelar-un-vrp/">¿Cómo calcular una matriz de distancias para modelar un VRP?</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/como-calcular-una-matriz-de-distancias-para-modelar-un-vrp/feed/</wfw:commentRss>
			<slash:comments>6</slash:comments>
		
		
			</item>
	</channel>
</rss>
