评级百分比指数 - 在下面的 C# 中实现:
// <copyright file="RPICalculator.cs" company="Steve Stokes Consulting, LLC">
// Copyright © Steve Stokes Consulting, LLC. All Rights Reserved.
// </copyright>
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RPI.Calculator
{
public class RPICalculator
{
public void Test()
{
//Home Score Away Score
//UConn 64 Kansas 57
//UConn 82 Duke 68
//Minnesota 71 UConn 72
//Kansas 69 UConn 62
//Duke 81 Minnesota 70
//Minnesota 52 Kansas 62
//resulting in the following ratings:
//UConn: 0.6660
//Kansas: 0.6378
//Duke: 0.3840
//Minnesota: 0.3403
List<Team> Teams = new List<Team>();
List<Match> Matches = new List<Match>();
Teams.Add(new Team()
{
Name = "UConn",
});
Teams.Add(new Team()
{
Name = "Kansas",
});
Teams.Add(new Team()
{
Name = "Duke",
});
Teams.Add(new Team()
{
Name = "Minnesota",
});
Matches.Add(new Match()
{
HomeTeam = Teams.Where(t => t.Name == "UConn").First(),
AwayTeam = Teams.Where(t => t.Name == "Kansas").First(),
Winner = Teams.Where(t => t.Name == "UConn").First(),
});
Matches.Add(new Match()
{
HomeTeam = Teams.Where(t => t.Name == "UConn").First(),
AwayTeam = Teams.Where(t => t.Name == "Duke").First(),
Winner = Teams.Where(t => t.Name == "UConn").First(),
});
Matches.Add(new Match()
{
HomeTeam = Teams.Where(t => t.Name == "Minnesota").First(),
AwayTeam = Teams.Where(t => t.Name == "UConn").First(),
Winner = Teams.Where(t => t.Name == "UConn").First(),
});
Matches.Add(new Match()
{
HomeTeam = Teams.Where(t => t.Name == "Kansas").First(),
AwayTeam = Teams.Where(t => t.Name == "UConn").First(),
Winner = Teams.Where(t => t.Name == "Kansas").First(),
});
Matches.Add(new Match()
{
HomeTeam = Teams.Where(t => t.Name == "Duke").First(),
AwayTeam = Teams.Where(t => t.Name == "Minnesota").First(),
Winner = Teams.Where(t => t.Name == "Duke").First(),
});
Matches.Add(new Match()
{
HomeTeam = Teams.Where(t => t.Name == "Minnesota").First(),
AwayTeam = Teams.Where(t => t.Name == "Kansas").First(),
Winner = Teams.Where(t => t.Name == "Kansas").First(),
});
var results = Calculate(Teams, Matches, Sport.NCAA_Basketball);
foreach (var team in results)
{
Debug.WriteLine(string.Format("{0} - {1}", team.Name.PadRight(Teams.Select(t => t.Name).Max(s => s.Length)), team.RPI));
}
}
private decimal defaultHomeWinValue = 1.0m;
private decimal defaultAwayWinValue = 1.0m;
private decimal homeWinValue = 1.0m;
private decimal awayWinValue = 1.0m;
public IEnumerable<TTeam> Calculate<TTeam, TMatch>(IEnumerable<TTeam> Teams, IEnumerable<TMatch> Matches, Sport Sport)
{
// TODO: transform a user team to our internal team type
}
/// <summary>
/// Calculate the RPI of each team
/// </summary>
/// <param name="Teams">The list of teams to calculate RPI for</param>
/// <param name="Matches">The list of matches and results of the matches the teams played in a period</param>
/// <param name="Sport">The sport the teams played - this modifies home/away weighting based on NCAA rules</param>
/// <returns>The list of teams with calculated RPI's</returns>
public IEnumerable<Team> Calculate(IEnumerable<Team> Teams, IEnumerable<Match> Matches, Sport Sport)
{
SetWeightingBasedOnSport(Sport);
foreach (var team in Teams)
{
// calculate the WP of each team
team.WP = CalculateWinPercent(team, Matches, homeWinValue, awayWinValue);
// calculate the OWP of each team
team.OWP = CalculateOpponentsWinPercent(team, Matches);
// calculate the OOWP of each team
team.OOWP = CalculateOpponentsOpponentsWinPercent(team, Teams, Matches);
// calculate the RPI for each team
team.RPI = CalculateRPI(team);
}
return Teams.OrderByDescending(t => t.RPI);
}
private decimal CalculateRPI(Team team)
{
//RPI = (WP * 0.25) + (OWP * 0.50) + (OOWP * 0.25)
//UConn: 0.6660
//Kansas: 0.6378
//Duke: 0.3840
//Minnesota: 0.3403
var RPI = (team.WP * 0.25m) + (team.OWP * 0.50m) + (team.OOWP * 0.25m);
return Math.Round(RPI, 4);
}
private decimal CalculateOpponentsOpponentsWinPercent(Team teamInQuestion, IEnumerable<Team> Teams, IEnumerable<Match> Matches)
{
//UConn: ((Kansas 0.6667) + (Kansas 0.6667) + (Duke 0.3333) + (Minnesota 0.3889)) / (4 games) = 0.5139
//Kansas: ((UConn 0.7500) + (UConn 0.7500) + (Minnesota 0.3889)) / (3 games) = 0.6296
//Duke: ((UConn 0.7500) + (Minnesota 0.3889)) / (2 games) = 0.5694
//Minnesota: ((UConn 0.7500) + (Duke 0.3333) + (Kansas 0.6667)) / (3 games) = 0.5833
// get each team i've played this season (not unique)
var teamsIvePlayed = Matches.Where(m => m.AwayTeam == teamInQuestion || m.HomeTeam == teamInQuestion).Select(s => s.HomeTeam == teamInQuestion ? s.AwayTeam : s.HomeTeam);
// get the opponent win percent (OWP) of each team I played
var teamsIvePlayedOpponentWinPercent = teamsIvePlayed.Select(t => new { Team = t, OWP = CalculateOpponentsWinPercent(t, Matches) });
// calculate the OOWP
return (decimal)(teamsIvePlayedOpponentWinPercent.Sum(t => t.OWP) / teamsIvePlayed.Count());
}
private decimal CalculateOpponentsWinPercent(Team teamInQuestion, IEnumerable<Match> Matches)
{
// get each teams WP without the team in question
//Home Score Away Score
//UConn 64 Kansas 57
//UConn 82 Duke 68
//Minnesota 71 UConn 72
//Kansas 69 UConn 62
//Duke 81 Minnesota 70
//Minnesota 52 Kansas 62
//UConn: ((Kansas 1.0) + (Kansas 1.0) + (Duke 1.0) + (Minnesota 0)) / (4 games) = 0.7500
//Kansas: ((UConn 1.0) + (UConn 1.0) + (Minnesota 0.0)) / (3 games) = 0.6667
//Duke: ((UConn 0.6667) + (Minnesota 0.0)) / (2 games) = 0.3333
//Minnesota: ((UConn 0.6667) + (Duke 0.0) + (Kansas 0.5)) / (3 games) = 0.3889
// get each team i've played this season (not unique)
var teamsIvePlayed = Matches.Where(m => m.AwayTeam == teamInQuestion || m.HomeTeam == teamInQuestion).Select(s => s.HomeTeam == teamInQuestion ? s.AwayTeam : s.HomeTeam);
// get the win percent of each team I played excluding matches with me
var teamsIvePlayedWinPercent = teamsIvePlayed.Select(t => new
{
Team = t,
WP = CalculateWinPercent(t, Matches.Where(m => m.AwayTeam != teamInQuestion && m.HomeTeam != teamInQuestion), defaultHomeWinValue, defaultAwayWinValue)
});
// calculate the OWP
return (decimal)(teamsIvePlayedWinPercent.Sum(t => t.WP) / teamsIvePlayed.Count());
}
private decimal CalculateWinPercent(Team teamInQuestion, IEnumerable<Match> Matches, decimal HomeWinValue, decimal AwayWinValue)
{
// get the teams win percent - sometimes weighted based on NCAA rules
//UConn: (0.6 + 0.6 + 1.4 + 0) / (0.6 + 0.6 + 1.4 + 1.4) = 0.6500
//Kansas: (0 + 0.6 + 1.4) / (1.4 + 0.6 + 1.4) = 0.5882
//Duke: (0 + 0.6) / (1.4 + 0.6) = 0.3000
//Minnesota: (0 + 0 + 0) / (0.6 + 1.4 + 0.6) = 0.0000
// get my wins and sum with weighting
var wins = Matches.Where(m => m.Winner == teamInQuestion).Sum(s => s.HomeTeam == teamInQuestion ? HomeWinValue : AwayWinValue);
// get my games played count weighted
var gamesPlayed = Matches.Where(m => m.HomeTeam == teamInQuestion || m.AwayTeam == teamInQuestion).Sum(s => s.HomeTeam == teamInQuestion ? HomeWinValue : AwayWinValue);
// get the WP
return wins / gamesPlayed;
}
private void SetWeightingBasedOnSport(Sport Sport)
{
switch (Sport)
{
case Sport.NCAA_Basketball:
homeWinValue = 0.6m;
awayWinValue = 1.4m;
break;
case Sport.NCAA_Baseball:
homeWinValue = 0.7m;
awayWinValue = 1.3m;
break;
default:
homeWinValue = defaultHomeWinValue;
awayWinValue = defaultAwayWinValue;
break;
}
}
}
public enum Sport
{
NoHomeOrAwayWeighting = 1,
NCAA_Basketball = 2,
NCAA_Baseball = 3,
}
public class Team
{
public string Name { get; set; }
public decimal RPI { get; internal set; }
public decimal WP { get; internal set; }
public decimal OWP { get; internal set; }
public decimal OOWP { get; internal set; }
}
public class Match
{
public Team HomeTeam { get; set; }
public Team AwayTeam { get; set; }
public Team Winner { get; set; }
}
}