# --------------------------------------------------------------------------
# Source file provided under Apache License, Version 2.0, January 2004,
# http://www.apache.org/licenses/
# (c) Copyright IBM Corp. 2015, 2019
# --------------------------------------------------------------------------
from docplex.util.environment import get_environment
try:
import pandas as pd
except ImportError:
pd = None
from docplex.util.csv_utils import write_csv, write_table_as_csv
def get_auto_publish_names(context, prop_name, default_name):
# comparing auto_publish to boolean values because it can be a non-boolean
autopubs = context.solver.auto_publish
if autopubs == None:
return None
if autopubs is True:
return [default_name]
elif autopubs is False:
return None
elif prop_name in autopubs:
name = autopubs[prop_name]
else:
name = None
if isinstance(name, str):
# only one string value: make this the name of the table
# in a list with one object
name = [name]
elif name is True:
# if true, then use default name:
name = [default_name]
elif name is False:
# Need to compare explicitely to False
name = None
else:
# otherwise the kpi_table_name can be a collection-like of names,
# just return it
pass
return name
def auto_publishing_result_output_names(context):
# Return the list of result output names for saving
return get_auto_publish_names(context, 'result_output', 'solution.json')
def auto_publishing_kpis_table_names(context):
# Return the list of kpi table names for saving
return get_auto_publish_names(context, 'kpis_output', 'kpis.csv')
def get_kpis_name_field(context):
autopubs = context.solver.auto_publish
if autopubs is True:
field = 'Name'
elif autopubs is False:
field = None
else:
field = context.solver.auto_publish.kpis_output_field_name
return field
def get_kpis_value_field(context):
autopubs = context.solver.auto_publish
if autopubs is True:
field = 'Value'
elif autopubs is False:
field = None
else:
field = context.solver.auto_publish.kpis_output_field_value
return field
[docs]class PublishResultAsDf(object):
'''Mixin for classes publishing a result as data frame
'''
@staticmethod
def value_if_defined(obj, attr_name, default=None):
value = getattr(obj, attr_name) if hasattr(obj, attr_name) else None
return value if value is not None else default
[docs] def write_output_table(self, df, context,
output_property_name=None,
output_name=None):
'''Publishes the output `df`.
The `context` is used to control the output name:
- If context.solver.auto_publish is true, the `df` is written using
output_name.
- If context.solver.auto_publish is false, This method does nothing.
- If context.solver.auto_publish.output_property_name is true,
then `df` is written using output_name.
- If context.solver.auto_publish.output_propert_name is None or
False, this method does nothing.
- If context.solver.auto_publish.output_propert_name is a string,
it is used as a name to publish the df
Example:
A solver can be defined as publishing a result as data frame::
class SomeSolver(PublishResultAsDf)
def __init__(self, output_customizer):
# output something if context.solver.autopublish.somesolver_output is set
self.output_table_property_name = 'somesolver_output'
# output filename unless specified by somesolver_output:
self.default_output_table_name = 'somesolver.csv'
# customizer if users wants one
self.output_table_customizer = output_customizer
# uses pandas.DataFrame if possible, otherwise will use namedtuples
self.output_table_using_df = True
def solve(self):
# do something here and return a result as a df
result = pandas.DataFrame(columns=['A','B','C'])
return result
Example usage::
solver = SomeSolver()
results = solver.solve()
solver.write_output_table(results)
'''
prop = self.value_if_defined(self, 'output_table_property_name')
prop = output_property_name if output_property_name else prop
default_name = self.value_if_defined(self, 'default_output_table_name')
default_name = output_name if output_name else default_name
names = get_auto_publish_names(context, prop, default_name)
use_df = self.value_if_defined(self, 'output_table_using_df', True)
if names:
env = get_environment()
customizer = self.value_if_defined(self, 'output_table_customizer', lambda x: x)
for name in names:
r = customizer(df)
if pd and use_df:
env.write_df(r, name)
else:
# assume r is a namedtuple
write_csv(env, r, r[0]._fields, name)
def is_publishing_output_table(self, context):
prop = self.value_if_defined(self, 'output_table_property_name')
default_name = self.value_if_defined(self, 'default_output_table_name')
names = get_auto_publish_names(context, prop, default_name)
return names
def write_kpis_table(env, context, model, solution):
names = auto_publishing_kpis_table_names(context)
kpis_table = []
for k in model.iter_kpis():
kpis_table.append([k.name, k.compute(solution)])
if kpis_table:
# do not create the kpi tables if there are no kpis to be written
field_names = [get_kpis_name_field(context),
get_kpis_value_field(context)]
for name in names:
write_table_as_csv(env, kpis_table, name, field_names)
def write_solution(env, solution, name):
with env.get_output_stream(name) as output:
output.write(solution.export_as_json_string().encode('utf-8'))
def write_result_output(env, context, model, solution):
names = auto_publishing_result_output_names(context)
for name in names:
write_solution(env, solution, name)