pangadfs is a pandas-based (python) genetic algorithm framework for fantasy sports. It uses a plugin architecture to enable maximum flexibility while also providing a fully-functional implementation of a genetic algorithm for lineup optimization.
Documentation: https://sansbacon.github.io/pangadfs/
Source Code: https://github.com/sansbacon/pangadfs
The key pangadfs features are:
- Fast: takes advantage of pandas and numpy to generate thousands of lineups quickly.
- Extensible: any desired functionality can be added with a straightforward plugin architecture.
- Pythonic: library is easy to use and extend as long as you are familiar with data analysis in python (pandas and numpy). You don't also have to be an expert in linear programming.
- Fewer bugs: Small core means fewer bugs and easier to trace code. Unlike other optimizers, pangadfs does not generate complicated equations behind the curtain that are difficult to comprehend and debug.
- Python 3.8+
- pandas 1.0+
- numpy 1.19+
- stevedore 3.30+
- numpy-indexed 0.3+
$ pip install pangadfs
A simple pangadfs optimizer app could look like the following
# pangadfs/app/app.py
from pangadfs.ga import GeneticAlgorithm
from pathlib import Path
import numpy as np
from stevedore.driver import DriverManager
from stevedore.named import NamedExtensionManager
def run():
"""Example optimizer application using pangadfs"""
ctx = {
'ga_settings': {
'crossover_method': 'uniform',
'csvpth': Path(__file__).parent / 'appdata' / 'pool.csv',
'elite_divisor': 5,
'elite_method': 'fittest',
'mutation_rate': .05,
'n_generations': 20,
'points_column': 'proj',
'population_size': 30000,
'position_column': 'pos',
'salary_column': 'salary',
'select_method': 'roulette',
'stop_criteria': 10,
'verbose': True
},
'site_settings': {
'flex_positions': ('RB', 'WR', 'TE'),
'lineup_size': 9,
'posfilter': {'QB': 14, 'RB': 8, 'WR': 8, 'TE': 5, 'DST': 4, 'FLEX': 8},
'posmap': {'DST': 1, 'QB': 1, 'TE': 1, 'RB': 2, 'WR': 3, 'FLEX': 7},
'salary_cap': 50000
}
}
# set up driver managers
dmgrs = {}
emgrs = {}
for ns in GeneticAlgorithm.PLUGIN_NAMESPACES:
pns = f'pangadfs.{ns}'
if ns == 'validate':
emgrs['validate'] = NamedExtensionManager(
namespace=pns,
names=['validate_salary', 'validate_duplicates'],
invoke_on_load=True,
name_order=True)
else:
dmgrs[ns] = DriverManager(
namespace=pns,
name=f'{ns}_default',
invoke_on_load=True)
# set up GeneticAlgorithm object
ga = GeneticAlgorithm(ctx=ctx, driver_managers=dmgrs, extension_managers=emgrs)
# run optimizer
results = ga.optimize()
# show best score and lineup at conclusion
# will break after n_generations or when stop_criteria reached
print(results['best_lineup'])
print(f'Lineup score: {results["best_score"]}')
if __name__ == '__main__':
run()
Run the sample application with:
$ python {pangadfs_directory}/app/app.py
INFO:root:Starting generation 1
INFO:root:Best lineup score 153.00000000000003
INFO:root:Lineup unimproved 1 times
INFO:root:Starting generation 2
INFO:root:Best lineup score 153.00000000000003
INFO:root:Lineup improved to 155.2
. . .
INFO:root:Starting generation 19
INFO:root:Best lineup score 156.3
INFO:root:Lineup improved to 156.5
INFO:root:Starting generation 20
INFO:root:Best lineup score 156.5
INFO:root:Lineup unimproved 1 times
player team pos salary proj
0 Saints NO DST 3800 9.8
34 Patrick Mahomes KC QB 8000 26.6
62 Dalvin Cook MIN RB 9500 27.2
68 Nyheim Hines IND RB 4600 15.9
72 Brian Hill ATL RB 4000 12.8
109 Gabriel Davis BUF WR 3000 10.7
136 Keelan Cole Sr. JAX WR 3600 11.9
138 Calvin Ridley ATL WR 7100 21.6
142 Justin Jefferson MIN WR 6300 20.0
Lineup score: 156.5
pangadfs is extensible by design and is motivated by difficulties I encountered with other optimizers, which tend to have a monolithic design and don't make it easy to swap out components.
This flexibility is made possible by the stevedore plugin system, which allows allow applications to customize one or more of the internal components.
As recommended by the stevedore documentation, the base module includes base classes to define each pluggable component. Each namespace has a default implementation (crossover, fitness, mutate, select, and so forth), which, collectively, provide a fully-functional implementation of a genetic algorithm.
This project is licensed under the terms of the MIT license.