This educational assumes a little familiarity with Jupyter and Python. If you’re simply getting began, get started with my educational right here:

Once you entire your setup, come proper again to this newsletter!

If you have already got Jupyter, Python, and Pandas put in then transfer on to step 2!

First, we’ll want our package deal imports. We in point of fact most effective want 3:

import requests
import pandas as pd
import numpy as np

Note: If you get an import error, it’s most definitely since you haven’t added the package deal to your atmosphere, or activated your construction atmosphere. Head over to your Anaconda Navigator and be sure to upload the package deal wanted to no matter atmosphere you turn on in your Jupyter Notebook paintings. Apply the replace, and don’t disregard to restart your terminal prior to you turn on your construction atmosphere!

Next, we’ll want the API URL:

url = 'https://fantasy.premierleague.com/api/bootstrap-static/'

Use the requests package deal to make a GET request from the API endpoint:

r = requests.get(url)

Then, turn into that request into a json object:

json = r.json()

Let’s take a look at the json keys, and then we’ll create our dataframe(s)

json.keys()

This returns a record of all the keys to the json:

A screenshot of the creator’s pocket book effects

The 3 keys I in point of fact maintain this newsletter are parts , element_type , and groups .

Next, we’ll create 3 other dataframes the use of those 3 keys, and then map some columns from the groups and element_type dataframes into our parts dataframe.

Once we construct our dataframes, we’ll be in a position to paintings with the knowledge and do such things as kind, filter out, map (equivalent to a v-lookup in Excel), and arrange our knowledge into a pivot desk.

So let’s get started through construction our dataframes:

elements_df = pd.DataFrame(json['elements'])
elements_types_df = pd.DataFrame(json['element_types'])
teams_df = pd.DataFrame(json['teams'])

Preview the most sensible Five rows of your dataframes with the head() way. Like so:

elements_df.head()
A screenshot of the creator’s pocket book effects

You too can get a glimpse of all of the columns in every dataframe with the columns way:

elements_df.columns
A screenshot of the creator’s pocket book effects

There is an terrible lot of knowledge, so I’m going to make a reproduction of this dataframe, however most effective reproduction over a few of the columns. I really like to prepend slim_ to those smaller, lighter dataframe copies:

slim_elements_df = elements_df[['second_name','team','element_type','selected_by_percent','now_cost','minutes','transfers_in','value_season','total_points']]

Now, maximum of the knowledge I’m lately excited about is viewable in a unmarried window:

slim_elements_df.head()
A screenshot of the creator’s pocket book effects

Success! Let’s transfer on.

This phase will likely be damaged into sub-sections:

Using Map()

The very first thing I’m going to do is map the function title from the elements_type_df to the slim_elements_df . If you’ve ever used Excel, you’ll understand that map() could be very equivalent to a v-lookup.

slim_elements_df['position'] = slim_elements_df.element_type.map(elements_type.set_index('identity').singular_name)

Now that we’ve completed that, take a take a look at the most sensible few rows:

slim_elements_df.head()
A screenshot of the creator’s pocket book effects

Great, now we will be able to simply see the function. This will come in useful after we arrange value_season through function.

Next, let’s upload the group title. We can do this through mapping “title” from our teams_df:

slim_elements_df['team'] = slim_elements_df.group.map(teams_df.set_index('identity').title)

Voilà:

A screenshot of the creator’s pocket book effects

Next, let’s kind this desk through value_season . I’m curious to take a look at the most sensible price choices this 12 months.

Using astype()

BUT FIRST, now we have to create a new column referred to as price .

Why? Because a few of the values in the column value_season are string values. I do know this as a result of I’ve tried to kind through value_season and were given a atypical, fallacious end result. Unexplained habits for your dataframe can regularly be due to conflicting kinds of knowledge saved in the identical column (as an example: strings, integers, and go with the flow values all saved in the identical column).

We want to be sure each unmarried price is of the identical kind. To do that, we’ll create a new column, and use the astype() way to set all of the values to a go with the flow price (fancy-talk for numbers with decimals, instance: 4.5).

slim_elements_df['value'] = slim_elements_df.value_season.astype(go with the flow)

Now we will be able to kind our dataframe on the column price . Let’s do it:

Using sort_values()

Sort_values() permits you to, neatly, kind values 🙂

By default, Pandas will kind values in the ascending course from low to excessive. But we would like values looked after in the descending course: excessive to low. So we want to explicitly set the ascending parameter to False like so:

slim_elements_df.sort_values('price',ascending=False).head(10)
A screenshot of the creator’s pocket book effects

Well, will ya take a look at that? Two avid gamers tied for the most sensible spot. Pope and Lundstram. A goalkeeper and a defender. Nice.

This necessarily approach Pope and Lundstram have earned the maximum issues consistent with price, as a result of price is just calculated through dividing total_points through now_cost .

price = issues / price

Did you are expecting that to be the case? A defender and a goalkeeper? Might this alteration the way you consider group variety subsequent 12 months? Or subsequent week?

Using pivot_table()

Next, we’ll create a pivot_table on the column function , and take a look at value-by-position:

slim_elements_df.pivot_table(index='function',values='price',aggfunc=np.imply).reset_index()

After developing the pivot_table and assigning the pivot_table to the variable pivot , we’ll kind the pivot desk descending through price:

pivot.sort_values('price',ascending=False)

This provides us:

A screenshot of the creator’s pocket book effects

Interesting. One of the two highest-value avid gamers in the recreation is a goalkeeper, but the goalkeeper function contributes the lowest price on reasonable. I believe that is most probably as a result of all the goalkeepers that play Zero mins. deliver down the reasonable of the starters.

To deal with this, let’s take away all the avid gamers from our dataframe that experience 0 mins this season. We’ll use loc[] for the first time:

Using .loc[]

.loc allows you to find particular rows that experience particular column values. It’s like filtering a spreadsheet on a particular price in a column.

For our present functions, we would like to find all rows in our dataframe that experience a price more than 0 in order to take away all values = 0. We aren’t simply doing this for keepers. We want to do that for each function. We write:

slim_elements_df = slim_elements_df.loc[slim_elements_df.value > 0]

Now let’s run that pivot desk again once more:

pivot = slim_elements_df.pivot_table(index='function',values='price',aggfunc=np.imply).reset_index()

and then:

pivot.sort_values('price',ascending=False)

There you could have it:

A screenshot of the creator’s pocket book effects

Now let’s do a other form of pivot desk subsequent. This time, we’ll take a look at which groups are offering the maximum price this 12 months. The most effective issues that want to trade about this pivot_table observation are the index and the pivot_table variable. Take a glance:

team_pivot = slim_elements_df.pivot_table(index='group',values='price',aggfunc=np.imply).reset_index()

Now we’ll show the team_pivot looked after, from the perfect price group to the lowest:

team_pivot.sort_values('price',ascending=False)

Results are as follows:

A screenshot of the creator’s pocket book effects

Wolves fanatics don’t have to concern about their innate biases. Lucky!

Spurs fanatics, on the different hand, have had to be a little extra mindful in their bias in opposition to their very own group to achieve the FPL this 12 months. How nerve-racking.

Using .hist()

Next, let’s take a look at a histogram distribution of price through function. Before we do this, we’ll create some filtered dataframes for every function. We will use .loc once more to accomplish this:

fwd_df = slim_elements_df.loc[slim_elements_df.position == 'Forward']mid_df = slim_elements_df.loc[slim_elements_df.position == 'Midfielder']def_df = slim_elements_df.loc[slim_elements_df.position == 'Defender']goal_df = slim_elements_df.loc[slim_elements_df.position == 'Goalkeeper']

Then, we’ll use the .hist() way on our goal_df.price dataframe column:

goal_df.price.hist()

This provides us:

A screenshot of the creator’s pocket book effects

There is a first rate quantity of price right here above 20. Let’s take a look at our dataframe, looked after through price to see who those characters are:

goal_df.sort_values('price',ascending=False).head(10)
A screenshot of the creator’s pocket book effects

Take-away? There’s a lot of price to be had in the goalkeeper function.

Plenty of different tactics to lower this cheese, however let’s transfer on to histograms of the defender function:

A screenshot of the creator’s pocket book effects

Our defensive histogram issues out one thing attention-grabbing. Notice the distinction between the goalkeeper histogram and the defender histogram?

For a goalkeeper, there may be a “best possible” selection, however we don’t in point of fact know who this is going to be beginning the season. It seems, goalkeeper variety was once beautiful forgiving seeing what number of goalkeepers presented price between 20 and 25:

A screenshot of the creator’s pocket book effects

Defenders, on the different hand, had only one transparent winner this 12 months, and most effective a few others in the 20–25 price vary that stood out from the pack. Let’s take a look at the most sensible of the defender price desk:

A screenshot of the creator’s pocket book effects

Lundstram was once THE crucial defender this 12 months, providing a massive quantity of price consistent with price: 26.1. Impressive!

The maximum necessary takeaway from this entire research is that Goal Keepers and Defenders be offering extra price than Midfielders and Forwards. But if you happen to assessment FPL Twitter, you’ll see many FPL managers that don’t slightly seize this perception. That, my pal, is known as “alternative.”

It’s now not all about price after all. It’s a group optimization problem. You have 100 foreign money devices to spend, and a group of high-value returners would possibly not rating sufficient issues to problem for a excessive end for your mini-leagues or general. A group of the highest-value returners will depart you with some cash in the financial institution. And the recreation is to leverage your cash for POINTS, now not VALUE.

Let’s take a look at the most sensible midfielders, as an example:

A screenshot of the creator’s pocket book effects

Cantwell gives the perfect price out of all midfielders, however he nonetheless has simply 100 issues. Meanwhile, De Bruyne has a price of simply 16.8, however 178 overall issues…

The level? You want each price and high-returns. You’re searching for a steadiness; a balanced optimization of price and issues.

Before we move, I sought after to display you ways to export to a CSV that you’ll be able to paintings with in Google Sheets or Excel.

It’s easy:

slim_elements_df.to_csv('~/Desktop/fpl_data.csv')

Run that mobile and Boom! Done. Nice task 🙂

Now have amusing exploring the knowledge in Google Sheets or Excel, if that’s your factor.

LEAVE A REPLY

Please enter your comment!
Please enter your name here