-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathOptimizer.py
123 lines (92 loc) · 4.75 KB
/
Optimizer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# -*- coding: utf-8 -*-
"""
Created on Sat Dec 26 16:46:16 2015
@author: lshulgin
"""
from Constants import *
import pandas as pd
from pulp import *
from Roster import Roster
class Optimizer(object):
def __init__(self, model_output):
self._frame = model_output.copy()
self._prob = None
def setup(self):
self.decorate_data()
self._prob = LpProblem("Fantasy", LpMaximize)
self._players = list(self._frame.index)
self._choices = LpVariable.dict("Include", self._players, 0, 1, LpInteger)
# Function to maximize
self._prob += lpSum([self._choices[player] * self._frame[COL_POINTS][player] for player in self._players]), "Projected points"
# Position constraints
constraints = self.get_constraints()
for key, data in constraints.iteritems():
self._prob += lpSum([self._choices[player] * self._frame[key][player] for player in self._players]) >= data['Min'], key + " Min"
self._prob += lpSum([self._choices[player] * self._frame[key][player] for player in self._players]) <= data['Max'], key + " Max"
# Salary constraint
self._prob += lpSum([self._choices[player] * self._frame[COL_SALARY][player] for player in self._players]) <= self._salary_cap, "Salary cap"
def decorate_data(self):
self._frame[COL_USED] = 0
for pos in set(self._positions):
if pos in self._position_map:
self._frame[pos] = map(lambda position: 1 if position in self._position_map[pos] else 0, self._frame[COL_POSITION])
else:
self._frame[pos] = map(lambda position: 1 if position == pos else 0, self._frame[COL_POSITION])
salaries = self._get_salaries()
# Now need to join self._frame and salaries using a proper key, which isn't implemented yet!
# For now use [Name, Position, Team], which isn't great because the two name columns
# come from different sources
self._frame = pd.merge(self._frame, salaries, 'left', [COL_NAME, COL_POSITION, COL_TEAM])
# Take an array of avaiable positions and convert into min/max counts for each tag
def get_constraints(self):
keys = set(self._positions)
constraints = {}
for key in keys:
min_times = 0
max_times = 0
for entry in self._positions:
if entry == key:
min_times += 1
max_times += 1
elif entry in self._position_map and key in self._position_map[entry]:
max_times += 1
elif key in self._position_map and entry in self._position_map[key]:
min_times += 1
max_times += 1
constraints[key] = {'Min' : min_times, 'Max' : max_times}
return constraints
def disjoint_rosters(self, n):
self.setup()
rosters = []
while(len(rosters) < n):
self._prob.solve()
if LpStatus[self._prob.status] != 'Optimal':
raise "Unable to find optimal solution"
players = filter(lambda x: value(self._choices[x]) == 1, self._choices)
valid = self._frame.index.map(lambda x: x in players)
roster = Roster(self._frame[valid].copy())
rosters.append(roster)
# Exclude these player from further iterations
for player in players:
if self._frame[COL_USED][player] == 0:
self._frame.set_value(player, COL_USED, 1)
self._prob += self._choices[player] == 0, "Exclude " + str(player)
return rosters
# Get the n top rosters -- this is broken since only returns one roster from each degenerate score
def top_rosters(self, n):
self.setup()
# For some reason smaller steps like 1e-6 don't work
epsilon = .001
count = 0
while(count < n):
self._prob.solve()
if LpStatus[self._prob.status] != 'Optimal':
raise "Unable to find optimal solution"
players = filter(lambda x: value(self._choices[x]) == 1, self._choices)
valid = self._frame.index.map(lambda x: x in players)
roster = Roster(self._frame[valid].copy())
yield roster
count += 1
# Set the threshold a bit lower
high_score = value(self._prob.objective)
self._prob += lpSum([self._choices[player] * self._frame[COL_POINTS][player] for player in self._players]) <= high_score - epsilon, "{0}th highest".format(count)