Skip to content

Penalty

pangadfs.penalty

Penalty framework

Idea is to replace optimizer rules with penalties. Penalties can be negative (bad) or positive (good). * Advantages * Doesn't throw away reasonable options (125% ownership arbitrary) and is flexible. * Does not require absurdly complex optimizer rules. * Can easily layer penalties on top of each other. * Disadvantages * Takes some fiddling to get the parameters correct.

Possible penalties

  • Individual ownership penalty (global or just high-owned)
  • Cumulative ownership penalty (global or just high-owned)
  • Distances (too many similar lineups)
  • Diversity (another way of measuring too many similar lineups)
  • Position combinations (QB vs DST, WR + own DST, etc.)

DistancePenalty()

Bases: PenaltyBase

Source code in pangadfs/base.py
def __init__(self):
    logging.getLogger(__name__).addHandler(logging.NullHandler())

penalty(*, population, player_weights=None)

Calculates distance penalty for overlapping lineups

Parameters:

Name Type Description Default
population ndarray

the population

required
player_weights ndarray

optional per-player weighting vector where index == player ID. If provided, weighted one-hot vectors are used for distance calculation.

None

Returns:

Type Description
ndarray

np.ndarray: 1D array of float

Source code in pangadfs/penalty.py
def penalty(self,
            *,
            population: np.ndarray,
            player_weights: np.ndarray = None) -> np.ndarray:
    """Calculates distance penalty for overlapping lineups

    Args:
        population (np.ndarray): the population
        player_weights (np.ndarray): optional per-player weighting vector where
            index == player ID. If provided, weighted one-hot vectors are used
            for distance calculation.

    Returns:
        np.ndarray: 1D array of float
    """
    # one-hot encoded population
    # so, assume pool has ids 0, 1, 2, 3, 4
    # lineup is 1, 2
    # ohe would be [0, 1, 1, 0, 0] for that lineup
    ohe = np.sum((np.arange(population.max()) == population[...,None]-1).astype(int), axis=1)

    if player_weights is not None:
        player_weights = np.asarray(player_weights, dtype=float)
        n_cols = ohe.shape[1]
        if player_weights.shape[0] < n_cols:
            raise ValueError('player_weights must be at least population.max() in length')
        ohe = ohe * player_weights[:n_cols]

    # now calculate distance between individuals in population
    # dist is a square matrix same length as population
    b = ohe.reshape(ohe.shape[0], 1, ohe.shape[1])
    dist = np.sqrt(np.einsum('ijk, ijk->ij', ohe-b, ohe-b))
    return 0 - ((dist - dist.mean()) / dist.std())

DiversityPenalty()

Bases: PenaltyBase

Source code in pangadfs/base.py
def __init__(self):
    logging.getLogger(__name__).addHandler(logging.NullHandler())

penalty(*, population)

Calculates diversity penalty for overlapping lineups

Parameters:

Name Type Description Default
population ndarray

the population

required

Returns:

Type Description
ndarray

np.ndarray: 1D array of float

Source code in pangadfs/penalty.py
def penalty(self, *, population: np.ndarray) -> np.ndarray:
    """Calculates diversity penalty for overlapping lineups

    Args:
        population (np.ndarray): the population

    Returns:
        np.ndarray: 1D array of float

    """
    uniques = np.unique(population)
    a = (population[..., None] == uniques).sum(1)
    out = np.einsum('ij,kj->ik', a, a)
    diversity = np.sum(out, axis=1) / population.size
    return 0 - ((diversity - diversity.mean()) / diversity.std())

OwnershipPenalty()

Bases: PenaltyBase

Source code in pangadfs/base.py
def __init__(self):
    logging.getLogger(__name__).addHandler(logging.NullHandler())

penalty(*, ownership, base=3, boost=2)

Calculates penalties that are inverse to projected ownership

Parameters:

Name Type Description Default
ownership ndarray

1D array of ownership

required
base int

the logarithm base, default 3

3
boost int

the constant to boost low-owned players

2

Returns:

Type Description
ndarray

np.ndarray: 1D array of penalties

Source code in pangadfs/penalty.py
def penalty(self, *, ownership: np.ndarray, base: float =3, boost: float = 2) -> np.ndarray:
    """Calculates penalties that are inverse to projected ownership

    Args:
        ownership (np.ndarray): 1D array of ownership
        base (int): the logarithm base, default 3
        boost (int): the constant to boost low-owned players

    Returns:
        np.ndarray: 1D array of penalties

    """
    return 0 - np.log(ownership) / np.log(base) + boost