<jupyter_start><jupyter_text>Load dataThis cell loads data from project data setsIt is marked as dd-ignore as it will be ignored when importing this notebook to DO for WS to use scenario data management.<jupyter_code>#dd-ignore

import types
import pandas as pd
from botocore.client import Config
import ibm_boto3

def __iter__(self): return 0

# @hidden_cell
# The following code accesses a file in your IBM Cloud Object Storage. It includes your credentials.
# You might want to remove those credentials before you share your notebook.
client_5aea6b8defe6477eb3bb359a3ad797c8 = ibm_boto3.client(service_name='s3',
    ibm_api_key_id='<KEY>',
    ibm_auth_endpoint="https://iam.eu-de.bluemix.net/oidc/token",
    config=Config(signature_version='oauth'),
    endpoint_url='https://s3.eu-geo.objectstorage.service.networklayer.com')

body = client_5aea6b8defe6477eb3bb359a3ad797c8.get_object(Bucket='do4wsmarketingcampaignshandson-donotdelete-pr-17nh3lvgn7be59',Key='candidate.csv')['Body']
# add missing __iter__ method, so pandas accepts body as file-like object
if not hasattr(body, "__iter__"): body.__iter__ = types.MethodType( __iter__, body )

df_data_1 = pd.read_csv(body)
df_data_1.head()

body = client_5aea6b8defe6477eb3bb359a3ad797c8.get_object(Bucket='do4wsmarketingcampaignshandson-donotdelete-pr-17nh3lvgn7be59',Key='customer.csv')['Body']
# add missing __iter__ method, so pandas accepts body as file-like object
if not hasattr(body, "__iter__"): body.__iter__ = types.MethodType( __iter__, body )

df_data_2 = pd.read_csv(body)
df_data_2.head()

body = client_5aea6b8defe6477eb3bb359a3ad797c8.get_object(Bucket='do4wsmarketingcampaignshandson-donotdelete-pr-17nh3lvgn7be59',Key='campaign-1.csv')['Body']
# add missing __iter__ method, so pandas accepts body as file-like object
if not hasattr(body, "__iter__"): body.__iter__ = types.MethodType( __iter__, body )

df_data_3 = pd.read_csv(body)
df_data_3.head()

#dd-ignore
import os, pandas as pd

inputs = {}

inputs['campaign']= df_data_3
inputs['customer'] = df_data_2
inputs['candidate']= df_data_1

inputs['candidate'].head()

outputs = {}<jupyter_output><empty_output><jupyter_text>Extract data from inputs dictionnaryIn DO for DSX the data is passed as a dictionnary indexed by table names<jupyter_code>import os, pandas as pd

df_campaign = inputs['campaign']
campaigns = df_campaign['id'].values
df_campaign.set_index('id', inplace=True)

df_customer = inputs['customer'] 
customers = df_customer['id'].values
df_customer.set_index('id', inplace=True)

df_candidate = inputs['candidate']
df_candidate.set_index(["Customer","Campaign"], inplace = True)

df_candidate.head()<jupyter_output><empty_output><jupyter_text>Import DO package and create a new model<jupyter_code>from docplex.mp.model import Model
mdl = Model("MarketingSmall")<jupyter_output><empty_output><jupyter_text>Create decision variablesWe will have one "selection" binary decision variable for each pair of cusomer and campaign<jupyter_code>selected = mdl.binary_var_matrix(keys1=customers, keys2=campaigns, name="selected")
df_selected = pd.DataFrame({'selected': selected})
df_selected.index.names=['customer', 'campaign']<jupyter_output><empty_output><jupyter_text>Create and add objectiveThe objective is to maximize expected revenue<jupyter_code>expected_revenue = mdl.sum( selected[customer,campaign] * df_candidate['expected value'][customer,campaign] for customer in customers for campaign in campaigns) 
mdl.add_kpi(expected_revenue, "Expected revenue");
mdl.maximize(expected_revenue);<jupyter_output><empty_output><jupyter_text>Create and add constraints* For each campaign the number of selected customers is lower than "max customers"<jupyter_code>for campaign in campaigns:
    mdl.add_constraint( mdl.sum( selected[customer,campaign] for customer in customers) <= df_campaign['max customers'][campaign])<jupyter_output><empty_output><jupyter_text>* For each customer, there is at most one selected campaign<jupyter_code>for customer in customers:
    mdl.add_constraint( mdl.sum( selected[customer,campaign] for campaign in campaigns) <= 1)
mdl.print_information()<jupyter_output>Model: MarketingSmall
 - number of variables: 30
   - binary=30, integer=0, continuous=0
 - number of constraints: 13
   - linear=13
 - parameters: defaults
 - problem type is: MILP<jupyter_text>Solve the model<jupyter_code>assert mdl.solve(), "!!! Solve of the model fails"
mdl.report()<jupyter_output>* model MarketingSmall solved with objective = 770
*  KPI: Expected revenue = 770.000<jupyter_text>Extract solution value for selected variables<jupyter_code>df_selected = df_selected.selected.apply(lambda v: v.solution_value)
df_selected.head()<jupyter_output><empty_output><jupyter_text>Build output data frame<jupyter_code>df_selected = df_selected.reset_index()
df_candidate = df_candidate.reset_index();
df_selected['expected revenue'] = df_selected.selected * df_candidate['expected value']

df_selected.head()<jupyter_output><empty_output><jupyter_text>And add it to outputs dictionnary so that it is exported to scenario<jupyter_code>outputs['selected'] = df_selected<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10396424407306645, 'fraction_numerical': 0.023707734162456275, 'mean_word_length': 7.650420168067227, 'pattern_counts': {'":': 0, '<': 46, '<?xml version=': 0, '>': 44, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '11', 'dir': 'jupyter-structured-clean-dedup', 'id': '295081', 'license': 'MIT', 'n_tokens_mistral': 1770, 'n_tokens_neox': 1698, 'n_words': 393, 'path': 'notebook/MarketingSmall.ipynb', 'repo_name': 'nerav-doshi/MarketingCampaign-HandsOn', 'size': '5185'}