0% found this document useful (0 votes)
161 views

Finding Optimal Locations of New Stores Using DO - Ipynb

Uploaded by

Yusuf Erdem
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
161 views

Finding Optimal Locations of New Stores Using DO - Ipynb

Uploaded by

Yusuf Erdem
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 12

{

"cells": [
{
"cell_type": "markdown",
"metadata": {
"render": false
},
"source": [
"# Finding Optimal Locations for New Stores\n",
"\n",
"This notebook is an example of how **Decision Optimization** can help to
prescribe decisions for a complex constrained problem.\n",
"\n",
"When you finish this notebook, you'll have a foundational knowledge of
_Prescriptive Analytics_.\n",
"\n",
">This notebook requires the Commercial Edition of CPLEX engines, which is
included in the Default Python 3.7 XS + DO in Watson Studio.\n",
"\n",
"Table of contents:\n",
"\n",
"- [Describe the business problem](#Describe-the-business-problem)\n",
"* [How decision optimization (prescriptive analytics) can help](#How--
decision-optimization-can-help)\n",
"* [Use decision optimization](#Use-decision-optimization)\n",
" * [Step 1: Import the docplex package](#Step-1:-Import-the-docplex-
package)\n",
" - [Step 2: Model the data](#Step-2:-Model-the-data)\n",
" * [Step 3: Prepare the data](#Step-3:-Prepare-the-data)\n",
" - [Step 4: Set up the prescriptive model](#Step-4:-Set-up-the-
prescriptive-model)\n",
" * [Define the decision variables](#Define-the-decision-variables)\n",
" * [Express the business constraints](#Express-the-business-
constraints)\n",
" * [Express the objective](#Express-the-objective)\n",
" * [Solve with the Decision Optimization solve service](#Solve-with-
the-Decision-Optimization-solve-service)\n",
" * [Step 5: Investigate the solution and run an example analysis](#Step-
5:-Investigate-the-solution-and-then-run-an-example-analysis)\n",
"* [Summary](#Summary)\n",
"\n",
"****"
]
},
{
"cell_type": "markdown",
"metadata": {
"render": false
},
"source": [
"## Describe the business problem\n",
"\n",
"* A fictional Coffee Company plans to open N shops in the near future and
needs to determine where they should be located knowing the following:\n",
" * Most of the customers of this coffee brewer enjoy reading and borrowing
books, so the goal is to locate those shops in such a way that all the city public
libraries are within minimal walking distance.\n",
"* We use <a href=\"https://data.cityofchicago.org\" target=\"_blank\"
rel=\"noopener noreferrer\">Chicago open data</a> for this example.\n",
"* We implement a <a href=\"https://en.wikipedia.org/wiki/K-
medians_clustering\" target=\"_blank\" rel=\"noopener noreferrer\">K-Median
model</a> to get the optimal location of our future shops."
]
},
{
"cell_type": "markdown",
"metadata": {
"render": false
},
"source": [
"## How decision optimization can help\n",
"\n",
"* Prescriptive analytics (decision optimization) technology recommends actions
that are based on desired outcomes. It takes into account specific scenarios,
resources, and knowledge of past and current events. With this insight, your
organization can make better decisions and have greater control of business
outcomes. \n",
"\n",
"* Prescriptive analytics is the next step on the path to insight-based
actions. It creates value through synergy with predictive analytics, which analyzes
data to predict future outcomes. \n",
"\n",
"* Prescriptive analytics takes that insight to the next level by suggesting
the optimal way to handle that future situation. Organizations that can act fast in
dynamic conditions and make superior decisions in uncertain environments gain a
strong competitive advantage. \n",
"<br/>\n",
"\n",
"With prescriptive analytics, you can: \n",
"\n",
"* Automate the complex decisions and trade-offs to better manage your limited
resources.\n",
"* Take advantage of a future opportunity or mitigate a future risk.\n",
"* Proactively update recommendations based on changing events.\n",
"* Meet operational goals, increase customer loyalty, prevent threats and
fraud, and optimize business processes."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Use decision optimization"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Step 1: Import the docplex package \n",
"\n",
"This package is presintalled on Watson Studio."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import sys\n",
"import docplex.mp"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<i>Note that the more global package docplex contains another subpackage
docplex.cp that is dedicated to Constraint Programming, another branch of
optimization.</i>"
]
},
{
"cell_type": "markdown",
"metadata": {
"render": false
},
"source": [
"### Step 2: Model the data\n",
"\n",
"- The data for this problem is quite simple: it is composed of the list of
public libraries and their geographical locations.\n",
"- The data is acquired from <a href=\"https://data.cityofchicago.org\"
target=\"_blank\" rel=\"noopener noreferrer\">Chicago open data</a> as a JSON file,
which is in the following format:\n",
"<code>\n",
"data\" : [ [ 1, \"13BFA4C7-78CE-4D83-B53D-B57C60B701CF\", 1,
1441918880, \"885709\", 1441918880, \"885709\", null, \"Albany Park\", \"M, W:
10AM-6PM; TU, TH: 12PM-8PM; F, SA: 9AM-5PM; SU:
Closed\", \"Yes\", \"Yes \", \"3401 W. Foster
Avenue\", \"CHICAGO\", \"IL\", \"60625\", \"(773) 539-5450\", [
\"http://www.chipublib.org/locations/1/\", null ], [ null, \"41.975456\", \"-
87.71409\", null, false ] ]\n",
"</code>\n",
"This code snippet represents library \"**3401 W. Foster Avenue**\" located at
**41.975456, -87.71409**\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"render": false
},
"source": [
"### Step 3: Prepare the data\n",
"We need to collect the list of public libraries locations and keep their
names, latitudes, and longitudes."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Store longitude, latitude and street crossing name of each public library
location.\n",
"class XPoint(object):\n",
" def __init__(self, x, y):\n",
" self.x = x\n",
" self.y = y\n",
" def __str__(self):\n",
" return \"P(%g_%g)\" % (self.x, self.y)\n",
"\n",
"class NamedPoint(XPoint):\n",
" def __init__(self, name, x, y):\n",
" XPoint.__init__(self, x, y)\n",
" self.name = name\n",
" def __str__(self):\n",
" return self.name"
]
},
{
"cell_type": "markdown",
"metadata": {
"render": false
},
"source": [
"#### Define how to compute the earth distance between 2 points\n",
"To easily compute distance between 2 points, we use the Python package <a
href=\"https://pypi.python.org/pypi/geopy\" target=\"_blank\" rel=\"noopener
noreferrer\">geopy</a>"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"try:\n",
" import geopy.distance\n",
"except:\n",
" if hasattr(sys, 'real_prefix'):\n",
" #we are in a virtual env.\n",
" !pip install geopy \n",
" else:\n",
" !pip install --user geopy"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Simple distance computation between 2 locations.\n",
"from geopy.distance import great_circle\n",
" \n",
"def get_distance(p1, p2):\n",
" return great_circle((p1.y, p1.x), (p2.y, p2.x)).miles"
]
},
{
"cell_type": "markdown",
"metadata": {
"render": false
},
"source": [
"#### Declare the list of libraries\n",
"Parse the JSON file to get the list of libraries and store them as Python
elements."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"render": false
},
"outputs": [],
"source": [
"def build_libraries_from_url(url, name_pos, lat_long_pos):\n",
" import requests\n",
" import json\n",
"\n",
" r = requests.get(url)\n",
" myjson = json.loads(r.text, parse_constant='utf-8')\n",
" myjson = myjson['data']\n",
"\n",
" libraries = []\n",
" k = 1\n",
" for location in myjson:\n",
" uname = location[name_pos]\n",
" try:\n",
" latitude = float(location[lat_long_pos][1])\n",
" longitude = float(location[lat_long_pos][2])\n",
" except TypeError:\n",
" latitude = longitude = None\n",
" try:\n",
" name = str(uname)\n",
" except:\n",
" name = \"???\"\n",
" name = \"P_%s_%d\" % (name, k)\n",
" if latitude and longitude:\n",
" cp = NamedPoint(name, longitude, latitude)\n",
" libraries.append(cp)\n",
" k += 1\n",
" return libraries"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"libraries =
build_libraries_from_url('https://data.cityofchicago.org/api/views/x8fc-8rcq/
rows.json?accessType=DOWNLOAD',\n",
" name_pos=10,\n",
" lat_long_pos=16)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"render": false
},
"outputs": [],
"source": [
"print(\"There are %d public libraries in Chicago\" % (len(libraries)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Define number of shops to open\n",
"Create a constant that indicates how many coffee shops we would like to open."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"render": false
},
"outputs": [],
"source": [
"nb_shops = 5\n",
"print(\"We would like to open %d coffee shops\" % nb_shops)"
]
},
{
"cell_type": "markdown",
"metadata": {
"render": false
},
"source": [
"#### Validate the data by displaying them\n",
"We will use the <a
href=\"https://folium.readthedocs.org/en/latest/quickstart.html#getting-started\"
target=\"_blank\" rel=\"noopener noreferrer\">folium</a> library to display a map
with markers."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"try:\n",
" import folium\n",
"except:\n",
" if hasattr(sys, 'real_prefix'):\n",
" #we are in a virtual env.\n",
" !pip install folium \n",
" else:\n",
" !pip install folium"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"import folium\n",
"map_osm = folium.Map(location=[41.878, -87.629], zoom_start=11)\n",
"for library in libraries:\n",
" lt = library.y\n",
" lg = library.x\n",
" folium.Marker([lt, lg]).add_to(map_osm)\n",
"map_osm"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"After running the above code, the data is displayed but it is impossible to
determine where to ideally open the coffee shops by just looking at the map.\n",
"\n",
"Let's set up DOcplex to write and solve an optimization model that will help
us determine where to locate the coffee shops in an optimal way."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Step 4: Set up the prescriptive model"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from docplex.mp.environment import Environment\n",
"env = Environment()\n",
"env.print_information()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Create the DOcplex model\n",
"The model contains all the business constraints and defines the objective."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from docplex.mp.model import Model\n",
"\n",
"mdl = Model(\"coffee shops\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Define the decision variables"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"BIGNUM = 999999999\n",
"\n",
"# Ensure unique points\n",
"libraries = set(libraries)\n",
"# For simplicity, let's consider that coffee shops candidate locations are the
same as libraries locations.\n",
"# That is: any library location can also be selected as a coffee shop.\n",
"coffeeshop_locations = libraries\n",
"\n",
"# Decision vars\n",
"# Binary vars indicating which coffee shop locations will be actually
selected\n",
"coffeeshop_vars = mdl.binary_var_dict(coffeeshop_locations,
name=\"is_coffeeshop\")\n",
"#\n",
"# Binary vars representing the \"assigned\" libraries for each coffee shop\n",
"link_vars = mdl.binary_var_matrix(coffeeshop_locations, libraries, \"link\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Express the business constraints\n",
"First constraint: if the distance is suspect, it needs to be excluded from the
problem."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for c_loc in coffeeshop_locations:\n",
" for b in libraries:\n",
" if get_distance(c_loc, b) >= BIGNUM:\n",
" mdl.add_constraint(link_vars[c_loc, b] == 0, \"ct_forbid_{0!s}_{1!
s}\".format(c_loc, b))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Second constraint: each library must be linked to a coffee shop that is open."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"mdl.add_constraints(link_vars[c_loc, b] <= coffeeshop_vars[c_loc]\n",
" for b in libraries\n",
" for c_loc in coffeeshop_locations)\n",
"mdl.print_information()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Third constraint: each library is linked to exactly one coffee shop."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"mdl.add_constraints(mdl.sum(link_vars[c_loc, b] for c_loc in
coffeeshop_locations) == 1\n",
" for b in libraries)\n",
"mdl.print_information()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Fourth constraint: there is a fixed number of coffee shops to open."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Total nb of open coffee shops\n",
"mdl.add_constraint(mdl.sum(coffeeshop_vars[c_loc] for c_loc in
coffeeshop_locations) == nb_shops)\n",
"\n",
"# Print model information\n",
"mdl.print_information()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Express the objective\n",
"\n",
"The objective is to minimize the total distance from libraries to coffee shops
so that a book reader always gets to our coffee shop easily.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Minimize total distance from points to hubs\n",
"total_distance = mdl.sum(link_vars[c_loc, b] * get_distance(c_loc, b) for
c_loc in coffeeshop_locations for b in libraries)\n",
"mdl.minimize(total_distance)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Solve with the Decision Optimization solve service\n",
"\n",
"Solve the model."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(\"# coffee shops locations = %d\" % len(coffeeshop_locations))\n",
"print(\"# coffee shops = %d\" % nb_shops)\n",
"\n",
"assert mdl.solve(), \"!!! Solve of the model fails\""
]
},
{
"cell_type": "markdown",
"metadata": {
"render": false
},
"source": [
"### Step 5: Investigate the solution and then run an example analysis\n",
"\n",
"The solution can be analyzed by displaying the location of the coffee shops on
a map."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"render": false
},
"outputs": [],
"source": [
"total_distance = mdl.objective_value\n",
"open_coffeeshops = [c_loc for c_loc in coffeeshop_locations if
coffeeshop_vars[c_loc].solution_value == 1]\n",
"not_coffeeshops = [c_loc for c_loc in coffeeshop_locations if c_loc not in
open_coffeeshops]\n",
"edges = [(c_loc, b) for b in libraries for c_loc in coffeeshop_locations if
int(link_vars[c_loc, b]) == 1]\n",
"\n",
"print(\"Total distance = %g\" % total_distance)\n",
"print(\"# coffee shops = {0}\".format(len(open_coffeeshops)))\n",
"for c in open_coffeeshops:\n",
" print(\"new coffee shop: {0!s}\".format(c))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Displaying the solution\n",
"Coffee shops are highlighted in red."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"import folium\n",
"map_osm = folium.Map(location=[41.878, -87.629], zoom_start=11)\n",
"for coffeeshop in open_coffeeshops:\n",
" lt = coffeeshop.y\n",
" lg = coffeeshop.x\n",
" folium.Marker([lt, lg], icon=folium.Icon(color='red',icon='info-
sign')).add_to(map_osm)\n",
" \n",
"for b in libraries:\n",
" if b not in open_coffeeshops:\n",
" lt = b.y\n",
" lg = b.x\n",
" folium.Marker([lt, lg]).add_to(map_osm)\n",
" \n",
"\n",
"for (c, b) in edges:\n",
" coordinates = [[c.y, c.x], [b.y, b.x]]\n",
" map_osm.add_child(folium.PolyLine(coordinates, color='#FF0000',
weight=5))\n",
"\n",
"map_osm"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"## Summary\n",
"\n",
"You have learned how to set up, formulate and solve an optimization model
using Decision Optimization in Watson Studio."
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"#### References\n",
"* <a href=\"https://rawgit.com/IBMDecisionOptimization/docplex-doc/master/
docs/index.html\" target=\"_blank\" rel=\"noopener noreferrer\">Decision
Optimization CPLEX Modeling for Python documentation</a>\n",
"* <a href=\"https://dataplatform.cloud.ibm.com/docs/content/wsj/getting-
started/welcome-main.html\" target=\"_blank\" rel=\"noopener noreferrer\">Cloud Pak
for Data documentation</a>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Copyright © 2017-2021 IBM. This notebook and its source code are released
under the terms of the MIT License."
]
}
],
"metadata": {
"celltoolbar": "Dashboard",
"kernelspec": {
"display_name": "Python 3.7",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.9"
}
},
"nbformat": 4,
"nbformat_minor": 1
}

You might also like