Skip to content

Commit

Permalink
Add win and tie weights to Elo
Browse files Browse the repository at this point in the history
  • Loading branch information
dustalov committed Aug 24, 2024
1 parent 9ca3569 commit f08b3d9
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 12 deletions.
14 changes: 12 additions & 2 deletions python/evalica/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,8 @@ class EloResult(Generic[T]):
Attributes:
scores: The element scores.
index: The index.
win_weight: The win weight.
tie_weight: The tie weight.
initial: The initial score of each element.
base: The base of the exponent.
scale: The scale factor.
Expand All @@ -493,6 +495,8 @@ class EloResult(Generic[T]):

scores: pd.Series[float]
index: dict[T, int]
win_weight: float
tie_weight: float
initial: float
base: float
scale: float
Expand All @@ -505,6 +509,8 @@ def elo(
ys: Collection[T],
ws: Collection[Winner],
index: dict[T, int] | None = None,
win_weight: float = 1.0,
tie_weight: float = 0.5,
initial: float = 1000.,
base: float = 10.,
scale: float = 400.,
Expand All @@ -522,6 +528,8 @@ def elo(
ys: The right-hand side elements.
ws: The winner elements.
index: The index.
win_weight: The win weight.
tie_weight: The tie weight.
initial: The initial score of each element.
base: The base of the exponent.
scale: The scale factor.
Expand All @@ -537,13 +545,15 @@ def elo(
assert index is not None, "index is None"

if solver == "pyo3":
scores = elo_pyo3(xs_indexed, ys_indexed, ws, len(index), initial, base, scale, k)
scores = elo_pyo3(xs_indexed, ys_indexed, ws, len(index), win_weight, tie_weight, initial, base, scale, k)
else:
scores = elo_naive(xs_indexed, ys_indexed, ws, len(index), initial, base, scale, k)
scores = elo_naive(xs_indexed, ys_indexed, ws, len(index), win_weight, tie_weight, initial, base, scale, k)

return EloResult(
scores=pd.Series(scores, index=index, name=elo.__name__).sort_values(ascending=False, kind="stable"),
index=index,
win_weight=win_weight,
tie_weight=tie_weight,
initial=initial,
base=base,
scale=scale,
Expand Down
2 changes: 2 additions & 0 deletions python/evalica/evalica.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ def elo_pyo3(
ys: npt.ArrayLike,
ws: Collection[Winner],
total: int,
win_weight: float,
tie_weight: float,
initial: float,
base: float,
scale: float,
Expand Down
10 changes: 5 additions & 5 deletions python/evalica/naive.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ def elo(
ys: Collection[int],
ws: Collection[Winner],
total: int,
win_weight: float = 1.0,
tie_weight: float = 0.5,
initial: float = 1000.,
base: float = 10.,
scale: float = 400.,
Expand All @@ -169,13 +171,11 @@ def elo(
scored_x, scored_y = 0., 0.

if w == Winner.X:
scored_x, scored_y = 1., 0.
scored_x = win_weight
elif w == Winner.Y:
scored_x, scored_y = 0., 1.
scored_y = win_weight
elif w == Winner.Draw:
scored_x, scored_y = .5, .5
else:
continue
scored_x = scored_y = tie_weight

scores[x] += k * (scored_x - expected_x)
scores[y] += k * (scored_y - expected_y)
Expand Down
8 changes: 8 additions & 0 deletions python/evalica/test_evalica.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,13 +207,17 @@ def test_newman(comparison: Comparison, v_init: float) -> None:

@given(
comparison=comparisons(),
win_weight=st.floats(0., 10.),
tie_weight=st.floats(0., 10.),
initial=st.floats(0., 1000.),
base=st.floats(0., 1000.),
scale=st.floats(0., 1000.),
k=st.floats(0., 1000.),
)
def test_elo(
comparison: Comparison,
win_weight: float,
tie_weight: float,
initial: float,
base: float,
scale: float,
Expand All @@ -223,6 +227,8 @@ def test_elo(

result_pyo3 = evalica.elo(
xs, ys, ws,
win_weight=win_weight,
tie_weight=tie_weight,
initial=initial,
base=base,
scale=scale,
Expand All @@ -232,6 +238,8 @@ def test_elo(

result_naive = evalica.elo(
xs, ys, ws,
win_weight=win_weight,
tie_weight=tie_weight,
initial=initial,
base=base,
scale=scale,
Expand Down
12 changes: 7 additions & 5 deletions src/elo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ pub fn elo<A: Float + AddAssign>(
ys: &ArrayView1<usize>,
ws: &ArrayView1<Winner>,
total: usize,
win_weight: A,
tie_weight: A,
initial: A,
base: A,
scale: A,
Expand All @@ -25,8 +27,6 @@ pub fn elo<A: Float + AddAssign>(

let mut scores = Array1::from_elem(total, initial);

let half = A::one() / (A::one() + A::one());

for ((x, y), &ref w) in xs.iter().zip(ys.iter()).zip(ws.iter()) {
let q_x = one_nan_to_num(base.powf(scores[*x] / scale), A::zero());
let q_y = one_nan_to_num(base.powf(scores[*y] / scale), A::zero());
Expand All @@ -37,9 +37,9 @@ pub fn elo<A: Float + AddAssign>(
let expected_y = one_nan_to_num(q_y / q, A::zero());

let (scored_x, scored_y) = match w {
Winner::X => (A::one(), A::zero()),
Winner::Y => (A::zero(), A::one()),
Winner::Draw => (half, half),
Winner::X => (win_weight, A::zero()),
Winner::Y => (A::zero(), win_weight),
Winner::Draw => (tie_weight, tie_weight),
};

scores[*x] += k * (scored_x - expected_x);
Expand Down Expand Up @@ -72,6 +72,8 @@ mod tests {
&ys.view(),
&ws.view(),
5,
1.0,
0.5,
initial,
base,
scale,
Expand Down
4 changes: 4 additions & 0 deletions src/python.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@ fn elo_pyo3<'py>(
ys: PyArrayLike1<'py, usize>,
ws: PyArrayLike1<'py, Winner>,
total: usize,
win_weight: f64,
tie_weight: f64,
initial: f64,
base: f64,
scale: f64,
Expand All @@ -205,6 +207,8 @@ fn elo_pyo3<'py>(
&ys.as_array(),
&ws.as_array(),
total,
win_weight,
tie_weight,
initial,
base,
scale,
Expand Down

0 comments on commit f08b3d9

Please sign in to comment.