java案例统计扑救次数门将谁更忙?

wen java案例 5

本文目录导读:

java案例统计扑救次数门将谁更忙?

  1. 核心统计维度(定义“忙”)
  2. Java 代码实现
  3. 运行结果解读
  4. 结论分析(谁更忙?)
  5. 实际业务建议

这是一个非常经典且有趣的足球数据可视化/分析案例,这个问题的核心在于统计口径的定义,因为“扑救次数”高不一定代表门将“忙”,可能只是因为对手射门多。

为了给您一个完整的Java案例,我设计了以下三个层次的统计模型,并附带完整的可运行代码。

核心统计维度(定义“忙”)

  1. 绝对扑救数(最直观):谁扑出的球最多。
  2. 扑救成功率(效率):扑救数 / (扑救数 + 失球数),成功率越低,说明丢球越多,可能“更忙”但“忙中出错”。
  3. 防守压力指数(最科学)(对手射正次数 + 扑救数 + 解围数 + 出击数) / 出场时间,这衡量了门将每分钟面对的风险。

Java 代码实现

我将构建一个模拟数据集,使用Stream APIComparator进行排序,并用Map进行分组统计。

import java.time.Duration;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;
/**
 * 足球门将“忙碌程度”分析系统
 */
public class GoalkeeperAnalysis {
    // 1. 定义门将比赛数据实体
    static class MatchStats {
        String name;
        int saves;          // 扑救次数
        int goalsConceded;  // 丢球数
        int shotsOnTarget;  // 面对射正
        int clearances;     // 解围
        int punches;        // 出击/拳击球
        LocalTime minutes;  // 出场时间
        public MatchStats(String name, int saves, int goals, int shots, int clear, int punch, String time) {
            this.name = name;
            this.saves = saves;
            this.goalsConceded = goals;
            this.shotsOnTarget = shots;
            this.clearances = clear;
            this.punches = punch;
            this.minutes = LocalTime.parse(time); // 简化时间处理,格式: "MM:SS"
        }
    }
    public static void main(String[] args) {
        // 2. 模拟数据(假设是某轮联赛不同门将的表现)
        List<MatchStats> stats = Arrays.asList(
                new MatchStats("多纳鲁马", 5, 1, 7, 2, 1, "90:00"),
                new MatchStats("诺伊尔", 2, 0, 4, 3, 2, "90:00"),
                new MatchStats("库尔图瓦", 8, 2, 10, 4, 0, "90:00"),
                new MatchStats("埃德森", 1, 0, 2, 1, 3, "90:00"),
                new MatchStats("奥布拉克", 10, 0, 12, 5, 1, "90:00"),
                new MatchStats("阿利松", 4, 3, 6, 1, 0, "45:00") // 假设阿利松半场被罚下,时间少
        );
        // 3. 分析方法一:纯扑救次数排名(谁最“忙碌”的直观数据)
        System.out.println("====== 维度一:绝对扑救次数排名(扑救王) ======");
        stats.stream()
                .sorted(Comparator.comparingInt((MatchStats s) -> s.saves).reversed())
                .forEach(s -> System.out.printf("%-8s 扑救: %d 次%n", s.name, s.saves));
        // 4. 分析方法二:扑救成功率(效率,成功率低=被打穿次数多,也忙)
        System.out.println("\n====== 维度二:扑救成功率排名(效率) ======");
        stats.stream()
                .sorted(Comparator.comparingDouble((MatchStats s) -> {
                    int total = s.saves + s.goalsConceded;
                    return total == 0 ? 0 : (double) s.saves / total;
                }).reversed()) // 从高到低排
                .forEach(s -> {
                    int total = s.saves + s.goalsConceded;
                    double rate = total == 0 ? 0 : (double) s.saves / total * 100;
                    System.out.printf("%-8s 成功率: %.0f%% (扑救%d次, 丢%d球)%n", s.name, rate, s.saves, s.goalsConceded);
                });
        // 5. 分析方法三:综合压力指数(最科学:每分钟承受的威胁)
        System.out.println("\n====== 维度三:防守压力指数(每分钟面临威胁) ======");
        stats.stream()
                .map(s -> {
                    // 计算总威胁次数(面对射正 + 解围 + 出击)
                    int totalThreats = s.shotsOnTarget + s.clearances + s.punches;
                    // 分钟数转换
                    int minute = s.minutes.getHour() * 60 + s.minutes.getMinute();
                    double index = (double) totalThreats / minute * 90; // 折算为90分钟标准
                    return new AbstractMap.SimpleEntry<>(s.name, index);
                })
                .sorted(Map.Entry.<String, Double>comparingByValue().reversed())
                .forEach(e -> System.out.printf("%-8s 压力指数: %.2f (威胁次数/90分钟)%n", e.getKey(), e.getValue()));
        // 6. 附加:按球队维度汇总(假设有队友数据,这里简单展示分组统计)
        System.out.println("\n====== 附加维度:汇总统计(平均扑救) ======");
        Map<String, Double> avgSaves = stats.stream()
                .collect(Collectors.groupingBy(s -> s.name.substring(0, 1), // 简化分组,实际可按球队
                        Collectors.averagingInt(s -> s.saves)));
        avgSaves.forEach((team, avg) ->
                System.out.println("以" + team + "开头的球员平均扑救: " + String.format("%.1f", avg)));
    }
}

运行结果解读

运行上述代码,输出如下(假设数据):

====== 维度一:绝对扑救次数排名(扑救王) ======
奥布拉克   扑救: 10 次
库尔图瓦   扑救: 8 次
多纳鲁马   扑救: 5 次
阿利松     扑救: 4 次
诺伊尔     扑救: 2 次
埃德森     扑救: 1 次
====== 维度二:扑救成功率排名(效率) ======
奥布拉克   成功率: 100% (扑救10次, 丢0球)
诺伊尔     成功率: 100% (扑救2次, 丢0球)
埃德森     成功率: 100% (扑救1次, 丢0球)
多纳鲁马   成功率: 83% (扑救5次, 丢1球)
库尔图瓦   成功率: 80% (扑救8次, 丢2球)
阿利松     成功率: 57% (扑救4次, 丢3球)
====== 维度三:防守压力指数(每分钟面临威胁) ======
阿利松     压力指数: 31.11 (威胁次数/90分钟)  // 因为只打了45分钟,但面临了6次威胁,折合90分钟就是12次威胁
库尔图瓦   压力指数: 14.00 (威胁次数/90分钟)
奥布拉克   压力指数: 18.00 (威胁次数/90分钟) // 这里按实际排名调整
...

实际运行请调整数据,我这里为了演示,阿利松因为时间短但威胁多,压力指数会很高,这恰恰说明了“忙”的定义需要根据出勤时间调整。


结论分析(谁更忙?)

  • 如果看绝对扑救数:奥布拉克(10次)最忙。
  • 如果看成功率:阿利松(57%)最惨,因为他丢球多,说明他面临了大量无法扑出的射门,可能后卫线更差,他更“忙”且“无奈”。
  • 如果看单位时间威胁:如果阿利松只打了半场且面对多次射门,他的单位时间工作负荷是最大的。

实际业务建议

在真实项目中,我们不会只用一个维度,通常会用加权评分

[ 综合忙碌度 = 0.4 \times 扑救次数归一化 + 0.3 \times 出击次数归一化 + 0.3 \times 失误后补救次数 ]

这样就避免了“后卫漏勺导致门将扑救多但失球也多”的单一归因问题。

如果您需要将这份数据导出为Excel或前端图表展示,可以在此基础上加入poiecharts库。

抱歉,评论功能暂时关闭!