philihp / openskill.ex

Elixir implementation of Weng-Lin Bayesian ranking, a better, license-free alternative to TrueSkill
https://hex.pm/packages/openskill
MIT License
11 stars 6 forks source link

Add support for predict_win #24

Open jauggy opened 5 months ago

jauggy commented 5 months ago

I know that predict_win is in the javascript version of openskill library. Is it possible to also have this function inside the Elixir library?

Jonovono commented 5 months ago

That would be great, struggling porting it over as a elixir noob

Jonovono commented 5 months ago

Hey @jauggy, this is what I ended up doing:

  @doc """
  Predict the win probability for each team.

  ## Examples

      iex> teams = [
      ...>   [{25, 8.333}, {30, 6.666}],
      ...>   [{27, 7.0}, {28, 5.5}]
      ...> ]
      iex> Openskill.predict_win(teams)
      [0.7310585786300049, 0.2689414213699951]

  """
  @spec predict_win([[{float(), float()}]]) :: [float()]
  def predict_win(teams) do
    # team_ratings = teams |> Enum.map(&Openskill.Util.team_rating/1) |> List.flatten()
    team_ratings = Openskill.Util.team_rating(teams)
    n = length(teams)
    denom = (n * (n - 1)) / 2
    betasq = @env.beta * @env.beta

    team_ratings
    |> Enum.with_index()
    |> Enum.map(fn {{mu_a, sigma_sq_a, _team, _i}, i} ->
      sum =
        team_ratings
        |> Enum.with_index()
        |> Enum.filter(fn {_, q} -> i != q end)
        |> Enum.map(fn {{mu_b, sigma_sq_b, _team, _i}, _} ->
          Statistics.phi_major((mu_a - mu_b) / :math.sqrt(n * betasq + sigma_sq_a + sigma_sq_b))
        end)
        |> Enum.sum()

      sum / denom
    end)
  end

It seems to be working correctly for me, but no promises ;)

jauggy commented 2 months ago

@Jonovono do you have a public github with openskill library fork? I might just look at yours because I assume you might have other features that are ahead.

jauggy commented 12 hours ago

Hey @jauggy, this is what I ended up doing:

  @doc """
  Predict the win probability for each team.

  ## Examples

      iex> teams = [
      ...>   [{25, 8.333}, {30, 6.666}],
      ...>   [{27, 7.0}, {28, 5.5}]
      ...> ]
      iex> Openskill.predict_win(teams)
      [0.7310585786300049, 0.2689414213699951]

  """
  @spec predict_win([[{float(), float()}]]) :: [float()]
  def predict_win(teams) do
    # team_ratings = teams |> Enum.map(&Openskill.Util.team_rating/1) |> List.flatten()
    team_ratings = Openskill.Util.team_rating(teams)
    n = length(teams)
    denom = (n * (n - 1)) / 2
    betasq = @env.beta * @env.beta

    team_ratings
    |> Enum.with_index()
    |> Enum.map(fn {{mu_a, sigma_sq_a, _team, _i}, i} ->
      sum =
        team_ratings
        |> Enum.with_index()
        |> Enum.filter(fn {_, q} -> i != q end)
        |> Enum.map(fn {{mu_b, sigma_sq_b, _team, _i}, _} ->
          Statistics.phi_major((mu_a - mu_b) / :math.sqrt(n * betasq + sigma_sq_a + sigma_sq_b))
        end)
        |> Enum.sum()

      sum / denom
    end)
  end

It seems to be working correctly for me, but no promises ;)

Statistics doesn't seem to have phi_major function. How did you get that? I am using statistics 0.6.3