Lineup Optimizer Part 1 — Nick's Niche (2024)

DFSLineup Optimizer

Written By Nick's Niche

Fanduel Player List

Hey Everyone!

Today's this tutorial is a step-by-step walkthrough to create your own daily fantasy sports lineup optimizer! Whether you want a single lineup, or are maxing out a 150 entry tournament, this video is for you!

***NOTE**** This video is going to be a fairly high level view of the code itself. Going forward my tutorial videos will be much more granular explaining what is happening, when, and why with demonstrations and examples for learning purposes. This video is just a preview of the end game to give everyone something to work toward!

Packages needed: pandas, pulp, openpyxl, and re as shown being imported below.

import pandas as pdfrom pulp import *import openpyxlimport re

Next up we will open a temporary excel workbook in the background to write lineups to -

wb = openpyxl.Workbook()ws = wb.active

Then, we will be loading our fantasy player list csv into a pandas dataframe for manipulation. -

players = pd.read_csv(r"C:\Users\nfwya\Fanduel\playerLists\20200807.csv", usecols= ['Id', 'Position', 'FPPG', 'Salary'])

After our player data is loaded, it’s time to start working with it. Regrouping dataframe by position and resetting the dataframe index off of positions — This allows us to use the grouped series as a new dataframe.

availables = players.groupby(["Position", "Id", "FPPG", "Salary", 'Team']).agg('count')availables = availables.reset_index()

Next, we need to define empty salary and point dictionaries in order to track the metrics for our lineup optimizer, by storing each player’s data by position -

salaries = {}points = {}teams = {}

Next, we will need to loop through each position in our grouped dataframe to create a dictionary with key value pair formatting of:

{player id: salary} and {player id: points}

Then, we will update empty dictionaries with a new dictionary entries with the Position being the Key, and all the player IDs as the value, with dual roles as the Key for the nested dictionary. ------ This results in nested dictionary with the following logic {K1:V1}, {K2:V2} --- {K1:{K2:V2}} Where K2=V1 ---- K1=Position, V1=K2=Player ID, V2= Points/Salary

for pos in availables.Position.unique(): available_pos = availables[availables.Position == pos] salary = list(available_pos[['Id', 'Salary']].set_index("Id").to_dict().values())[0] point = list(available_pos[['Id', 'FPPG']].set_index("Id").to_dict().values())[0] salaries[pos] = salary points[pos] = point

After this, we need to define dictionary of positional constraints (how many player we need from each position) and define the Salary Cap we want to use.

pos_num_available = { "PG": 2, "SG": 2, "SF": 2, "PF": 2, "C": 1 }salary_cap = 60000

Next, we will be establishing our lineup creation loop:

for lineup in range(1,151):

To do this, we will need to create a pulp variable to track whether player is chosen or not, (Binary = yes/no)

for lineup in range(1,151): _vars = {k: LpVariable.dict(k, v, cat='Binary') for k, v in points.items()}

define our ‘problem’, maximize function because we want max fantasy points

for lineup in range(1,151): _vars = {k: LpVariable.dict(k, v, cat='Binary') for k, v in points.items()} prob = LpProblem("Fantasy", LpMaximize)

Create empty lists to track rewards, costs and positional constraints

for lineup in range(1,151): _vars = {k: LpVariable.dict(k, v, cat='Binary') for k, v in points.items()} prob = LpProblem("Fantasy", LpMaximize) rewards = [] costs = [] position_constraints = []

Iterate over each player to populate previously created tracking lists for solving.

for lineup in range(1,151): _vars = {k: LpVariable.dict(k, v, cat='Binary') for k, v in points.items()} prob = LpProblem("Fantasy", LpMaximize) rewards = [] costs = [] position_constraints = [] for k, v in _vars.items(): costs += lpSum([salaries[k][i] * _vars[k][i] for i in v]) rewards += lpSum([points[k][i] * _vars[k][i] for i in v]) prob += lpSum([_vars[k][i] for i in v]) == pos_num_available[k]

Next, we will add the costs(Salary) and rewards(Points) into the optimization calculation, add in our logic check for slightly lowering the salary cap for every lineup after the first iteration to ensure varied lineups, and then solve.

for lineup in range(1,151): _vars = {k: LpVariable.dict(k, v, cat='Binary') for k, v in points.items()} prob = LpProblem("Fantasy", LpMaximize) rewards = [] costs = [] position_constraints = [] for k, v in _vars.items(): costs += lpSum([salaries[k][i] * _vars[k][i] for i in v]) rewards += lpSum([points[k][i] * _vars[k][i] for i in v]) prob += lpSum([_vars[k][i] for i in v]) == pos_num_available[k] prob += lpSum(rewards) prob += lpSum(costs) <= salary_cap if not lineup == 1: prob += (lpSum(rewards) <= total_score-0.01) prob.solve()

Now that we have solved the lineup function, we need to evaluate it to return the total score, then reset our column variable for writing to excel back to column 1 to start the new lineup.

for lineup in range(1,151): _vars = {k: LpVariable.dict(k, v, cat='Binary') for k, v in points.items()} prob = LpProblem("Fantasy", LpMaximize) rewards = [] costs = [] position_constraints = [] for k, v in _vars.items(): costs += lpSum([salaries[k][i] * _vars[k][i] for i in v]) rewards += lpSum([points[k][i] * _vars[k][i] for i in v]) prob += lpSum([_vars[k][i] for i in v]) == pos_num_available[k] prob += lpSum(rewards) prob += lpSum(costs) <= salary_cap if not lineup == 1: prob += (lpSum(rewards) <= total_score-0.01) prob.solve() score= str(prob.objective) colnum = 1

Now, the default output of this function is going to be a list of every single player in our dataset, with a variable of either a 1 or 0 assigned to it, depending on whether or not that player was chosen for the lineup. So we need to loop through this list and single out the players chosen, and write them to excel.

for lineup in range(1,151): _vars = {k: LpVariable.dict(k, v, cat='Binary') for k, v in points.items()} prob = LpProblem("Fantasy", LpMaximize) rewards = [] costs = [] position_constraints = [] for k, v in _vars.items(): costs += lpSum([salaries[k][i] * _vars[k][i] for i in v]) rewards += lpSum([points[k][i] * _vars[k][i] for i in v]) prob += lpSum([_vars[k][i] for i in v]) == pos_num_available[k] prob += lpSum(rewards) prob += lpSum(costs) <= salary_cap if not lineup == 1: prob += (lpSum(rewards) <= total_score-0.01) prob.solve() score= str(prob.objective) constraints = [str(const) for const in prob.constraints.values()] colnum = 1 for v in prob.variables(): score = score.replace(v.name, str(v.varValue)) if v.varValue !=0: ws.cell(row=lineup, column=colnum).value = v.name colnum +=1

Next, we just need to save our total_score variable to reference in all lineup creations after the first run through, write our final score into the excel spreadsheet for reference, and print out the lineup number and score if you would like to see the results as it runs. That finishes up our for loop creating the lineups, so we decrease the indent level back outside of the for loop and save the workbook. And we’re done!

for lineup in range(1,151): _vars = {k: LpVariable.dict(k, v, cat='Binary') for k, v in points.items()} prob = LpProblem("Fantasy", LpMaximize) rewards = [] costs = [] position_constraints = [] for k, v in _vars.items(): costs += lpSum([salaries[k][i] * _vars[k][i] for i in v]) rewards += lpSum([points[k][i] * _vars[k][i] for i in v]) prob += lpSum([_vars[k][i] for i in v]) == pos_num_available[k] prob += lpSum(rewards) prob += lpSum(costs) <= salary_cap if not lineup == 1: prob += (lpSum(rewards) <= total_score-0.01) prob.solve() score= str(prob.objective) constraints = [str(const) for const in prob.constraints.values()] colnum = 1 for v in prob.variables(): score = score.replace(v.name, str(v.varValue)) if v.varValue !=0: ws.cell(row=lineup, column=colnum).value = v.name colnum +=1 total_score = eval(score) ws.cell(row=lineup, column=colnum).value = total_score print(lineup, total_score)wb.save(r"\playerLists\Lineups20200807_12111.xlsx")

With this export format a little cleanup will be required prior to uploading to fanduel: -Remove Position and following Underscore from player ID ----- PF_PLAYERID == PLAYERID Then find and replace interior underscore with hyphen to match format of fanduel download. Can write a simple vlookup function in excel to return the player name from the fanduel download if you want to review lineups prior to uploading.

DFSLineup Optimizer

Nick's Niche

Lineup Optimizer Part 1 — Nick's Niche (2024)

References

Top Articles
Latest Posts
Article information

Author: Arline Emard IV

Last Updated:

Views: 6103

Rating: 4.1 / 5 (72 voted)

Reviews: 87% of readers found this page helpful

Author information

Name: Arline Emard IV

Birthday: 1996-07-10

Address: 8912 Hintz Shore, West Louie, AZ 69363-0747

Phone: +13454700762376

Job: Administration Technician

Hobby: Paintball, Horseback riding, Cycling, Running, Macrame, Playing musical instruments, Soapmaking

Introduction: My name is Arline Emard IV, I am a cheerful, gorgeous, colorful, joyous, excited, super, inquisitive person who loves writing and wants to share my knowledge and understanding with you.