diff --git a/README.md b/README.md index df33a1a..f14ac14 100644 --- a/README.md +++ b/README.md @@ -2,41 +2,50 @@ **fantasy-ga** is a Python module and a command line tool that uses genetic algorithm to automate the generation of fantasy sports linesups. Currently supports DraftKings basketball rules. ## Installation - Dependency - - Python >= 3.9 - `numpy` ```bash pip install fantasy-ga ``` ## Usage -Provide a `numpy.ndarray` where the columns correspond to player ID, salary, fantasy points (FPTS) and position information i.e. `player_id,salary,fpts,PG,SG,SF,PF,C,G,F,Util`. + +Export a csv file from your daily fantasy basketball platform for a given contest, and read the file with `fantasy_ga.read_csv`. Currently DraftKings is supported. + +Alternatively, you can provide a `numpy.array` where the columns correspond to player ID, salary, fantasy points (FPTS) and position information i.e. `id,salary,fpts,PG,SG,SF,PF,C,G,F,UTIL`. ### Python ```python import numpy as np -from fantasy_ga import LineupGenerator +from fantasy_ga import LineupGenerator, read_csv + +# load data from DraftKings salary csv +id_to_name, m = read_csv("examples/DraftKings/DKSalaries.csv", site="DraftKings") -m = np.loadtxt('examples/mat.csv', delimiter=',', skiprows=1) +# initial population of random lineups n_pop = 1000 +# number of evolutions to itererate breeding and mutation for +n_gen = 16 +# number of children lineups to choose from two best lineups n_breed = 30 +# number of random mutations for each evolution n_mutate = 30 -n_gen = 16 +# number of compound evolutions with additional random lineups n_compound = 5 model = LineupGenerator(m, n_pop, n_gen, n_breed, n_mutate, n_compound) -lineups, fit = model.compound() -optimal_lineups, top_n_scores = model.get_top_n_lineups(lineups, fit, 1) +lineups, scores = model.fit() +optimal_lineups, top_n_scores = model.get_top_n_lineups(lineups, scores, 1) ``` ### CLI As a module ``` -$ python -m fantasy_ga --filepath=examples/mat.csv --n_pop=100 --n_gen=5 --n_breed=100 --n_mutate=100 --n_compound=10 --top_n_lineups=1 +$ python -m fantasy_ga --filepath=examples/DraftKings/DKSalarie.csv --site=DraftKings --n_pop=100 --n_gen=5 --n_breed=100 --n_mutate=100 --n_compound=10 --top_n_lineups=1 > PlayerIDs: [ 17. 106. 70. 0. 63. 33. 1. 108.], FPTS: 308.62082 ``` or a command ``` -$ fantasy-ga --filepath=examples/mat.csv --n_pop=100 --n_gen=5 --n_breed=100 --n_mutate=100 --n_compound=10 --top_n_lineups=1 +$ fantasy-ga --filepath=examples/DraftKings/DKSalarie.csv --site=DraftKings --n_pop=100 --n_gen=5 --n_breed=100 --n_mutate=100 --n_compound=10 --top_n_lineups=1 > PlayerIDs: [ 17. 106. 70. 0. 63. 33. 1. 108.], FPTS: 308.62082 ``` \ No newline at end of file diff --git a/examples/DraftKings/DKSalaries.csv b/examples/DraftKings/DKSalaries.csv new file mode 100644 index 0000000..e715491 --- /dev/null +++ b/examples/DraftKings/DKSalaries.csv @@ -0,0 +1,67 @@ +Position,Name + ID,Name,ID,Roster Position,Salary,Game Info,TeamAbbrev,AvgPointsPerGame +C,Nikola Jokic (26802940),Nikola Jokic,26802940,C/UTIL,11800,LAC@DEN 02/26/2023 10:00PM ET,DEN,59.03 +PG,Damian Lillard (26802894),Damian Lillard,26802894,PG/G/UTIL,11400,HOU@POR 02/26/2023 09:00PM ET,POR,50.91 +SF/PF,Kawhi Leonard (26802907),Kawhi Leonard,26802907,SF/PF/F/UTIL,9300,LAC@DEN 02/26/2023 10:00PM ET,LAC,40.65 +SG/SF,Paul George (26802900),Paul George,26802900,SG/SF/F/G/UTIL,8300,LAC@DEN 02/26/2023 10:00PM ET,LAC,42.82 +PG,Jamal Murray (26802961),Jamal Murray,26802961,PG/G/UTIL,8200,LAC@DEN 02/26/2023 10:00PM ET,DEN,36.33 +C,Alperen Sengun (26803068),Alperen Sengun,26803068,C/UTIL,7800,HOU@POR 02/26/2023 09:00PM ET,HOU,34.54 +PG,Kevin Porter Jr. (26803006),Kevin Porter Jr.,26803006,PG/G/UTIL,7600,HOU@POR 02/26/2023 09:00PM ET,HOU,37.41 +PF,Aaron Gordon (26802935),Aaron Gordon,26802935,PF/F/UTIL,7500,LAC@DEN 02/26/2023 10:00PM ET,DEN,33.40 +PG/SG,Anfernee Simons (26802997),Anfernee Simons,26802997,PG/SG/G/UTIL,7200,HOU@POR 02/26/2023 09:00PM ET,POR,33.56 +PG,Russell Westbrook (26802873),Russell Westbrook,26802873,PG/G/UTIL,7100,LAC@DEN 02/26/2023 10:00PM ET,LAC,37.52 +PF,Jerami Grant (26802932),Jerami Grant,26802932,PF/F/UTIL,7000,HOU@POR 02/26/2023 09:00PM ET,POR,33.14 +PG/SG,Jalen Green (26803059),Jalen Green,26803059,PG/SG/G/UTIL,6500,HOU@POR 02/26/2023 09:00PM ET,HOU,34.04 +C,Jusuf Nurkic (26802938),Jusuf Nurkic,26802938,C/UTIL,6300,HOU@POR 02/26/2023 09:00PM ET,POR,33.11 +SF,Michael Porter Jr. (26802984),Michael Porter Jr.,26802984,SF/F/UTIL,6000,LAC@DEN 02/26/2023 10:00PM ET,DEN,28.17 +PF,Jabari Smith Jr. (26803082),Jabari Smith Jr.,26803082,PF/F/UTIL,5800,HOU@POR 02/26/2023 09:00PM ET,HOU,25.80 +SG/SF,Norman Powell (26802924),Norman Powell,26802924,SG/SF/F/G/UTIL,5600,LAC@DEN 02/26/2023 10:00PM ET,LAC,26.06 +C,Ivica Zubac (26802966),Ivica Zubac,26802966,C/UTIL,5500,LAC@DEN 02/26/2023 10:00PM ET,LAC,27.52 +SF/PF,KJ Martin (26803055),KJ Martin,26803055,SF/PF/F/UTIL,5500,HOU@POR 02/26/2023 09:00PM ET,HOU,22.78 +C,Mason Plumlee (26802905),Mason Plumlee,26802905,C/UTIL,5100,LAC@DEN 02/26/2023 10:00PM ET,LAC,31.69 +PG/SG,Terance Mann (26802950),Terance Mann,26802950,PG/SG/G/UTIL,4600,LAC@DEN 02/26/2023 10:00PM ET,LAC,18.88 +C,Drew Eubanks (26802954),Drew Eubanks,26802954,C/UTIL,4600,HOU@POR 02/26/2023 09:00PM ET,POR,16.96 +SG,Kentavious Caldwell-Pope (26802917),Kentavious Caldwell-Pope,26802917,SG/G/UTIL,4500,LAC@DEN 02/26/2023 10:00PM ET,DEN,22.81 +SG/SF,Bruce Brown (26802971),Bruce Brown,26802971,SG/SF/F/G/UTIL,4500,LAC@DEN 02/26/2023 10:00PM ET,DEN,24.53 +PG/SG,Reggie Jackson (26802890),Reggie Jackson,26802890,PG/SG/G/UTIL,4400,LAC@DEN 02/26/2023 10:00PM ET,DEN,20.30 +SG/SF,Eric Gordon (26802881),Eric Gordon,26802881,SG/SF/F/G/UTIL,4300,LAC@DEN 02/26/2023 10:00PM ET,LAC,21.95 +SG/SF,Jae'Sean Tate (26802945),Jae'Sean Tate,26802945,SG/SF/F/G/UTIL,4200,HOU@POR 02/26/2023 09:00PM ET,HOU,17.81 +SG/SF,Cam Reddish (26803001),Cam Reddish,26803001,SG/SF/F/G/UTIL,4100,HOU@POR 02/26/2023 09:00PM ET,POR,15.91 +SF/PF,Tari Eason (26803047),Tari Eason,26803047,SF/PF/F/UTIL,4100,HOU@POR 02/26/2023 09:00PM ET,HOU,20.16 +SF/PF,Nassir Little (26803015),Nassir Little,26803015,SF/PF/F/UTIL,4000,HOU@POR 02/26/2023 09:00PM ET,POR,12.72 +PG/SG,Shaedon Sharpe (26803087),Shaedon Sharpe,26803087,PG/SG/G/UTIL,4000,HOU@POR 02/26/2023 09:00PM ET,POR,13.51 +PG,TyTy Washington Jr. (26803079),TyTy Washington Jr.,26803079,PG/G/UTIL,3700,HOU@POR 02/26/2023 09:00PM ET,HOU,9.66 +PF,Marcus Morris Sr. (26802897),Marcus Morris Sr.,26802897,PF/F/UTIL,3600,LAC@DEN 02/26/2023 10:00PM ET,LAC,22.84 +SF/PF,Vlatko Cancar (26802980),Vlatko Cancar,26802980,SF/PF/F/UTIL,3600,LAC@DEN 02/26/2023 10:00PM ET,DEN,12.29 +SG/SF,Matisse Thybulle (26802956),Matisse Thybulle,26802956,SG/SF/F/G/UTIL,3600,HOU@POR 02/26/2023 09:00PM ET,POR,8.29 +PF/C,Nicolas Batum (26802886),Nicolas Batum,26802886,PF/C/F/UTIL,3500,LAC@DEN 02/26/2023 10:00PM ET,LAC,16.75 +SG/SF,Keon Johnson (26803038),Keon Johnson,26803038,SG/SF/F/G/UTIL,3500,HOU@POR 02/26/2023 09:00PM ET,POR,8.63 +C,Trendon Watford (26803024),Trendon Watford,26803024,C/UTIL,3400,HOU@POR 02/26/2023 09:00PM ET,POR,13.17 +SG/SF,Josh Christopher (26803033),Josh Christopher,26803033,SG/SF/F/G/UTIL,3400,HOU@POR 02/26/2023 09:00PM ET,HOU,8.48 +PF,Jeff Green (26802870),Jeff Green,26802870,PF/F/UTIL,3200,LAC@DEN 02/26/2023 10:00PM ET,DEN,14.11 +PG,Ryan Arcidiacono (26802929),Ryan Arcidiacono,26802929,PG/G/UTIL,3200,HOU@POR 02/26/2023 09:00PM ET,POR,2.93 +SG/SF,Christian Braun (26803019),Christian Braun,26803019,SG/SF/F/G/UTIL,3100,LAC@DEN 02/26/2023 10:00PM ET,DEN,9.93 +PF/C,Jabari Walker (26803043),Jabari Walker,26803043,PF/C/F/UTIL,3100,HOU@POR 02/26/2023 09:00PM ET,POR,6.67 +SG,Daishen Nix (26803063),Daishen Nix,26803063,SG/G/UTIL,3100,HOU@POR 02/26/2023 09:00PM ET,HOU,10.99 +PG,Ish Smith (26802876),Ish Smith,26802876,PG/G/UTIL,3000,LAC@DEN 02/26/2023 10:00PM ET,DEN,8.38 +C,DeAndre Jordan (26802879),DeAndre Jordan,26802879,C/UTIL,3000,LAC@DEN 02/26/2023 10:00PM ET,DEN,13.62 +PF/C,Robert Covington (26802911),Robert Covington,26802911,PF/C/F/UTIL,3000,LAC@DEN 02/26/2023 10:00PM ET,LAC,15.15 +C,Thomas Bryant (26802964),Thomas Bryant,26802964,C/UTIL,3000,LAC@DEN 02/26/2023 10:00PM ET,DEN,22.24 +PF,Jack White (26802968),Jack White,26802968,PF/F/UTIL,3000,LAC@DEN 02/26/2023 10:00PM ET,DEN,2.6 +SF/PF,Amir Coffey (26802976),Amir Coffey,26802976,SF/PF/F/UTIL,3000,LAC@DEN 02/26/2023 10:00PM ET,LAC,7.87 +PG,Keaton Wallace (26802987),Keaton Wallace,26802987,PG/G/UTIL,3000,LAC@DEN 02/26/2023 10:00PM ET,LAC,0 +PG,Collin Gillespie (26802994),Collin Gillespie,26802994,PG/G/UTIL,3000,LAC@DEN 02/26/2023 10:00PM ET,DEN,0 +PG,Jason Preston (26803009),Jason Preston,26803009,PG/G/UTIL,3000,LAC@DEN 02/26/2023 10:00PM ET,LAC,8.5 +PG,Bones Hyland (26803026),Bones Hyland,26803026,PG/G/UTIL,3000,LAC@DEN 02/26/2023 10:00PM ET,LAC,21.14 +PF/C,Zeke Nnaji (26803029),Zeke Nnaji,26803029,PF/C/F/UTIL,3000,LAC@DEN 02/26/2023 10:00PM ET,DEN,9.84 +PG/SG,Brandon Boston Jr. (26803051),Brandon Boston Jr.,26803051,PG/SG/G/UTIL,3000,LAC@DEN 02/26/2023 10:00PM ET,LAC,10.75 +PF/C,Moussa Diabate (26803070),Moussa Diabate,26803070,PF/C/F/UTIL,3000,LAC@DEN 02/26/2023 10:00PM ET,LAC,7.75 +SG,Peyton Watson (26803074),Peyton Watson,26803074,SG/G/UTIL,3000,LAC@DEN 02/26/2023 10:00PM ET,DEN,2.73 +C,Boban Marjanovic (26802915),Boban Marjanovic,26802915,C/UTIL,3000,HOU@POR 02/26/2023 09:00PM ET,HOU,5.25 +PF/C,Frank Kaminsky III (26802920),Frank Kaminsky III,26802920,PF/C/F/UTIL,3000,HOU@POR 02/26/2023 09:00PM ET,HOU,6.43 +SF,Justise Winslow (26802942),Justise Winslow,26802942,SF/F/UTIL,3000,HOU@POR 02/26/2023 09:00PM ET,POR,20.46 +SF/PF,Kevin Knox II (26802990),Kevin Knox II,26802990,SF/PF/F/UTIL,3000,HOU@POR 02/26/2023 09:00PM ET,POR,10.24 +PF,Darius Days (26803012),Darius Days,26803012,PF/F/UTIL,3000,HOU@POR 02/26/2023 09:00PM ET,HOU,6.88 +C,Usman Garuba (26803066),Usman Garuba,26803066,C/UTIL,3000,HOU@POR 02/26/2023 09:00PM ET,HOU,12.35 +C,John Butler (26803077),John Butler,26803077,C/UTIL,3000,HOU@POR 02/26/2023 09:00PM ET,POR,1.56 +C,Ibou Badji (26803085),Ibou Badji,26803085,C/UTIL,3000,HOU@POR 02/26/2023 09:00PM ET,POR,0 +PG,Trevor Hudgins (26803091),Trevor Hudgins,26803091,PG/G/UTIL,3000,HOU@POR 02/26/2023 09:00PM ET,HOU,2.8 diff --git a/examples/generate_lineups.py b/examples/generate_lineups.py index 36b5b61..530ebe9 100644 --- a/examples/generate_lineups.py +++ b/examples/generate_lineups.py @@ -1,11 +1,8 @@ -#!/usr/bin/env python3 - -import numpy as np -from fantasy_ga import LineupGenerator +from fantasy_ga import LineupGenerator, read_csv if __name__ == "__main__": - # load data - m = np.loadtxt("examples/mat.csv", delimiter=",", skiprows=1) + # load data from DraftKings salary csv + id_to_name, m = read_csv("examples/DraftKings/DKSalaries.csv", site="DraftKings") n_pop = 1000 n_breed = 30 n_mutate = 30 @@ -14,9 +11,9 @@ pos_start_idx = 3 model = LineupGenerator(m, n_pop, n_gen, n_breed, n_mutate, n_compound) - lineups, fit = model.compound() - optimal_lineups, top_n_scores = model.get_top_n_lineups(lineups, fit, 1) + lineups, scores = model.fit() + optimal_lineups, top_n_scores = model.get_top_n_lineups(lineups, scores, 1) print( - f"[Optimal Lineup]\nPlayer IDs: {optimal_lineups[0]}\nFPTS: {top_n_scores[0]}" + f"Players: {[id_to_name[id] for id in optimal_lineups[0]]}\nFPTS: {top_n_scores[0]}" ) diff --git a/examples/mat.csv b/examples/mat.csv deleted file mode 100644 index c591723..0000000 --- a/examples/mat.csv +++ /dev/null @@ -1,117 +0,0 @@ -player_id,salary,fpts,PG,SG,SF,PF,C,G,F,Util -0,6600.0,36.46503,0,0,0,1,1,0,1,1 -1,4200.0,26.760368,0,0,1,1,0,0,1,1 -2,3000.0,4.38538,1,1,0,0,0,1,0,1 -3,5000.0,27.175564,0,0,0,0,1,0,0,1 -4,3400.0,16.734577,0,1,1,0,0,1,1,1 -5,5900.0,3.4382372,0,1,1,0,0,1,1,1 -6,3000.0,-0.18490964,1,1,0,0,0,1,0,1 -7,3000.0,11.075589,0,0,1,1,0,0,1,1 -8,3000.0,6.469783,0,0,0,0,1,0,0,1 -9,3000.0,8.459954,0,0,0,0,1,0,0,1 -10,5700.0,33.98281,0,0,0,1,1,0,1,1 -11,3000.0,2.723007,1,1,0,0,0,1,0,1 -12,9700.0,53.04824,0,1,0,0,0,1,0,1 -13,5400.0,26.987614,0,0,0,0,1,0,0,1 -14,5300.0,4.511323,1,0,1,0,0,1,1,1 -15,3000.0,4.2247944,1,1,0,0,0,1,0,1 -16,5400.0,31.739548,0,0,0,0,1,0,0,1 -17,8100.0,49.793514,1,1,0,0,0,1,0,1 -18,3000.0,1.6884186,0,0,0,1,1,0,1,1 -19,8800.0,55.447075,1,0,0,0,0,1,0,1 -20,3100.0,6.693487,0,0,0,1,1,0,1,1 -21,3900.0,22.403994,0,0,0,1,0,0,1,1 -22,3100.0,5.1051073,1,0,0,0,0,1,0,1 -23,3900.0,16.7142,0,0,1,1,0,0,1,1 -24,6500.0,36.989887,0,0,0,0,1,0,0,1 -25,4900.0,8.001146,1,1,0,0,0,1,0,1 -26,8400.0,46.367416,1,1,0,0,0,1,0,1 -27,3000.0,3.565782,1,1,0,0,0,1,0,1 -28,3000.0,1.5881665,1,0,1,0,0,1,1,1 -29,3000.0,13.594312,0,0,0,1,1,0,1,1 -30,3000.0,4.888688,0,1,1,0,0,1,1,1 -31,3200.0,10.518579,0,0,0,0,1,0,0,1 -32,4000.0,9.680565,0,0,0,0,1,0,0,1 -33,6300.0,40.61529,1,0,0,0,0,1,0,1 -34,3200.0,14.426795,0,0,0,1,1,0,1,1 -35,4400.0,8.9096575,0,0,0,1,1,0,1,1 -36,3000.0,4.842949,1,1,0,0,0,1,0,1 -37,11300.0,49.8009,0,0,1,1,0,0,1,1 -38,4300.0,11.58001,0,0,1,1,0,0,1,1 -39,3100.0,4.2896495,0,0,0,1,1,0,1,1 -40,3000.0,3.9184914,0,0,0,1,1,0,1,1 -41,3000.0,0.8968501,0,0,0,0,1,0,0,1 -42,3000.0,0.99253654,1,0,1,0,0,1,1,1 -43,4300.0,23.873348,0,0,0,0,1,0,0,1 -44,5300.0,19.438892,0,0,1,1,0,0,1,1 -45,3200.0,12.133769,0,0,1,1,0,0,1,1 -46,3000.0,6.711598,1,1,0,0,0,1,0,1 -47,3000.0,2.5235684,0,0,1,1,0,0,1,1 -48,4800.0,27.230423,0,0,0,0,1,0,0,1 -49,3100.0,13.744759,1,0,0,0,0,1,0,1 -50,4600.0,14.92864,0,1,1,0,0,1,1,1 -51,6000.0,33.618874,0,0,1,1,0,0,1,1 -52,4500.0,24.54412,0,0,0,1,1,0,1,1 -53,5800.0,35.539524,1,0,0,0,0,1,0,1 -54,5900.0,1.5064497,0,1,1,0,0,1,1,1 -55,3000.0,2.1067536,1,1,0,0,0,1,0,1 -56,4200.0,22.726187,0,1,1,0,0,1,1,1 -57,7200.0,41.673916,0,0,0,1,1,0,1,1 -58,3000.0,4.3885193,0,0,0,1,1,0,1,1 -59,3600.0,13.55876,0,1,1,0,0,1,1,1 -60,4100.0,15.501369,0,0,1,1,0,0,1,1 -61,3300.0,17.748304,0,1,1,0,0,1,1,1 -62,3000.0,4.5180635,0,0,1,0,0,0,1,1 -63,7000.0,43.691612,0,0,0,0,1,0,0,1 -64,10400.0,58.72256,0,0,0,0,1,0,0,1 -65,3200.0,16.956982,0,0,1,0,0,0,1,1 -66,6200.0,37.39934,0,1,1,0,0,1,1,1 -67,8300.0,48.804142,1,0,0,0,0,1,0,1 -68,5100.0,24.905209,0,1,1,0,0,1,1,1 -69,3200.0,16.86441,0,1,1,0,0,1,1,1 -70,4000.0,24.2682,0,1,1,0,0,1,1,1 -71,6900.0,42.351025,0,1,1,0,0,1,1,1 -72,9000.0,42.09257,1,0,0,0,0,1,0,1 -73,10800.0,59.728188,0,0,1,1,0,0,1,1 -74,5500.0,30.543383,1,1,0,0,0,1,0,1 -75,3000.0,3.780979,1,1,0,0,0,1,0,1 -76,4400.0,24.909985,0,0,1,1,0,0,1,1 -77,5200.0,27.519306,1,1,0,0,0,1,0,1 -78,4700.0,27.407288,0,0,1,1,0,0,1,1 -79,4200.0,26.735838,0,0,1,1,0,0,1,1 -80,3000.0,4.400921,0,0,0,1,1,0,1,1 -81,3900.0,14.739421,0,1,1,0,0,1,1,1 -82,3000.0,5.1543717,0,0,0,1,1,0,1,1 -83,3100.0,19.541342,0,0,1,1,0,0,1,1 -84,3000.0,13.442607,0,0,0,1,1,0,1,1 -85,5700.0,32.0483,0,1,1,0,0,1,1,1 -86,4700.0,18.733253,0,0,1,1,0,0,1,1 -87,3200.0,10.434569,0,1,1,0,0,1,1,1 -88,3000.0,1.9844115,0,0,0,0,1,0,0,1 -89,6700.0,39.11975,1,0,0,0,0,1,0,1 -90,3000.0,-0.23641694,0,0,0,1,0,0,1,1 -91,4000.0,11.97461,0,1,1,0,0,1,1,1 -92,3400.0,14.11885,0,0,0,1,1,0,1,1 -93,4600.0,22.652382,0,0,1,1,0,0,1,1 -94,3300.0,7.4123487,0,1,1,0,0,1,1,1 -95,3100.0,6.045568,0,0,0,1,1,0,1,1 -96,3000.0,9.351659,0,0,1,1,0,0,1,1 -97,3000.0,4.703019,0,0,1,1,0,0,1,1 -98,3100.0,9.391193,1,1,0,0,0,1,0,1 -99,3000.0,3.163245,1,1,0,0,0,1,0,1 -100,3000.0,0.81587124,0,0,0,1,1,0,1,1 -101,5600.0,1.3123381,1,1,0,0,0,1,0,1 -102,4000.0,19.90596,0,0,0,1,1,0,1,1 -103,5500.0,5.4211426,1,1,0,0,0,1,0,1 -104,3000.0,5.912597,0,1,1,0,0,1,1,1 -105,4400.0,17.321472,0,0,0,1,1,0,1,1 -106,5600.0,32.826046,1,1,0,0,0,1,0,1 -107,3100.0,8.75638,0,1,1,0,0,1,1,1 -108,8200.0,54.20076,1,0,0,0,0,1,0,1 -109,5800.0,33.379597,0,0,1,1,0,0,1,1 -110,3200.0,8.071403,0,1,0,0,0,1,0,1 -111,5300.0,28.61013,1,1,0,0,0,1,0,1 -112,3700.0,11.533555,1,0,0,0,0,1,0,1 -113,3500.0,17.753204,0,0,1,1,0,0,1,1 -114,3000.0,2.8501549,0,1,1,0,0,1,1,1 -115,3000.0,4.055771,0,0,0,1,1,0,1,1 diff --git a/fantasy_ga/__init__.py b/fantasy_ga/__init__.py index fea7536..d9c4a84 100644 --- a/fantasy_ga/__init__.py +++ b/fantasy_ga/__init__.py @@ -1,5 +1,5 @@ -#!/usr/bin/env python3 - -from .lineup_generator import LineupGenerator +from fantasy_ga.lineup_generator import LineupGenerator +from fantasy_ga.utils import read_csv +from fantasy_ga.constants import * __version__ = "0.1.0" diff --git a/fantasy_ga/__main__.py b/fantasy_ga/__main__.py index 0465876..955fd76 100644 --- a/fantasy_ga/__main__.py +++ b/fantasy_ga/__main__.py @@ -1,13 +1,12 @@ -#!/usr/bin/env python3 - import argparse -import numpy as np -from .lineup_generator import LineupGenerator +from fantasy_ga.lineup_generator import LineupGenerator +from fantasy_ga.utils import read_csv def main(): parser = argparse.ArgumentParser() parser.add_argument("--filepath", help="filepath") + parser.add_argument("--site", help="site") parser.add_argument("--top_n_lineups", help="filepath") parser.add_argument("--n_pop", help="Do the bar option") parser.add_argument("--n_gen", help="Foo the program") @@ -18,7 +17,7 @@ def main(): args = parser.parse_args() - m = np.loadtxt(args.filepath, delimiter=",", skiprows=1) + id_to_name, m = read_csv(args.filepath, args.site) model = LineupGenerator( m=m, n_pop=int(args.n_pop), @@ -28,11 +27,11 @@ def main(): n_compound=int(args.n_compound), ) - lineups, fit = model.compound() - lineups, scores = model.get_top_n_lineups(lineups, fit, int(args.top_n_lineups)) + lineups, scores = model.fit() + lineups, scores = model.get_top_n_lineups(lineups, scores, int(args.top_n_lineups)) print(f"generated top {args.top_n_lineups} lineups") for lineup, score in zip(lineups, scores): - print(f"PlayerIDs: {lineup}, FPTS: {score}") + print(f"Players: {[id_to_name[id] for id in lineup]}, FPTS: {score}") if __name__ == "__main__": diff --git a/fantasy_ga/constants.py b/fantasy_ga/constants.py new file mode 100644 index 0000000..af30301 --- /dev/null +++ b/fantasy_ga/constants.py @@ -0,0 +1 @@ +DK_POSITIONS = ["PG", "SG", "SF", "PF", "C", "G", "F", "UTIL"] diff --git a/fantasy_ga/lineup_generator.py b/fantasy_ga/lineup_generator.py index ff0fb18..8cc3037 100644 --- a/fantasy_ga/lineup_generator.py +++ b/fantasy_ga/lineup_generator.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 - import numpy as np @@ -25,20 +23,22 @@ def __init__( self.sal_cap = sal_cap @staticmethod - def get_top_n_lineups(lineups, fit, n): - top_n_scores = (-fit).argsort()[:n] - return lineups.take(top_n_scores.astype(int), 0), fit.take( + def get_top_n_lineups(lineups, scores, n): + top_n_scores = (-scores).argsort()[:n] + return lineups.take(top_n_scores.astype(int), 0), scores.take( top_n_scores.astype(int), 0 ) - def calc_fit(self, lineups: np.array): - fit = [] + def calc_scores(self, lineups: np.array): + scores = [] for lineup in lineups: - sal, fpts = self.m.take(lineup.astype(int), 0)[:, [1, 2]].sum(axis=0) - fit.append(fpts) if sal <= self.sal_cap and len( + sal, fpts = self.m[np.in1d(self.m[:, 0], lineup.astype(int))][ + :, [1, 2] + ].sum(axis=0) + scores.append(fpts) if sal <= self.sal_cap and len( np.unique(lineup) - ) == 8 else fit.append(-1) - return np.array(fit) + ) == 8 else scores.append(-1) + return np.array(scores) def create_random_lineups(self): lineups = [] @@ -56,9 +56,9 @@ def create_random_lineups(self): lineups.append(lineup) return np.array(lineups) - def breed(self, lineups, fit): + def breed(self, lineups, scores): new_lineups = [] - parents_idx = (-fit).argsort()[:2] + parents_idx = (-scores).argsort()[:2] parents = lineups.take(parents_idx, 0) for _ in range(self.n_breed): @@ -74,8 +74,9 @@ def mutate(self, lineups): for idx in mutate_idx: mutant = self.m[np.random.choice(self.m.shape[0]), :] - original = self.m[lineups[idx][np.random.choice(8)].astype(int), :] - + original = self.m[ + self.m[:, 0] == np.random.choice(lineups[idx]).astype(int), : + ][0] eligible_pos = np.where( mutant[self.pos_start_idx :].astype(bool) & original[self.pos_start_idx :].astype(bool) @@ -85,20 +86,18 @@ def mutate(self, lineups): lineups[-1, swap_pos] = mutant[0] return lineups - def evolve(self, lineups, fit): + def evolve(self, lineups, scores): for _ in range(self.n_gen): - lineups = self.breed(lineups, fit) + lineups = self.breed(lineups, scores) lineups = self.mutate(lineups) - fit = self.calc_fit(lineups) - # print(f"gen {i}: {lineups.shape[0]} lineups generated") - return lineups, fit + scores = self.calc_scores(lineups) + return lineups, scores - def compound(self): + def fit(self): lineups = self.create_random_lineups() - # print(f"init: {lineups.shape[0]} lineups generated") - fit = self.calc_fit(lineups) + scores = self.calc_scores(lineups) for _ in range(self.n_compound): - lineups, fit = self.evolve(lineups, fit) + lineups, scores = self.evolve(lineups, scores) lineups = np.vstack([lineups, self.create_random_lineups()]) - fit = self.calc_fit(lineups) - return lineups, fit + scores = self.calc_scores(lineups) + return lineups, scores diff --git a/fantasy_ga/utils.py b/fantasy_ga/utils.py new file mode 100644 index 0000000..1af0b86 --- /dev/null +++ b/fantasy_ga/utils.py @@ -0,0 +1,31 @@ +import csv +import numpy as np +from fantasy_ga.constants import DK_POSITIONS + + +def encode_position(s: str) -> list[int]: + res = [0] * len(DK_POSITIONS) + for pos in s.split("/"): + for i in range(len(DK_POSITIONS)): + if pos == DK_POSITIONS[i]: + res[i] = 1 + break + return res + + +def read_csv(filepath: str, site: str) -> tuple[dict, list]: + with open(filepath, mode="r") as f: + reader = csv.reader(f) + _ = next(reader) + players = [] + id_to_name = {} + if site == "DraftKings": + for row in reader: + id_to_name[int(row[3])] = row[2] + players.append( + [int(row[3]), int((row[5])), float(row[8])] + + encode_position(row[4]) + ) + elif site == "FanDuel": + raise NotImplementedError + return id_to_name, np.array(players)