RPI
Mix.install([
{:kino, "~> 0.12.3"},
{:kino_explorer, "~> 0.1.19"}
])
Section
require Explorer.DataFrame, as: DF
require Explorer.Series, as: Series
df = DF.from_csv!("GameResults.csv")
defmodule RPI do
def calc_wl(game_data, team, type) do
Series.sum(Series.equal(game_data[type], team))
end
def filter_team(game_data, team) do
DF.filter(game_data, winner == ^team or loser == ^team)
end
def filter_team_opp(game_data, team, opp) do
DF.filter(filter_team(game_data, team), winner != ^opp and loser != ^opp)
end
def wp(winners, team) do
Series.sum(Series.equal(winners, team)) / Series.count(winners)
end
def calc_wp(game_data, team) do
games_played = filter_team(game_data, team)
wp(games_played[:winner], team)
end
def calc_pd(game_data, team) do
game_data
|> filter_team(team)
|> DF.mutate(
pd: if(winner == ^team,
do: winner_score - loser_score,
else: loser_score - winner_score)
)
|> DF.pull("pd")
|> Series.sum()
end
## retaining this version as a reminder of alternative approach
## can use Enum.zip if only have two elements to zip
# def calc_pd(game_data, team) do
# gp = filter_team(game_data)
# List.zip([
# Series.to_list(gp[:winner]),
# Series.to_list(gp[:winner_score]),
# Series.to_list(gp[:loser_score])
# ])
# |> Enum.map(fn {w, ws, ls} -> if(w == team, do: ws - ls, else: ls - ws) end)
# |> Series.from_list()
# |> Series.sum()
# end
def calc_owp(game_data, team) do
game_data
|> DF.filter(winner == ^team or loser == ^team)
|> DF.mutate(opp: if(winner == ^team, do: loser, else: winner))
|> DF.pull("opp")
# transform is computationally expensive b/c of type conversion
|> Series.transform(fn x -> calc_wp_owp(game_data, x, team) end)
|> Series.mean()
end
def calc_wp_owp(game_data, team, opp) do
games_played = filter_team_opp(game_data, team, opp)
wp(games_played[:winner], team)
end
def calc_oowp(game_data, team) do
game_data
|> DF.filter(winner == ^team or loser == ^team)
|> DF.mutate(opp: if(winner == ^team, do: loser, else: winner))
|> DF.pull("opp")
|> Series.transform(fn opp -> calc_owp(game_data, opp) end)
|> Series.mean()
end
def calc_sos(game_data, team) do
(2 * calc_owp(game_data, team) + calc_oowp(game_data, team)) / 3
end
def calc_rpi(game_data, team) do
0.25 * calc_wp(game_data, team) +
0.5 * calc_owp(game_data, team) +
0.25 * calc_oowp(game_data, team)
end
end
teams =
Series.concat(df[:winner], df[:loser])
|> Series.distinct()
|> Series.to_enum()
DF.new(
team: teams,
win: Enum.map(teams, fn x -> RPI.calc_wl(df, x, :winner) end),
loss: Enum.map(teams, fn x -> RPI.calc_wl(df, x, :loser) end),
wp: Enum.map(teams, fn x -> RPI.calc_wp(df, x) end),
pd: Enum.map(teams, fn x -> RPI.calc_pd(df, x) end),
sos: Enum.map(teams, fn x -> RPI.calc_sos(df, x) end),
rpi: Enum.map(teams, fn x -> RPI.calc_rpi(df, x) end)
)
|> DF.sort_by(desc: rpi)