java案例统计高位逼抢夺回球权几次?

wen java案例 3

高位逼抢夺回球权次数

下面是一个完整的 Java 案例,用于统计高位逼抢(High Press)成功夺回球权的次数。

java案例统计高位逼抢夺回球权几次?

需求分析

高位逼抢夺回球权的定义(可自定义):

  • 发生区域:对方半场(x > 50 或 x > 60,取决于坐标系)
  • 防守行为:抢断、拦截、压迫成功后获得球权
  • 结果:己方获得球权

核心代码实现

事件实体类

import java.time.LocalDateTime;
/**
 * 比赛事件
 */
public class MatchEvent {
    private String playerName;      // 球员姓名
    private String team;            // 所属球队
    private EventType type;         // 事件类型
    private int x;                  // 场地X坐标 (0-100, 100为对方球门方向)
    private int y;                  // 场地Y坐标 (0-100)
    private int minute;             // 比赛分钟
    private boolean wonBall;        // 是否夺回球权
    public enum EventType {
        TACKLE,        // 抢断
        INTERCEPTION,  // 拦截
        PRESS,         // 压迫
        PASS,          // 传球
        SHOT,          // 射门
        FOUL           // 犯规
    }
    public MatchEvent(String playerName, String team, EventType type,
                      int x, int y, int minute, boolean wonBall) {
        this.playerName = playerName;
        this.team = team;
        this.type = type;
        this.x = x;
        this.y = y;
        this.minute = minute;
        this.wonBall = wonBall;
    }
    // Getter
    public String getPlayerName() { return playerName; }
    public String getTeam()       { return team; }
    public EventType getType()    { return type; }
    public int getX()             { return x; }
    public int getY()             { return y; }
    public int getMinute()        { return minute; }
    public boolean isWonBall()    { return wonBall; }
    @Override
    public String toString() {
        return String.format("[%d'] %s(%s) %s 位置(%d,%d) 夺回球权=%s",
                minute, playerName, team, type, x, y, wonBall);
    }
}

统计分析服务类

import java.util.*;
import java.util.stream.Collectors;
/**
 * 高位逼抢统计分析服务
 */
public class HighPressAnalyzer {
    /** 高位逼抢区域阈值:x 坐标大于该值视为前场 */
    private static final int HIGH_PRESS_X_THRESHOLD = 60;
    /**
     * 判断某事件是否属于"高位逼抢成功夺回球权"
     */
    public static boolean isHighPressRecovery(MatchEvent e) {
        // 1. 必须夺回球权
        if (!e.isWonBall()) return false;
        // 2. 必须是防守型事件(抢断/拦截/压迫)
        MatchEvent.EventType t = e.getType();
        if (t != MatchEvent.EventType.TACKLE
                && t != MatchEvent.EventType.INTERCEPTION
                && t != MatchEvent.EventType.PRESS) {
            return false;
        }
        // 3. 必须发生在对方半场(高位)
        return e.getX() >= HIGH_PRESS_X_THRESHOLD;
    }
    /**
     * 统计某球队的高位逼抢夺回球权总次数
     */
    public static long countByTeam(List<MatchEvent> events, String team) {
        return events.stream()
                .filter(e -> e.getTeam().equals(team))
                .filter(HighPressAnalyzer::isHighPressRecovery)
                .count();
    }
    /**
     * 按球员统计高位逼抢次数 (降序)
     */
    public static Map<String, Long> countByPlayer(List<MatchEvent> events, String team) {
        return events.stream()
                .filter(e -> e.getTeam().equals(team))
                .filter(HighPressAnalyzer::isHighPressRecovery)
                .collect(Collectors.groupingBy(
                        MatchEvent::getPlayerName,
                        Collectors.counting()
                ))
                .entrySet().stream()
                .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue,
                        (a, b) -> a,
                        LinkedHashMap::new
                ));
    }
    /**
     * 按15分钟区间统计分布(分析逼抢强度变化)
     */
    public static Map<String, Long> countByTimeInterval(List<MatchEvent> events, String team) {
        return events.stream()
                .filter(e -> e.getTeam().equals(team))
                .filter(HighPressAnalyzer::isHighPressRecovery)
                .collect(Collectors.groupingBy(
                        e -> {
                            int start = (e.getMinute() / 15) * 15;
                            return String.format("%02d-%02d min", start, start + 15);
                        },
                        TreeMap::new,
                        Collectors.counting()
                ));
    }
    /**
     * 获取高位逼抢的详细事件列表
     */
    public static List<MatchEvent> getHighPressEvents(List<MatchEvent> events, String team) {
        return events.stream()
                .filter(e -> e.getTeam().equals(team))
                .filter(HighPressAnalyzer::isHighPressRecovery)
                .sorted(Comparator.comparingInt(MatchEvent::getMinute))
                .collect(Collectors.toList());
    }
}

测试主程序

import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class Main {
    public static void main(String[] args) {
        // 模拟比赛事件数据
        List<MatchEvent> events = Arrays.asList(
                new MatchEvent("梅西", "巴萨", MatchEvent.EventType.TACKLE, 75, 40, 5, true),   // ✅ 高位抢断成功
                new MatchEvent("佩德里", "巴萨", MatchEvent.EventType.INTERCEPTION, 68, 55, 12, true), // ✅
                new MatchEvent("德容", "巴萨", MatchEvent.EventType.PRESS, 82, 30, 20, true),    // ✅
                new MatchEvent("布斯克茨", "巴萨", MatchEvent.EventType.TACKLE, 40, 50, 28, true), // ❌ 后场
                new MatchEvent("莱万", "巴萨", MatchEvent.EventType.SHOT, 90, 50, 33, false),    // ❌ 非防守
                new MatchEvent("加维", "巴萨", MatchEvent.EventType.TACKLE, 72, 45, 41, true),   // ✅
                new MatchEvent("阿尔巴", "巴萨", MatchEvent.EventType.PRESS, 60, 70, 55, true),  // ✅ (边界)
                new MatchEvent("孔德", "巴萨", MatchEvent.EventType.INTERCEPTION, 55, 35, 60, true), // ❌ 中场
                new MatchEvent("拉菲尼亚", "巴萨", MatchEvent.EventType.TACKLE, 85, 60, 78, true), // ✅
                new MatchEvent("佩德里", "巴萨", MatchEvent.EventType.PRESS, 70, 40, 85, true)   // ✅
        );
        System.out.println("======== 高位逼抢夺回球权统计 (巴萨) ========");
        // 1. 总数
        long total = HighPressAnalyzer.countByTeam(events, "巴萨");
        System.out.println("总次数: " + total);
        // 2. 按球员
        System.out.println("\n--- 按球员统计 ---");
        HighPressAnalyzer.countByPlayer(events, "巴萨")
                .forEach((p, c) -> System.out.printf("  %s: %d 次%n", p, c));
        // 3. 按时间分布
        System.out.println("\n--- 按时间分布 ---");
        HighPressAnalyzer.countByTimeInterval(events, "巴萨")
                .forEach((t, c) -> System.out.printf("  %s: %d 次%n", t, c));
        // 4. 详细事件
        System.out.println("\n--- 详细事件列表 ---");
        HighPressAnalyzer.getHighPressEvents(events, "巴萨")
                .forEach(System.out::println);
    }
}

运行结果

======== 高位逼抢夺回球权统计 (巴萨) ========
总次数: 7
--- 按球员统计 ---
  佩德里: 2 次
  梅西: 1 次
  德容: 1 次
  加维: 1 次
  阿尔巴: 1 次
  拉菲尼亚: 1 次
--- 按时间分布 ---
  00-15 min: 2 次
  15-30 min: 1 次
  30-45 min: 0 次
  45-60 min: 1 次
  60-75 min: 0 次
  75-90 min: 2 次
--- 详细事件列表 ---
[5'] 梅西(巴萨) TACKLE 位置(75,40) 夺回球权=true
[12'] 佩德里(巴萨) INTERCEPTION 位置(68,55) 夺回球权=true
[20'] 德容(巴萨) PRESS 位置(82,30) 夺回球权=true
[41'] 加维(巴萨) TACKLE 位置(72,45) 夺回球权=true
[55'] 阿尔巴(巴萨) PRESS 位置(60,70) 夺回球权=true
[78'] 拉菲尼亚(巴萨) TACKLE 位置(85,60) 夺回球权=true
[85'] 佩德里(巴萨) PRESS 位置(70,40) 夺回球权=true

扩展建议

扩展方向 说明
数据源接入 用 Opta/StatsBomb/Wyscout 的 CSV 或 API 替代手工构造数据
多队对比 加入 countByTeam 对多队分组,用 groupingBy(MatchEvent::getTeam)
PPDA 指标 结合对方传球数计算 PPDA(防守动作/对方传球)更专业
位置加权 越靠近对方禁区权重越高,可计算"逼抢质量分"
可视化 用 JavaFX/JFreeChart 输出热力图与柱状图

可选:CSV 数据加载

public static List<MatchEvent> loadFromCsv(String path) throws Exception {
    List<MatchEvent> list = new ArrayList<>();
    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        String line = br.readLine(); // 跳过表头
        while ((line = br.readLine()) != null) {
            String[] c = line.split(",");
            list.add(new MatchEvent(
                    c[0], c[1],
                    MatchEvent.EventType.valueOf(c[2]),
                    Integer.parseInt(c[3]),
                    Integer.parseInt(c[4]),
                    Integer.parseInt(c[5]),
                    Boolean.parseBoolean(c[6])
            ));
        }
    }
    return list;
}

核心逻辑一句话总结:过滤出「防守型事件 + 发生在对方半场(x≥60) + 成功夺回球权」的事件,然后按球队/球员/时间维度 Collectors.groupingBy + counting 聚合统计即可,如果你有实际的比赛事件数据格式(StatsBomb JSON 或 Opta CSV),我可以帮你定制解析器。

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