DEV Community

Cover image for How I tried hacking Fantasy Football using Python
adc
adc

Posted on

How I tried hacking Fantasy Football using Python

If you met an Italian guy he will talk you about three main topic:

  1. Food
  2. Politics
  3. Fantacalcio

If you're not Italian, I suppose it may difficult to understand what "fantacalcio" could mean, but we can try: "fanta" can be translated with "fantasy" and "calcio" is, literally, "soccer", if you combine them you get "fantasoccer".

We go totally crazy with soccer as far as Americans are do the same with football so, like Americans, before the starts of the Italian Championship we decide to organize an "analogic" role game, called "fantacalcio" that's quite similar to American Fantasy Football:
The first of September a tons of friends decide to meet each other and start a new campaign, as we were playing D&D, but we won't have any items or magic potion, the only weapon we have are professional football players.

Despite years playing fantacalcio is still funny for me but, in the last years, I decided to make it funnier combining it with Python.

Our dads used to play writing bought players and result using pen and paper but we're tech (not anymore so) young guy! So we uses the most popular fantacalcio service provider called, I'm not kidding, "Fantacalcio", formerly known as "Fantagazzetta".

Fantagazzetta is a cool website, it's not cool 'cause everyone uses it, it's cool because it provide all users a couple of Excel files where you can study all players stats in past and current season:

       Id  R        Nome     Squadra  Pg    Mv    Mf  Gf  Gs  Rp  Rc  R+  R-  Ass  Amm  Esp  Au
0       3  P   RADUNOVIC      Verona   3  5.83  3.00   0   8   0   0   0   0    0    1    0   0
1       4  P  SPORTIELLO    Atalanta   6  6.17  5.33   0   5   0   0   0   0    0    0    0   0
2       5  D      ALMICI      Verona   0  0.00  0.00   0   0   0   0   0   0    0    0    0   0
3      11  D       CONTI       Milan  21  5.69  5.60   0   0   0   0   0   0    2    8    0   0
4      15  D    MASIELLO       Genoa  22  5.80  5.73   0   0   0   0   0   0    1    5    0   0
..    ... ..         ...         ...  ..   ...   ...  ..  ..  ..  ..  ..  ..  ...  ...  ...  ..
748  4950  D  DALLE MURA  Fiorentina   0  0.00  0.00   0   0   0   0   0   0    0    0    0   0
749  4951  D   PICCININI    Sassuolo   0  0.00  0.00   0   0   0   0   0   0    0    0    0   0
750  4952  D   MONTERISI       Lecce   0  0.00  0.00   0   0   0   0   0   0    0    0    0   0
751  4953  C     PALUMBO     Udinese   0  0.00  0.00   0   0   0   0   0   0    0    0    0   0
752  4954  D     CELESIA      Torino   0  0.00  0.00   0   0   0   0   0   0    0    0    0   0  

[753 rows x 17 columns]
Enter fullscreen mode Exit fullscreen mode

And another one with the price of all buyable players, of course higher value Qt. is better that player could perform:

       Id  R         Nome   Squadra  Qt. A  Qt. I  Diff.
0     785  A     IMMOBILE     Lazio     43     38      5
1    2841  A     VLAHOVIC  Juventus     41     34      7
2     531  A      BERARDI  Sassuolo     38     31      7
3     608  A    ZAPATA D.  Atalanta     37     33      4
4    2764  A  MARTINEZ L.     Inter     33     32      1
..    ... ..          ...       ...    ...    ...    ...
541  5505  A   KAIO JORGE  Juventus      1      5     -4
542  2654  A        CETER  Cagliari      1      2     -1
543   532  A   FALCINELLI   Bologna      1      1      0
544  4940  A     GAGLIANO  Cagliari      1      1      0
545  5060  A      AKE' M.  Juventus      1      1      0

[546 rows x 7 columns]
Enter fullscreen mode Exit fullscreen mode

So, we have data lemon, how can we do a lemonade dataset?

First of all, I decided to take only the last three football season data, especially yearly average score (Fantamedia) and how much games they played (Partite Giocate) dropping all players that didn't played in Italy in last three season.

     R         Nome   Squadra  Qt. I  Partite Giocate 2019-20  Fantamedia 2019-20  Partite Giocate 2020-21  Fantamedia 2020-21  Partite Giocate 2021-22  Fantamedia 2021-22
0    A     IMMOBILE     Lazio     38                       37                9.49                       22                8.05                       19                9.03
1    A     VLAHOVIC  Juventus     34                       27                6.46                       22                7.32                       21                8.88
2    A      BERARDI  Sassuolo     31                       31                7.85                       18                7.94                       20                8.42
3    A    ZAPATA D.  Atalanta     33                       28                8.66                       23                7.72                       16                8.50
4    A  MARTINEZ L.     Inter     32                       35                7.29                       23                8.39                       18                7.89
..  ..          ...       ...    ...                      ...                 ...                      ...                 ...                      ...                 ...
293  C      PALUMBO  Juventus      1                        0                0.00                        1                5.00                        0                0.00
294  C     KINGSLEY   Bologna      1                        1                6.00                        0                0.00                        2                5.50
295  A        EDERA    Torino      3                        9                5.83                        2                6.50                        0                0.00
296  A  NESTOROVSKI   Udinese      9                       21                6.24                       14                6.28                        0                0.00
297  A     GAGLIANO  Cagliari      1                        1               11.50                        0                0.00                        0                0.00

[298 rows x 10 columns]
Enter fullscreen mode Exit fullscreen mode

With these data I code a little algorithm trying to get the value "Fattore Fantahack" that will display players with better performance in last years:

FantaHack

Quite easy to be Pathonyzed:

for row in dataset.iterrows():
    if  row['Partite Giocate ' + years[0]] > 0:  
        fattore_fantahack = (row['Partite Giocate ' + years[0]]/38 * row['Fantamedia ' + years[0]])*0.20 + 
            (row['Partite Giocate ' + years[1]]/38 * row['Fantamedia ' + years[1]])*0.60 + 
            (row['Partite Giocate ' + years[2]]/partite_giocate * row['Fantamedia ' + years[2]])*1.20 
Enter fullscreen mode Exit fullscreen mode

This will be our key value that we will use to get what players have the best performance-price ratio:

Rapporto FQ

for row in dataset.iterrows():
    media.append(row["Fattore Fantahack"]/row["Qt. I"])
dataset["Rapporto Fattore Quota"] = media
Enter fullscreen mode Exit fullscreen mode

Finally we can get the players that we should buy to get the ones with potentially highest score but lower price:

Convenienza

for row in dataset.iterrows():
    if  row["Fattore Fantahack"] > 0:    
        total.append((row["Fattore Fantahack"]*row["Rapporto Fattore Quota"]))
dataset["Convenienza"] = total
result = dataset.sort_values(by="Convenienza", ascending=False)
Enter fullscreen mode Exit fullscreen mode

This should be an example output, ordered by this last value we found named "Convenienza", of course top class player will have a "Fattore Fantahack" higher than the other ones.

     R         Nome      Squadra  Qt. I  Partite Giocate 2019-20  Fantamedia 2019-20  ...  Fantamedia 2020-21  Partite Giocate 2021-22  Fantamedia 2021-22  Fattore Fantahack  Rapporto Fattore Quota  Convenienza
56   P       OSPINA       Napoli      1                       17                5.09  ...                5.17                       19                5.55           6.121667                6.121667    37.474803        
175  C        MEDEL      Bologna      2                       23                5.65  ...                6.12                       20                5.75           6.568111                3.284056    21.570042        
176  C       DEIOLA     Cagliari      2                       10                6.05  ...                5.59                       18                6.00           6.354105                3.177053    20.187327        
88   P  TERRACCIANO   Fiorentina      1                        7                5.64  ...                0.00                       18                4.83           4.071789                4.071789    16.579470        
101  C      CATALDI        Lazio      3                       16                6.22  ...                5.62                       19                6.16           6.790409                2.263470    15.369886        
..  ..          ...          ...    ...                      ...                 ...  ...                 ...                      ...                 ...                ...                     ...          ...        
242  A         MORO        Lazio      3                        0                0.00  ...                0.00                        7                6.07           0.000000                0.000000     0.000000        
250  D  JAROSZYNSKI  Salernitana      5                        0                0.00  ...                0.00                        8                5.12           0.000000                0.000000     0.000000        
262  P       ARESTI     Cagliari      1                        0                0.00  ...                0.00                        0                0.00           0.000000                0.000000     0.000000        
264  P    MARCHETTI        Genoa      1                        0                0.00  ...                2.75                        0                0.00           0.000000                0.000000     0.000000        
219  A        OUNAS       Napoli      7                        0                0.00  ...                6.44                        5                6.10           0.000000                0.000000     0.000000

[298 rows x 13 columns]
Enter fullscreen mode Exit fullscreen mode

Bonus: if you would like to convert the output you can use the built-in Pandas feature, creating a sheet for each role.

# I already know I could use a list of dict or an even more pythonic way for this but I preferred using two list to make this script more readable by everyone :)
position_compact = ["P", "D", "C", "A"]
position = ["Portieri", "Difensori", "Centrocampisti", "Attaccanti"]
with pd.ExcelWriter("Fantahack.xlsx") as writer:
    for p in position_compact: result[result["R"] == p].to_excel(writer, sheet_name=position[position_compact.index(p)], index=False)
Enter fullscreen mode Exit fullscreen mode

Does it work? Will tell you in June, when Italian Championship ends, at the moment I'm second placed out ten that's not bad for a squad created by a "computer" that's challenging "humans" :)

Top comments (0)