<jupyter_start><jupyter_text>Import libraries<jupyter_code>import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns<jupyter_output><empty_output><jupyter_text>Import the dataset<jupyter_code>df=pd.read_csv('fifaindex_21.csv')<jupyter_output><empty_output><jupyter_text>Dataset details<jupyter_code>df.shape
df.head()
df.tail()<jupyter_output><empty_output><jupyter_text>Step 1 : Exploratory Data Analysis and Preprocessing<jupyter_code>print('The DataFrame contains {} lines and {} columns. The number of lines corresponds to the number of players and 38 is the number of features'.format(df.shape[0],df.shape[1]))
df=df.drop(['Value $', 'Value £','Wage $','Wage £'],axis=1)
df.columns<jupyter_output><empty_output><jupyter_text>Variable prensentationHere are all the columns of the DataSet by category. For each feature except the player description, they have a rate over 100:  1 . Player description :  Age                  Market Value  Salary       2 . Technical skillsBall controlDribbling     3 . Defense : Man to Man markingSlinding tackle Standing tackle     4 . Aptitude : CommitmentResponsiveness Placem. off. InterceptVista  Discipline      5 . Passes : CrossShort pass Long pass6 . Physic : PaceEndurance  Strength BalanceSpeed Agility  Vertical jump      7 . Shots : HeaderShot strength  Finishing  Long shots  Effect  Precision Free kickPenalties  Volley  8 . GoalKeeper Abilities : PlacementDive  Hand play Kick play  Reflexes        We only have numeric values and string for the name of the player<jupyter_code>df.dtypes.unique()<jupyter_output><empty_output><jupyter_text>Here is the statitical description of the features<jupyter_code>df.describe().T<jupyter_output><empty_output><jupyter_text>In this dataset, we have 248 players for who a market value and a salary are missing, so we are going to use them to test our models<jupyter_code>df[df["Value €"].isnull()].head()
df.isna().sum()<jupyter_output><empty_output><jupyter_text>We put all the players without price in a dataset that we won't use for training and drop these rows from the actual dataframe<jupyter_code>test_set=df[df["Value €"].isnull()]
test_set.shape
df=df.dropna()
df.shape<jupyter_output><empty_output><jupyter_text>We are going to see the correlations of the features to the target<jupyter_code>df1Corr=pd.DataFrame(df.corr().unstack().sort_values(ascending=False)['Value €'],columns=['Correlation to the target'])
df1Corr.style.background_gradient(cmap=sns.light_palette("red", as_cmap=True))<jupyter_output><empty_output><jupyter_text>Features that are correlated to the value are reaction, composure (self control), vision Below we see that 17778 over 18000 have unique name.  So 222 people have same names<jupyter_code>len(df['Name'].unique())<jupyter_output><empty_output><jupyter_text>For example here <NAME> is the name and first name of 2 different players<jupyter_code>df[df['Name']=='<NAME>']<jupyter_output><empty_output><jupyter_text>Here we visualize players who have duplicated name in the dataframe and who worth more than 4 million euros<jupyter_code>df_players_same_name=df[df['Name'].duplicated()==True]
df_players_same_name.shape
df_players_same_name[df_players_same_name['Value €']>4000000]<jupyter_output><empty_output><jupyter_text>Create variables **Class player**    If the player has 5 times more than 90 rating : he is a world class player. Encoder = 5If he has 5 times more than 85 rating : he is a very good player. Encoder = 4If he has 5 times more than 80 rating : he is a good player. Encoder = 3If he has 5 times more than 75 rating : he is a normal player. Encoder = 2If he has 5 times more than 70 rating : he is a basic player. Encoder = 1If he has 5 times more than 60 rating : he is a district player. Encoder = 0<jupyter_code>def count_values_in_row(df,rating):
    return (df.iloc[:,3:] > rating).sum(axis=1)>=5
df.loc[count_values_in_row(df,0),'class_player'] = 0
df.loc[count_values_in_row(df,70),'class_player'] = 1
df.loc[count_values_in_row(df,75),'class_player'] = 2
df.loc[count_values_in_row(df,80),'class_player'] = 3
df.loc[count_values_in_row(df,85),'class_player'] = 4
df.loc[count_values_in_row(df,90),'class_player'] = 5<jupyter_output><empty_output><jupyter_text>Players according to the class they belong<jupyter_code>df[['Name','class_player']].head(15) # Here are some player and the class they belong to
df['class_player'].value_counts()<jupyter_output><empty_output><jupyter_text>**Class age** :  If the player is or younger than 21, he is a Junior.  Encoder = 0 If the player is between 21 and 25, he is a player who is developping.  Encoder = 1 If the player is between 25 and 30, he is an experimented player.  Encoder = 2 If the player is or older than 30, he is a Senior.  Encoder = 3<jupyter_code>df['class_age'] = df['Age'].apply(lambda x : 0 if x <=21 else( 1 if x <=25 else (2 if x<=30 else 3 )))<jupyter_output><empty_output><jupyter_text>Explore the dataframe  The value of the player according to the level class player    If the player has 5 times more than 90 rating : he is a world class player. Encoder = 5If he has 5 times more than 85 rating : he is a very good player. Encoder = 4If he has 5 times more than 80 rating : he is a good player. Encoder = 3If he has 5 times more than 75 rating : he is a normal player. Encoder = 2If he has 5 times more than 70 rating : he is a basic player. Encoder = 1If he has 5 times more than 70 rating : he is a district player. Encoder = 0<jupyter_code>sns.set(font_scale=1.4)
ax= df.groupby('class_player').mean()['Value €'].plot(kind='bar',grid=True,figsize=(15,15), linewidth=2.5, color='maroon')
plt.xlabel('Category class player', labelpad=20)
plt.xticks(rotation=0)
plt.ylabel('Price range player', labelpad=20)
ax.set_yticklabels(['{:,}'.format(int(x)) for x in ax.get_yticks().tolist()])
plt.title('Player price range according to the level class of the player')
plt.show()<jupyter_output><ipython-input-26-391372678c71>:6: UserWarning: FixedFormatter should only be used together with FixedLocator
  ax.set_yticklabels(['{:,}'.format(int(x)) for x in ax.get_yticks().tolist()])<jupyter_text>Players market value rise following their class we see that class 5 players cost on average 50 million euros whereas 4 class players cost around 30 million euros We can see that the level class of a player has a big impact on his market value. The relation ship is linear.  Distict and basic players (class 0 & 1) have a market value too low to be visible on the plot  The value of the player according to the age class player     If the player is or is younger than 21, he is a Junior.  Encoder = 0  If the player is between 21 and 25, he is a player who is developping.  Encoder = 1 If the player is between 25 and 30, he is an experimented player.  Encoder = 2 If the player is or is older than 30, he is a Senior.  Encoder = 3<jupyter_code>df_viz1=df.head(500) #We take the first 5000 players because here we have a lot of players who are not that competitive
sns.set(font_scale=1.4)
ax= df_viz1.groupby('class_age').mean()['Value €'].plot(kind='bar',grid=True,figsize=(10,7), linewidth=2.5, color='maroon')
plt.xlabel('Category age class player', labelpad=20)
plt.xticks(rotation=0)
plt.ylabel('Price range player', labelpad=20)
ax.set_yticklabels(['{:,}'.format(int(x)) for x in ax.get_yticks().tolist()])
plt.title('Player price range according to the age class of the player')
plt.show()<jupyter_output><ipython-input-27-80c447a633d2>:7: UserWarning: FixedFormatter should only be used together with FixedLocator
  ax.set_yticklabels(['{:,}'.format(int(x)) for x in ax.get_yticks().tolist()])<jupyter_text>We have to higlight that players used for this visualisation are a part of the top 500 players in the world.On average players under 25 years old are the more expensive with a mean price around 45 million euros.  Whereas players over 25 years old are a little bit less expensive with a mean price around 30 million euros.    The value of the player according to his wage<jupyter_code>sns.set(font_scale=1.4)
ax= df.groupby('Wage €').mean()['Value €'].plot(kind='area',grid=True,figsize=(12,8), linewidth=2.5, color='maroon')
plt.xlabel('Wage € ', labelpad=20)
plt.xticks(rotation=0)
plt.ylabel('Price range player', labelpad=20)
ax.set_yticklabels(['{:,}'.format(int(x)) for x in ax.get_yticks().tolist()])
plt.title('Player price range according to the wage')
plt.show()<jupyter_output><ipython-input-28-d3fc60917d98>:6: UserWarning: FixedFormatter should only be used together with FixedLocator
  ax.set_yticklabels(['{:,}'.format(int(x)) for x in ax.get_yticks().tolist()])<jupyter_text>Let's see the ranking of the 20 most expensive players<jupyter_code>df1Corr=pd.DataFrame((df[['Name','Value €']].sort_values(ascending=False,by='Value €'))).head(20)
df1Corr.style.background_gradient(cmap=sns.light_palette("red", as_cmap=True))<jupyter_output><empty_output><jupyter_text>Mbappe and Neymar are the 2 most expensive players followed by <NAME><jupyter_code>df1Corr=pd.DataFrame((df[['Name','Value €']].sort_values(ascending=True,by='Value €'))).head(20)
df1Corr.style.background_gradient(cmap=sns.light_palette("green", as_cmap=True))<jupyter_output><empty_output><jupyter_text>The less expensive player in the world is <NAME>, which seems to be shocking but realistic because he is 43<jupyter_code>viz1=df.drop(['Age','Name','Wage €','Value €','class_player','class_age'],axis=1)

viz1['best_attribute']=viz1.idxmax(axis=1)
viz1['Name']=df.Name
viz1['Value €']=df['Value €']
viz1['Age']=df.Age
df1Corr=pd.DataFrame((viz1[['Name','Value €','best_attribute']].sort_values(ascending=False,by='Value €'))).head(20)
df1Corr.style.background_gradient(cmap=sns.light_palette("red", as_cmap=True))<jupyter_output><empty_output><jupyter_text>Here we added the best attribute of each player in this ranking<jupyter_code>viz1['Age'].head(20).mean() # The mean age of these 20 most valuable players is 29.35 which means that most of them are experienced<jupyter_output><empty_output><jupyter_text>Below we can see the correlation between features and the target<jupyter_code>df1Corr=pd.DataFrame(df.corr().unstack().sort_values(ascending=False)['Value €'],columns=['Correlation to the target']).head(15)
df1Corr.style.background_gradient(cmap=sns.light_palette("red", as_cmap=True))<jupyter_output><empty_output><jupyter_text>We can see the 15 best correlations between features and the target.  Correlations are very good. Among these important feature we have :  Class player column that we created Reactions (defensive ability) Composure (Ability to stay calm and cold minded)   Wage in EurosLower in the ranking, we can find the age  Processing step : Shuffle the dataframe before spliting it<jupyter_code>from sklearn.utils import shuffle
df_train=shuffle(df)
df_train.head()<jupyter_output><empty_output><jupyter_text>Defining X and y sets<jupyter_code>X=df_train.drop(['Name','Value €','class_player','class_age'],axis=1)
y=df_train.iloc[:,2]
X.dtypes<jupyter_output><empty_output><jupyter_text>Step 3 : Modeling  Import libraries<jupyter_code>from sklearn.model_selection import train_test_split
from sklearn import preprocessing
from sklearn import linear_model
from sklearn.metrics import r2_score,mean_squared_error
from sklearn.model_selection import GridSearchCV
from sklearn.svm import SVR
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import RandomForestRegressor
from sklearn.ensemble import ExtraTreesRegressor<jupyter_output><empty_output><jupyter_text>We do both (scale or not) to see if it is better with standardization of data<jupyter_code>reporting={}
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.3)
Xscaled= preprocessing.StandardScaler().fit(X).transform(X)
X_trainscaled,X_testscaled,y_trainscaled,y_testscaled=train_test_split(Xscaled,y,test_size=0.3)<jupyter_output><empty_output><jupyter_text>Mutiple Linear Regression  We tried to drop some of the features of the X_train_MultipleRegression to see if the score rised but it was not the case. So we kept the feature selection with the best scoreFurthermore, we don't scale data in Linear Regression, but if we did we would first have to split into train and test set  Without scaling<jupyter_code>regr = linear_model.LinearRegression()
fitregr=regr.fit(X_train, y_train)
predregrtrain=fitregr.predict(X_train)
predregrtest=fitregr.predict(X_test)
print('The equation of this Linear Regression has format : y= coefficient*Features + intercept')
print('Linear model intercept: {} and the coefficients are: {}'
     .format(fitregr.intercept_,fitregr.coef_))<jupyter_output>The equation of this Linear Regression has format : y= coefficient*Features + intercept
Linear model intercept: -6783589.907011641 and the coefficients are: [-2.92263124e+05  2.86678196e+02 -2.95383479e+03 -3.84676739e+03
  9.35068762e+03 -9.44357091e+03  4.81703202e+03 -4.71916880e+03
  1.14012220e+05 -1.15101162e+04  3.45092769e+02  7.74009203e+03
  2.13917952e+04  2.49017983e+03  2.99546489e+04 -5.81333053e+03
  9.19262994e+03  2.11663501e+04  2.41844142e+04  1.00273950e+04
  1.28576251e+04 -1.01894447e+04 -3.06622264e+03 -6.27833258e+03
 -1.34100250e+03  2.98681097e+04 -1.87433796e+04  1.93549719e+03
  2.79534656e+03 -1.35634977e+04  9.22990292e+03  9.04915516e+03
  3.20757567e+03  1.32025881e+04  1.52571943e+04  1.46083286e+04]<jupyter_text>After fitting the model, we print the score between predictions and test targets<jupyter_code>train_set_rmse = (np.sqrt(mean_squared_error(predregrtrain,y_train )))
train_set_r2 = r2_score(y_train, predregrtrain)
print('Linear model, R2 train score is : {} and the train root mean square Without scaling is: {}'
     .format(train_set_r2,train_set_rmse))

test_set_rmse = (np.sqrt(mean_squared_error(predregrtest,y_test )))
test_set_r2 = r2_score(y_test, predregrtest)
print('Linear model, R2 test score is : {} and the test root mean square without scaling is: {}'
     .format(test_set_r2,test_set_rmse))
reporting['Multiple Linear regression']=test_set_r2<jupyter_output><empty_output><jupyter_text>With scaling<jupyter_code>regr = linear_model.LinearRegression()
fitregr=regr.fit(X_trainscaled, y_trainscaled)
predregrtrain=fitregr.predict(X_trainscaled)
predregrtest=fitregr.predict(X_testscaled)
print('The equation of this Linear Regression has format : y= coefficient*Features + intercept')
print('Linear model intercept: {} and the coefficients are: {}'
     .format(fitregr.intercept_,fitregr.coef_))<jupyter_output>The equation of this Linear Regression has format : y= coefficient*Features + intercept
Linear model intercept: 3061765.2495241957 and the coefficients are: [-1331647.81155455  5583799.66017489    -6424.75885386  -150248.64593169
   133643.26285972   -28808.60584036   104773.82433833   -90279.01721599
  1064026.5019447   -247957.02787957  -104930.14227728   122231.33348944
   286704.29396795   142641.68113919   324725.34738767   -34276.44536726
    39564.26269102   379696.74014828   281100.83331003   120551.82642159
   191334.5974237   -117343.28881738   -32809.63756946  -139008.09371855
   -64715.89104765   685711.4451603   -442610.33022937   232211.93348625
  -147519.45522185  -222624.2042792     91478.380067     223436.00441729
    63515.18650874   237745.97922011   -20797.77996716   388097.35114844]<jupyter_text>After fitting the model, we print the score between predictions and test targets<jupyter_code>train_set_rmse = (np.sqrt(mean_squared_error(predregrtrain,y_trainscaled )))
train_set_r2 = r2_score(y_trainscaled, predregrtrain)
print('Linear model, R2 train score is : {} and the train root mean square Without scaling is: {}'
     .format(train_set_r2,train_set_rmse))

test_set_rmse = (np.sqrt(mean_squared_error(predregrtest,y_testscaled )))
test_set_r2 = r2_score(y_testscaled, predregrtest)
print('Linear model, R2 test score is : {} and the test root mean square without scaling is: {}'
     .format(test_set_r2,test_set_rmse))<jupyter_output>Linear model, R2 train score is : 0.6846584563428278 and the train root mean square Without scaling is: 4397838.523139761
Linear model, R2 test score is : 0.7023548920677603 and the test root mean square without scaling is: 4223713.046730448<jupyter_text>The test scores are better with scaling than without scaling  SVR regression Without scaling<jupyter_code>C=[100000,150000,200000,250000 ]
for i in C:
    svr_Model = SVR(C = i).fit(X_train, y_train)
    r2_train_svr = svr_Model.score(X_train, y_train)
    r2_test_svr=svr_Model.score(X_test, y_test)
    print('C = {:.2f}\n \
SVR R2 training: {:.2f}, R2 test: {:.2f}\n'
         .format(i, r2_train_svr, r2_test_svr))<jupyter_output>C = 100000.00
 SVR R2 training: 0.34, R2 test: 0.38

C = 150000.00
 SVR R2 training: 0.38, R2 test: 0.42

C = 200000.00
 SVR R2 training: 0.41, R2 test: 0.44

C = 250000.00
 SVR R2 training: 0.42, R2 test: 0.46<jupyter_text>With scaling<jupyter_code>svr_Model=SVR()

param = {'C' : [100000,150000,200000,250000 ]}

gridSearchSVR=GridSearchCV(svr_Model,param,scoring='r2',cv=5)
gridSearchSVR.fit(X_trainscaled,y_trainscaled)


best_SVR=gridSearchSVR.best_estimator_
bestSVR_testScore=best_SVR.score(X_testscaled,y_testscaled)

print('The best R2 train score is : {:.2f} with C = {:.2f}\n \
'.format(gridSearchSVR.best_score_,gridSearchSVR.best_params_['C']))
print('The best R2 test score is : {:.2f}\n with Alpha = {:.2f}\n \
'.format(bestSVR_testScore,gridSearchSVR.best_params_['C']))
reporting['SVR']=gridSearchSVR.best_score_<jupyter_output><empty_output><jupyter_text>Random Forest Regressor<jupyter_code>randomForestAlgo = RandomForestRegressor()


param = {'n_estimators' : [400,450,480], 
         'max_depth' : [100,120,140],
         'min_samples_split':[4],
         'min_samples_leaf':[2],
         'bootstrap' : [True]
        }

gridSearch_RandomForest=GridSearchCV(randomForestAlgo,param,scoring='r2',cv=3)
gridSearch_RandomForest.fit(X_train,y_train)

best_randomForest=gridSearch_RandomForest.best_estimator_
bestRandomForest_testScore=best_randomForest.score(X_test,y_test)

print('The best Random Forest R2 train score is : {:.2f} with n estimators = {:.2f}, max depth : {:.2f}, min samples split : {} and min samples leaf : {}  \n \
'.format(gridSearch_RandomForest.best_score_,gridSearch_RandomForest.best_params_['n_estimators'],gridSearch_RandomForest.best_params_['max_depth'],gridSearch_RandomForest.best_params_['min_samples_split'],gridSearch_RandomForest.best_params_['min_samples_leaf'] ))
print('The best Random Forest R2 test score is : {:.2f} with n estimators = {:.2f}, max depth : {:.2f}, min samples split : {} and min samples leaf : {}  \n \
'.format(bestRandomForest_testScore,gridSearch_RandomForest.best_params_['n_estimators'],gridSearch_RandomForest.best_params_['max_depth'],gridSearch_RandomForest.best_params_['min_samples_split'],gridSearch_RandomForest.best_params_['min_samples_leaf'] ))

reporting['Random Forest Regressor']=bestRandomForest_testScore<jupyter_output><empty_output><jupyter_text>Extra Trees Regressor<jupyter_code>extraTreesAlgo = ExtraTreesRegressor()

param = {'n_estimators' : [400,450,480], 
         'max_depth' : [100,120,140],
         'min_samples_split':[4],
         'min_samples_leaf':[2],
         'bootstrap' : [True]
        }

gridSearch_ExtraTrees=GridSearchCV(extraTreesAlgo,param,scoring='r2',cv=5)
gridSearch_ExtraTrees.fit(X_train,y_train)

best_ExtrasTrees=gridSearch_ExtraTrees.best_estimator_
bestExtrasTrees_testScore=best_ExtrasTrees.score(X_test,y_test)


print('The best Random Forest R2 score is : {:.2f} with n estimators = {:.2f}, max depth : {:.2f}, min samples split : {} and min samples leaf : {}  \n \
'.format(gridSearch_ExtraTrees.best_score_,gridSearch_ExtraTrees.best_params_['n_estimators'],gridSearch_ExtraTrees.best_params_['max_depth'],gridSearch_ExtraTrees.best_params_['min_samples_split'],gridSearch_ExtraTrees.best_params_['min_samples_leaf'] ))
print('The best Random Forest R2 test score is : {:.2f} with n estimators = {:.2f}, max depth : {:.2f}, min samples split : {} and min samples leaf : {}  \n \
'.format(bestExtrasTrees_testScore,gridSearch_ExtraTrees.best_params_['n_estimators'],gridSearch_ExtraTrees.best_params_['max_depth'],gridSearch_ExtraTrees.best_params_['min_samples_split'],gridSearch_ExtraTrees.best_params_['min_samples_leaf'] ))
reporting['Extra Trees Regressor']=bestExtrasTrees_testScore<jupyter_output><empty_output><jupyter_text>Present all the results of every model in a data frame and plot these in function of the model<jupyter_code>df_results=pd.DataFrame()
df_results['model']=reporting.keys()
df_results['Score R2']=reporting.values()
df_results=df_results.set_index('model')
dfVizModels=pd.DataFrame(df_results['Score R2'].sort_values(ascending=False))
dfVizModels.style.background_gradient(cmap=sns.light_palette("red", as_cmap=True))<jupyter_output><empty_output><jupyter_text>The best model after grid search is the Random Forest regressor with a R2 score of 0.90 rounded to the nearest tenth         Let's predict real player price thanks to our model<jupyter_code>test=pd.read_csv('fifaindex_21.csv')<jupyter_output><empty_output><jupyter_text>Let's predict Messi price<jupyter_code>test.loc[1]
test=test.drop(['Name','Value €','Value $', 'Value £','Wage $','Wage £'],axis=1)<jupyter_output><empty_output><jupyter_text>Get the players attributes to predict the price<jupyter_code>player=test.iloc[1,:].values
[player]
best_randomForest.predict([player])[0]
df.loc[1]['Value €'] # We can see the prediction is very accurate, we just have 1 million difference<jupyter_output><empty_output><jupyter_text>Table of the Top 100 player's name, real value and prediction<jupyter_code>pd.set_option('display.float_format', '{:,}'.format)
df_pred=pd.DataFrame(columns=['Name','Value', 'Prediction'])
df_pred['Name']= [i for i in df[:100].Name]

df_pred['Value']= [round(i,0) for i in df.loc[:99]['Value €']]
player_stats=test.iloc[:,:].values
df_pred['Prediction']=[round(best_randomForest.predict([player])[0],0) for player in player_stats[:100]]
df_pred['Difference']= round(abs(df_pred['Value']-df_pred['Prediction']),0)
df_pred[0:50]<jupyter_output><empty_output><jupyter_text>Let's pickle the model with the best R2 in order to implement the API<jupyter_code>import pickle<jupyter_output><empty_output><jupyter_text>save the model in a pickle file<jupyter_code>pickle.dump(best_randomForest, open("model.pkl",'wb'))<jupyter_output><empty_output><jupyter_text>load the model in a variable<jupyter_code>model=pickle.load(open("model.pkl",'rb'))<jupyter_output><empty_output><jupyter_text>Let's take this example, with theses values for the features<jupyter_code>sterling_index=55
print(model.predict([test.iloc[sterling_index,:].values])[0])<jupyter_output>98410768.51100287
{'fraction_non_alphanumeric': 0.11439470338054106, 'fraction_numerical': 0.06813697548998114, 'mean_word_length': 6.600133288903699, 'pattern_counts': {'":': 0, '<': 179, '<?xml version=': 0, '>': 179, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '44', 'dir': 'jupyter-structured-clean-dedup', 'id': '426336', 'license': 'MIT', 'n_tokens_mistral': 8939, 'n_tokens_neox': 7929, 'n_words': 1966, 'path': 'Modeling/EDA_And_ML_modeling.ipynb', 'repo_name': 'ikhlo/Price_prediction_footballers', 'size': '22828'}