Kaggle competition: Santa's Workshop Tour 2019
<SOS> metric: SantaWorkshopSchedule2019 Kaggle data source: santas-workshop-tour-2019<split>
%matplotlib inline
NUMBER_DAYS = 100
NUMBER_FAMILIES = 5000
MAX_BEST_CHOICE = 5
data = pd.read_csv('/kaggle/input/santa-workshop-tour-2019/family_data.csv')
 submission = pd.read_csv('/kaggle/input/santas-workshop-tour-2019-optimal-solution/preference_cost_43622.csv')
 assigned_days = submission['assigned_day'].values
columns = data.columns[1:11]
DESIRED = data[columns].values
COST_PER_FAMILY = [0,50,50,100,200,200,300,300,400,500]
COST_PER_FAMILY_MEMBER = [0, 0, 9, 9, 9, 18, 18, 36, 36,235]
N_PEOPLE = data['n_people'].values
def get_daily_occupancy(assigned_days):
daily_occupancy = np.zeros(100, int)
 for fid, assigned_day in enumerate(assigned_days):
daily_occupancy[assigned_day-1] += N_PEOPLE[fid]
return daily_occupancy
def days_plot(assigned_days):
daily_occupancy = get_daily_occupancy(assigned_days)
 best_choices = get_daily_occupancy(DESIRED[:,0])
 plt.rcParams['figure.figsize'] = [20, 5]
plt.xticks(np.arange(1, 101, step=1), rotation=90)
 plt.axhline(y=125, color='gray', linestyle=':')
 plt.axhline(y=300, color='gray', linestyle=':')
 mondays125 = np.array([(day+1, daily_occupancy[day])for day in range(100)if day % 7 == 1 and daily_occupancy[day] == 125])
 other_mondays = np.array([(day+1, daily_occupancy[day])for day in range(100)if day % 7 == 1 and daily_occupancy[day] != 125])
 weekends = np.array([(day+1, daily_occupancy[day])for day in range(100)if day % 7 in [2,3,4] or day == 0])
 not_weekends = np.array([(day+1, daily_occupancy[day])for day in range(1, 100)if day % 7 in [0,5,6]])
 plt.bar(*weekends.transpose() , color = 'y', label = 'Weekends')
 plt.bar(*not_weekends.transpose() , color = 'b', label = 'Thu-Wed-Tue')
 plt.bar(*other_mondays.transpose() , color = 'm', label = 'Mondays > 125')
 plt.bar(*mondays125.transpose() , color = 'g', label = 'Mondays = 125')
 plt.plot(range(1,101), best_choices, color = 'k', label = 'Best choices')
 plt.ylim(0, 500)
 plt.xlim(0, 101)
 plt.xlabel('Days before Christmas', fontsize=14)
 plt.ylabel('Occupancy', fontsize=14)
 plt.legend()
plt.show()
def days_plot2(assigned_days):
daily_occupancy = get_daily_occupancy(assigned_days)
 best_choices = get_daily_occupancy(DESIRED[:,0])
 plt.rcParams['figure.figsize'] = [20, 5]
plt.xticks(np.arange(1, 101, step=1), rotation=90)
 plt.axhline(y=125, color='gray', linestyle=':')
 plt.axhline(y=300, color='gray', linestyle=':')
 mondays = np.array([(day+1, daily_occupancy[day])for day in range(100)if day % 7 == 1])
 weekends = np.array([(day+1, daily_occupancy[day])for day in range(100)if day % 7 in [2,3,4] or day == 0])
 not_weekends = np.array([(day+1, daily_occupancy[day])for day in range(1, 100)if day % 7 in [0,5,6]])
 plt.bar(*weekends.transpose() , color = 'y', label = 'Weekends')
 plt.bar(*not_weekends.transpose() , color = 'b', label = 'Thu-Wed-Tue')
 plt.bar(*mondays.transpose() , color = 'm', label = 'Mondays')
 plt.plot(range(1,101), best_choices, color = 'k', label = 'Best choices')
 plt.ylim(0, 500)
 plt.xlim(0, 101)
 plt.xlabel('Days before Christmas', fontsize=14)
 plt.ylabel('Occupancy', fontsize=14)
 plt.legend()
plt.show()
def cost_function(prediction):
N_DAYS = 100
MAX_OCCUPANCY = 300
MIN_OCCUPANCY = 125
penalty = 0
days = list(range(N_DAYS,0,-1))
tmp = pd.read_csv('/kaggle/input/santa-workshop-tour-2019/family_data.csv', index_col='family_id')
 family_size_dict = tmp[['n_people']].to_dict() ['n_people']
cols = [f'choice_{i}' for i in range(10)]
choice_dict = tmp[cols].to_dict()
daily_occupancy = {k:0 for k in days}
for f, d in enumerate(prediction):
n = family_size_dict[f]
choice_0 = choice_dict['choice_0'][f]
choice_1 = choice_dict['choice_1'][f]
choice_2 = choice_dict['choice_2'][f]
choice_3 = choice_dict['choice_3'][f]
choice_4 = choice_dict['choice_4'][f]
choice_5 = choice_dict['choice_5'][f]
choice_6 = choice_dict['choice_6'][f]
choice_7 = choice_dict['choice_7'][f]
choice_8 = choice_dict['choice_8'][f]
choice_9 = choice_dict['choice_9'][f]
daily_occupancy[d] += n
if d == choice_0:
penalty += 0
elif d == choice_1:
penalty += 50
elif d == choice_2:
penalty += 50 + 9 * n
elif d == choice_3:
penalty += 100 + 9 * n
elif d == choice_4:
penalty += 200 + 9 * n
elif d == choice_5:
penalty += 200 + 18 * n
elif d == choice_6:
penalty += 300 + 18 * n
elif d == choice_7:
penalty += 300 + 36 * n
elif d == choice_8:
penalty += 400 + 36 * n
elif d == choice_9:
penalty += 500 + 36 * n + 199 * n
else:
penalty += 500 + 36 * n + 398 * n
for _, v in daily_occupancy.items() :
if(v < MIN_OCCUPANCY):
penalty += 100000000
accounting_cost =(daily_occupancy[days[0]]-125.0)/ 400.0 * daily_occupancy[days[0]]**(0.5)
 accounting_costs = [max(0, accounting_cost)]
diffs = [0]
yesterday_count = daily_occupancy[days[0]]
for day in days[1:]:
today_count = daily_occupancy[day]
diff = abs(today_count - yesterday_count)
 accounting_costs.append(max(0,(today_count-125.0)/ 400.0 * today_count**(0.5 + diff / 50.0)))
 yesterday_count = today_count
return penalty, sum(accounting_costs), penalty + sum(accounting_costs)
 def print_cost_function(prediction):
cfa = cost_function(prediction)
 print("Preference cost: ",cfa[0])
 print("Accounting penalty: ",cfa[1])
 print("Score: ",cfa[2])
 days_plot(assigned_days )
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=1/3, random_state=0 )<find_best_model_class>
print_cost_function(assigned_days )
class Model:
def __init__(self, model):
self.model = model
self.X, self.y = X, y
self.X_train, self.X_test, self.y_train, self.y_test = X_train, X_test, y_train, y_test
self.train()
def model_name(self):
model_name = type(self.model ).__name__
return model_name
def cross_validation(self, cv=5):
print(f"Evaluate {self.model_name() } score by cross-validation...")
 CVS = cross_val_score(self.model, self.X, self.y, scoring='accuracy', cv=cv)
 print(CVS)
 print("="*60, "
Mean accuracy of cross-validation: ", CVS.mean())
 def train(self):
print(f"Training {self.model_name() } Model...")
 self.model.fit(X_train, y_train)
 print("Model Trained.")
 def prediction(self, test_x=None, test=False):
if test == False:
y_pred = self.model.predict(self.X_test)
 else:
y_pred = self.model.predict(test_x)
 return y_pred
def accuracy(self):
y_pred = self.prediction()
y_test = self.y_test
acc = accuracy_score(y_pred, y_test)
 print(f"{self.model_name() } Model Accuracy: ", acc )<choose_model_class>
submission = pd.read_csv('/kaggle/input/santas-workshop-tour-2019-optimal-solution/accounting_penalty_313.27.csv')
 assigned_days = submission['assigned_day'].values
days_plot2(assigned_days )
xgb = XGBClassifier()
xgb = Model(xgb )<choose_model_class>
print_cost_function(assigned_days )
gnb = GaussianNB()
gnb = Model(gnb )<import_modules>
submission = pd.read_csv('/kaggle/input/santas-workshop-tour-2019-optimal-solution/submission_68888.04.csv')
 assigned_days = submission['assigned_day'].values
rfc = RandomForestClassifier()
rfc = Model(rfc)
 rfc.cross_validation()
rfc.accuracy()<normalization>
!pip install pulp
test_X = test_df.values
test_X = scale.transform(test_X )<predict_on_test>
%%time
daily_occupancy = get_daily_occupancy(assigned_days)
 lower_bounds = np.zeros(NUMBER_DAYS)
 upper_bounds = np.zeros(NUMBER_DAYS)
 delta = 0
for fi in range(NUMBER_DAYS):
lower_bounds[fi] = max(daily_occupancy[fi]-delta,125)
 upper_bounds[fi] = min(daily_occupancy[fi]+delta,300)
 prob = LpProblem("Setup occupation of days", LpMinimize)
 PCOSTM, B = {}, {}
for fid in range(NUMBER_FAMILIES):
for i in range(MAX_BEST_CHOICE):
B[fid, DESIRED[fid][i]-1] = LpVariable(f'b{fid, i}', 0, 1, LpInteger)
 if assigned_days[fid] == DESIRED[fid][i]:
B[fid, DESIRED[fid][i]-1].setInitialValue(1)
 else:
B[fid, DESIRED[fid][i]-1].setInitialValue(0)
 PCOSTM[fid, DESIRED[fid][i]-1] = COST_PER_FAMILY[i] + N_PEOPLE[fid] * COST_PER_FAMILY_MEMBER[i]
D = {}
for j in range(NUMBER_DAYS):
rj = range(int(lower_bounds[j]),int(upper_bounds[j])+1)
 for i in rj:
if j<99:
rj1 = range(int(lower_bounds[j+1]),int(upper_bounds[j+1])+1)
 for i1 in rj1:
D[j, i,i1] = LpVariable(f'D{j, i,i1}', 0, 1, LpInteger)
 if daily_occupancy[j] == i and daily_occupancy[j+1] == i1:
D[j, i,i1].setInitialValue(1)
 else:
D[j, i,i1].setInitialValue(0)
 else:
D[j,i,i] = LpVariable(f'D{j, i,i}', 0, 1, LpInteger)
 if daily_occupancy[j] == i:
D[j, i,i].setInitialValue(1)
 else:
D[j, i,i].setInitialValue(0)
 prob += lpSum(PCOSTM[i, j] * B[i, j] for i, j in B)+ lpSum(D[j,i,i1]*(int(i)-125.0)/400.0*int(i)**(0.5+abs(i-i1)/50.0)for j,i,i1 in D)
 I = {}
for j in range(NUMBER_DAYS):
I[j] = LpVariable(f'I{j}', int(lower_bounds[j]), int(upper_bounds[j]), LpInteger)
 I[j].setInitialValue(daily_occupancy[j])
 prob += lpSum([N_PEOPLE[i] * B[i, j] for i in range(NUMBER_FAMILIES)if(i,j)in B])== I[j]
for j in range(NUMBER_DAYS):
rj = range(int(lower_bounds[j]),int(upper_bounds[j])+1)
 if j<99:
rj1 = range(int(lower_bounds[j+1]),int(upper_bounds[j+1])+1)
 prob += lpSum([D[j, i,i1]*i for i in rj for i1 in rj1])== I[j]
prob += lpSum([D[j, i,i1]*i1 for i in rj for i1 in rj1])== I[j+1]
else:
prob += lpSum([D[j, i,i]*i for i in rj])== I[j]
for i in range(NUMBER_FAMILIES):
prob += lpSum(B[i, j] for j in range(NUMBER_DAYS)if(i,j)in B)== 1
prob.solve()
print("Status:", LpStatus[prob.status])
 print("Score = ", value(prob.objective))
xgb_pred = xgb.prediction(test_x=test_X, test=True)
 gnb_pred = gnb.prediction(test_x=test_X, test=True)
 svc_pred = svc.prediction(test_x=test_X, test=True)
 rfc_pred = rfc.prediction(test_x=test_X, test=True )<define_variables>
print_cost_function(assigned_days )
<save_to_csv><EOS> 
submission['assigned_day'] = assigned_days
submission.to_csv('submission_68888.04.csv', index=False )