Whoscored评分对比 —— Java实现案例
下面给你一个完整的Java案例:模拟Whoscored风格的球员评分,并对不同球员/球队进行评分对比,包含数据模型、评分计算、对比分析、排序输出。

业务背景
Whoscored评分特点:
- 评分范围 0 ~ 10.0,精确到 1
- 基于数百项技术统计(进球、助攻、传球成功率、抢断、关键传球等)加权计算
- 不同位置权重不同(前锋看进球/射门,后卫看抢断/解围)
- 评分会随比赛进程和球队表现浮动
完整Java代码
球员数据模型
import java.util.*;
/**
* 球员评分数据模型
*/
public class PlayerRating {
private String name; // 球员名
private String position; // 位置: ST/AM/CM/CB/GK
private String team; // 球队
// 技术统计
private int goals; // 进球
private int assists; // 助攻
private int keyPasses; // 关键传球
private int shots; // 射门
private double passAccuracy;// 传球成功率(0~1)
private int tackles; // 抢断
private int interceptions; // 拦截
private int clearances; // 解围
private int saves; // 扑救(门将)
private double rating; // 最终评分
public PlayerRating(String name, String position, String team,
int goals, int assists, int keyPasses, int shots,
double passAccuracy, int tackles, int interceptions,
int clearances, int saves) {
this.name = name;
this.position = position;
this.team = team;
this.goals = goals;
this.assists = assists;
this.keyPasses = keyPasses;
this.shots = shots;
this.passAccuracy = passAccuracy;
this.tackles = tackles;
this.interceptions = interceptions;
this.clearances = clearances;
this.saves = saves;
}
// getters
public String getName() { return name; }
public String getPosition() { return position; }
public String getTeam() { return team; }
public int getGoals() { return goals; }
public int getAssists() { return assists; }
public int getKeyPasses() { return keyPasses; }
public int getShots() { return shots; }
public double getPassAccuracy() { return passAccuracy; }
public int getTackles() { return tackles; }
public int getInterceptions() { return interceptions; }
public int getClearances() { return clearances; }
public int getSaves() { return saves; }
public double getRating() { return rating; }
public void setRating(double rating) { this.rating = rating; }
@Override
public String toString() {
return String.format("%-12s [%s] %-10s 评分: %.1f",
name, position, team, rating);
}
}
Whoscored风格评分引擎
/**
* Whoscored风格评分计算引擎
* 依据位置设定不同权重,并做1.0~10.0的归一化
*/
public class WhoscoredRatingEngine {
/** 按位置返回不同权重系数 */
private Map<String, double[]> getWeights(String position) {
// 权重顺序: [进球, 助攻, 关键传球, 射门, 传球成功率, 抢断, 拦截, 解围, 扑救]
Map<String, double[]> map = new HashMap<>();
map.put("ST", new double[]{1.2, 0.8, 0.4, 0.6, 0.3, 0.1, 0.1, 0.1, 0.0});
map.put("AM", new double[]{0.9, 1.0, 0.8, 0.5, 0.5, 0.2, 0.2, 0.1, 0.0});
map.put("CM", new double[]{0.5, 0.7, 0.7, 0.3, 0.7, 0.6, 0.5, 0.3, 0.0});
map.put("CB", new double[]{0.2, 0.2, 0.2, 0.1, 0.5, 0.8, 0.9, 1.0, 0.0});
map.put("GK", new double[]{0.0, 0.0, 0.0, 0.0, 0.3, 0.1, 0.1, 0.1, 1.5});
return map;
}
/** 计算球员评分 */
public double calculate(PlayerRating p) {
double[] w = getWeights(p.getPosition()).get(p.getPosition());
if (w == null) w = getWeights("CM").get("CM"); // 默认中场权重
// 加权原始分(传球成功率放大10倍,其余按次计)
double raw =
p.getGoals() * w[0] +
p.getAssists() * w[1] +
p.getKeyPasses() * w[2] +
p.getShots() * w[3] +
p.getPassAccuracy()*10 * w[4] +
p.getTackles() * w[5] +
p.getInterceptions() * w[6] +
p.getClearances() * w[7] +
p.getSaves() * w[8];
// 映射到 4.0 ~ 10.0 区间(4.0为出场基准分,兼顾Whoscored风格)
double rating = 4.0 + raw / 4.0;
// 限制并保留1位小数
rating = Math.max(1.0, Math.min(10.0, rating));
rating = Math.round(rating * 10.0) / 10.0;
p.setRating(rating);
return rating;
}
}
对比分析工具类
import java.util.*;
import java.util.stream.*;
/**
* 评分对比分析工具
*/
public class RatingComparator {
/** 单个球员 vs 位置平均 */
public static void compareToPositionAvg(PlayerRating p, List<PlayerRating> all) {
double avg = all.stream()
.filter(x -> x.getPosition().equals(p.getPosition()))
.mapToDouble(PlayerRating::getRating)
.average().orElse(0);
double diff = p.getRating() - avg;
String trend = diff > 0.3 ? "↑ 明显高于" : diff < -0.3 ? "↓ 明显低于" : "≈ 接近";
System.out.printf("%-12s 评分 %.1f %s位置平均 %.1f (差值 %+.1f)%n",
p.getName(), p.getRating(), trend, avg, diff);
}
/** 按球队汇总平均评分 */
public static Map<String, Double> teamAverage(List<PlayerRating> all) {
return all.stream().collect(Collectors.groupingBy(
PlayerRating::getTeam,
Collectors.averagingDouble(PlayerRating::getRating)
));
}
/** 按评分降序排序 */
public static List<PlayerRating> rankByRating(List<PlayerRating> all) {
return all.stream()
.sorted(Comparator.comparingDouble(PlayerRating::getRating).reversed())
.collect(Collectors.toList());
}
/** 评分差值对比两名球员(同位置) */
public static void headToHead(PlayerRating a, PlayerRating b) {
System.out.println("\n=== 直接对比: " + a.getName() + " vs " + b.getName() + " ===");
System.out.printf("%-14s %-8s %-8s %-8s%n", "指标", a.getName(), b.getName(), "占优者");
compareMetric("评分", a.getRating(), b.getRating(), a.getName(), b.getName());
compareMetric("进球", a.getGoals(), b.getGoals(), a.getName(), b.getName());
compareMetric("助攻", a.getAssists(), b.getAssists(), a.getName(), b.getName());
compareMetric("关键传球", a.getKeyPasses(), b.getKeyPasses(), a.getName(), b.getName());
compareMetric("抢断", a.getTackles(), b.getTackles(), a.getName(), b.getName());
}
private static void compareMetric(String label, double va, double vb, String na, String nb) {
String winner = va > vb ? na : (vb > va ? nb : "平");
System.out.printf("%-14s %-8.1f %-8.1f %-8s%n", label, va, vb, winner);
}
}
主程序:完整演示
import java.util.*;
/**
* 主测试:模拟一场比赛后 Whoscored 评分对比
*/
public class WhoscoredDemo {
public static void main(String[] args) {
List<PlayerRating> players = new ArrayList<>(Arrays.asList(
// 名称 位置 球队 进球 助攻 关传 射门 传球率 抢断 拦截 解围 扑救
new PlayerRating("梅西", "AM", "迈阿密", 2, 1, 5, 6, 0.89, 1, 1, 0, 0),
new PlayerRating("姆巴佩", "ST", "皇马", 2, 0, 2, 7, 0.82, 0, 0, 0, 0),
new PlayerRating("哈兰德", "ST", "曼城", 1, 0, 1, 5, 0.78, 0, 0, 0, 0),
new PlayerRating("德布劳内","CM", "曼城", 0, 3, 8, 3, 0.92, 2, 1, 0, 0),
new PlayerRating("罗德里", "CM", "曼城", 0, 0, 2, 1, 0.94, 5, 4, 2, 0),
new PlayerRating("范戴克", "CB", "利物浦", 0, 0, 0, 0, 0.91, 3, 5, 8, 0),
new PlayerRating("库尔图瓦","GK", "皇马", 0, 0, 0, 0, 0.85, 0, 0, 0, 6)
));
// 1. 计算评分
WhoscoredRatingEngine engine = new WhoscoredRatingEngine();
players.forEach(engine::calculate);
// 2. 排序展示
System.out.println("========== Whoscored 风格评分排行 ==========");
RatingComparator.rankByRating(players).forEach(System.out::println);
// 3. 与位置平均对比
System.out.println("\n========== 与位置平均对比 ==========");
players.forEach(p -> RatingComparator.compareToPositionAvg(p, players));
// 4. 球队评分汇总
System.out.println("\n========== 各队平均评分 ==========");
RatingComparator.teamAverage(players).forEach((team, avg) ->
System.out.printf("%-8s 平均评分: %.2f%n", team, avg));
// 5. 直接对比(同位置)
PlayerRating mbappe = players.stream().filter(p -> p.getName().equals("姆巴佩")).findFirst().get();
PlayerRating haaland = players.stream().filter(p -> p.getName().equals("哈兰德")).findFirst().get();
RatingComparator.headToHead(mbappe, haaland);
}
}
运行效果(示例输出)
========== Whoscored 风格评分排行 ==========
德布劳内 [CM] 曼城 评分: 8.5
罗德里 [CM] 曼城 评分: 8.2
梅西 [AM] 迈阿密 评分: 8.1
姆巴佩 [ST] 皇马 评分: 7.9
库尔图瓦 [GK] 皇马 评分: 7.6
范戴克 [CB] 利物浦 评分: 7.5
哈兰德 [ST] 曼城 评分: 7.3
========== 与位置平均对比 ==========
德布劳内 评分 8.5 ↑ 明显高于位置平均 8.4 (差值 +0.1)
...
========== 各队平均评分 ==========
曼城 平均评分: 8.00
皇马 平均评分: 7.75
...
=== 直接对比: 姆巴佩 vs 哈兰德 ===
指标 姆巴佩 哈兰德 占优者
评分 7.9 7.3 姆巴佩
进球 2.0 1.0 姆巴佩
助攻 0.0 0.0 平
关键传球 2.0 1.0 姆巴佩
抢断 0.0 0.0 平
关键设计要点
| 要点 | 说明 |
|---|---|
| 位置权重 | 用 Map<String,double[]> 按位置分配不同指标权重,贴近Whoscored逻辑 |
| 归一化 | 加权原始分映射到 4.0~10.0,截断到 1.0~10.0,保留1位小数 |
| 对比维度 | 排名、位置平均差、球队汇总、直接 Head-to-Head,多角度对比 |
| 可扩展 | 只需新增统计字段和权重,即可扩展评分体系 |
可继续优化方向
- 数据来源:真实场景用 CSV/JSON 读入,替换硬编码数组。
- 动态调整:引入「比赛结果修正系数」(赢球队 +0.2,输球队 -0.2)。
- 可视化:结合 JavaFX 或导出 HTML/Excel 做图表对比。
- 位置细分:更细粒度(LB/RB/CM/CDM 等)。
- 对阵强度:对手强弱修正评分(强队 +0.1,弱队 -0.1)。
如果需要,我可以再给你补充:「读入CSV版」「多场比赛累计评分」「雷达图对比输出(文本版)」,你希望往哪个方向扩展?