Initializing help system before first use

Facility location problem using Pandas data frames


Type: Programming
Rating: 2 (easy-medium)
Description: Shows how to use Pandas data frames to store model data and decision variables.
File(s): facility_location_pandas.py

facility_location_pandas.py
# A facility location problem, demonstrating how to use Pandas with the
# Xpress Python interface.
#
# The problem is to choose which facilities to open in order to satisfy
# customer demand. The constraints are:
# - each customer must be served from exactly one facility
# - customers can only be served from open facilities
# - customer demand must be satisfied
# - facility capacity must not be exceeded
# We minimize the sum of transport cost (between customer and facility) and
# the cost for opening a facility.
#
# (C) 2025 Fair Isaac Corporation

import pandas as pd
import xpress as xp

# Customer ids, names and demand are stored in a data frame
customers = pd.DataFrame({
    'customer_id': ['C1', 'C2', 'C3', 'C4'],
    'name': ['Customer 1', 'Customer 2', 'Customer 3', 'Customer 4'],
    'demand': [80, 270, 250, 130]
}).set_index('customer_id')

# Facility ids, names, capacities and opening costs are stored in a data frame
facilities = pd.DataFrame({
    'facility_id': ['F1', 'F2', 'F3'],
    'name': ['Facility 1', 'Facility 2', 'Facility 3'],
    'capacity': [500, 400, 600],
    'cost': [1000, 900, 1000]
}).set_index('facility_id')

# Define distances between each customer and each facility in a matrix
distances = pd.DataFrame(
    [
        # Customer:
        # 1, 2, 3, 4
        [4, 6, 9, 2],   # Facility 1
        [5, 4, 7, 8],   # Facility 2
        [6, 3, 4, 4]    # Facility 3
    ],
    index=facilities.index,
    columns=customers.index
)

# Convert the matrix to a flat data frame, columns will be:
# facility_id, customer_id, distance
routes = distances.stack().reset_index(name='distance')

# Create the Xpress problem
p = xp.problem()

# Binary variables: whether to open each facility
facilities['open'] = p.addVariables(len(facilities), name='open', vartype=xp.binary)

# Continuous variables: how many units to serve from each facility to each customer
serve_vars = p.addVariables(len(facilities), len(customers), name='serve')
routes['serve'] = pd.Series(serve_vars.reshape(len(routes)), dtype='xpressobj')

# Demand must be met for each customer
p.addConstraint(routes.groupby('customer_id').serve.sum() == customers.demand)

# Units served from each facility must not exceed capacity, and only open facilities may serve units
p.addConstraint(routes.groupby('facility_id').serve.sum() <= facilities.capacity * facilities.open)

# Minimize total facility cost
p.setObjective(facilities.cost.dot(facilities.open))

# Solve the problem
p.optimize()
print()

# Check the result
if p.attributes.solstatus != xp.SolStatus.OPTIMAL:
    print(f'Problem was not solved to optimality. Status: {p.attributes.solstatus.name}')
else:
    # Fetch the solution values
    facilities['open_sol'] = p.getSolution(facilities.open)
    routes['serve_sol'] = p.getSolution(routes.serve)

    # Print the solution
    print('Facilities to open:')
    open_facilities = facilities[facilities.open_sol > 0.5]
    print(open_facilities.to_string(columns=['name'], header=False, index=False))

    print()
    for c in customers.itertuples():
        print(f'{c.name} served by:')
        served_by = routes[(routes.customer_id == c.Index) & (routes.serve_sol > 0)]
        # Join the facilities table to get the facility names
        print(served_by.join(facilities, on='facility_id').to_string(columns=['name', 'serve_sol'], header=False, index=False))

© 2001-2025 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.