Kneejerk is a term used in the FPL community to transfer in those players who played really well in the previous game week. If Jesse Lingard scores a banger against Spurs this game week, there is a big chance of people transferring him in even though his next match is against Manchester City.
In the Getting Data from FPL tutorial, we know how to get all kinds of data using FPL API. From the API we will use transfers_in_event and transfers_out_event and subtract the two to get a net transfer value of a player.
After that, we will use linear programming optimization to make an FPL team with all the constraints of FPL.
Step 1: Importing Libraries and getting FPL data
import pandas as pd
from pulp import *
import requests
import warnings
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.font_manager as font_manager
import matplotlib as mpl
import datetime
warnings.filterwarnings('ignore')
url = "https://fantasy.premierleague.com/api/bootstrap-static/"
#Request packagae to make GET request from the API endpoint
r = requests.get(url)
#Transform request variable to a JSON object
json = r.json()
json.keys()
#Build a Dataframe
elements_df = pd.DataFrame(json['elements'])
#Checking the columns
elements_df.columns
#Getting the required metrics for analysis
red_elements_df = elements_df[['team','id','first_name','second_name','web_name','element_type','cost_change_start','now_cost','selected_by_percent', 'transfers_in', 'transfers_out', 'total_points', 'bonus', 'minutes', 'goals_scored', 'assists', 'clean_sheets', 'status', 'form', 'transfers_in_event', 'transfers_out_event']]
#Map Element type to actual position of the players
red_elements_df['element_type']=red_elements_df['element_type'].map({4:'Forward', 3:'Midfielder', 2:'Defender', 1:'Goalkeeper'})
#Map team names
red_elements_df['team']=red_elements_df['team'].map({1:'Arsenal',2:'Aston Villa',3:'Bournemouth',4:'Brentford',5:'Brighton',6:'Chelsea',7:'Crystal Palace',8:'Everton',9:'Fulham',10:'Leicester City',11:'Leeds United',12:'Liverpool',13:'Manchester City',14:'Manchester Utd',15:'Newcastle Utd',16:'Nottingham Forest',17:'Southampton',18:'Tottenham',19:'West Ham',20:'Wolves'})
#Convert value to float
red_elements_df['selected_by_percent'] = red_elements_df.selected_by_percent.astype(float)
red_elements_df['cost_change_start'] = red_elements_df['cost_change_start']/10
red_elements_df['now_cost'] = red_elements_df['now_cost']/10
#rename columns
red_elements_df.columns = ['Team','Player ID','First Name','Second Name','Web Name','Position','Start Price','Current Price','Selected By','Transfers In', 'Transfers Out', 'Total Points', 'Bonus', 'Minutes', 'Goals Scored', 'Assists', 'Clean Sheets', 'Status', 'Form', 'Transfers_In', 'Transfers_Out']
Step 2: Net Transfers Column Addition
Now, we will copy the dataframe as df and get the Net Transfers value
df = red_elements_df.copy()
#Net Transfers
df['Net Transfers'] = df['Transfers_In'] - df['Transfers_Out']
#Form to float because it won't sort later if you don't convert it
df['Form'] = df['Form'].astype(float)
df.head()
Followed by a little bit of column renaming
df = df[['Web Name', 'Team', 'Position', 'Total Points', 'Current Price', 'Minutes', 'Net Transfers','Status', 'Form']]
df = df.rename(columns={'Web Name': 'Name', 'Total Points': 'Points', 'Current Price': 'Price', 'Net Transfers': 'NetTransfers'})
Step 3: Linear Programming
And then the Linear Programming. Don’t ask me about it now. I am still learning how it works but in this case it just works but I don’t know how.
#Get Unique Positions and Clubs
POS = df.Position.unique()
CLUBS = df.Team.unique()
#FPL Budget
BUDGET = 100
#Number of players we can take in our teams
pos_available = {
'Defender': 5,
'Forward': 3,
'Midfielder': 5,
'Goalkeeper': 2,
}
# Initialize Variables
names = [df.Name[i] for i in df.index]
teams = [df.Team[i] for i in df.index]
positions = [df.Position[i] for i in df.index]
prices = [df.Price[i] for i in df.index]
points = [df.Points[i] for i in df.index]
minutes = [df.Minutes[i] for i in df.index]
transfers = [df.NetTransfers[i] for i in df.index]
status = [df.Status[i] for i in df.index]
form = [df.Form[i] for i in df.index]
players = [LpVariable("player_" + str(i), cat="Binary") for i in df.index]
# Initialize the problem
prob = LpProblem("FPL Player Choices", LpMaximize)
# Define the objective
prob += lpSum(players[i] * transfers[i] for i in range(len(df))) # Objective
# Build the constraints
prob += lpSum(players[i] * df.Price[df.index[i]] for i in range(len(df))) <= BUDGET # Budget Limit
for pos in POS:
prob += lpSum(players[i] for i in range(len(df)) if positions[i] == pos) <= pos_available[pos] # Position Limit
for club in CLUBS:
prob += lpSum(players[i] for i in range(len(df)) if teams[i] == club) <= 3 # Club Limit
# Solve the problem
prob.solve()
Step 4: Build the dataframe
We will have to build a dataframe out of the result.
#print results as a dataframe
df_result = pd.DataFrame(columns=['Name', 'Team', 'Position', 'Points', 'Price', 'NetTransfers', 'Status', 'Form'])
for v in prob.variables():
if v.varValue != 0:
name = df.Name[int(v.name.split("_")[1])]
club = df.Team[int(v.name.split("_")[1])]
position = df.Position[int(v.name.split("_")[1])]
point = df.Points[int(v.name.split("_")[1])]
price = df.Price[int(v.name.split("_")[1])]
minutes = df.Minutes[int(v.name.split("_")[1])]
transfers = df.NetTransfers[int(v.name.split("_")[1])]
status = df.Status[int(v.name.split("_")[1])]
form = df.Form[int(v.name.split("_")[1])]
df_result = df_result.append({'Name': name, 'Team': club, 'Position': position, 'Points': point, 'Price': price, 'Minutes': minutes, 'NetTransfers': transfers, 'Status': status, 'Form': form}, ignore_index=True)
Few sortings and adding columns
#Points per minute
df_result['Points per million'] = df_result['Points'] / df_result['Price']
df_result['Points per million'] = df_result['Points per million'].astype(float).round(2)
df_result['Price'] = df_result['Price'].round(2)
df_result['Minutes'] = df_result['Minutes'].astype(int)
df_result.sort_values(by=['Form', 'NetTransfers', 'Position'], ascending=False, inplace=True)
df_result.reset_index(drop=True, inplace=True)
df_result
or Step: Get on my Github profile and download the files.
https://github.com/probablyvivek/Fantasy/blob/main/KneeJerkTeam.ipynb
The result will be the team created using Net transfers of all the players and sorted by Form first.
I then decided to create a team every week (Gameweek 4 onwards) and see how the team performs. This is what the team looks like for Gameweek 4.
I had to choose whom to keep on the bench and for that we would have to improve our linear optimization (we will do that in the future). Also, the captain’s choice.
Let’s see how this team will perform.
