java案例统计斜长传精准度如何?

wen java案例 1

Java 案例:统计斜长传精准度

下面我用一个完整的 Java 案例来演示如何统计足球比赛中斜长传(Diagonal Long Pass)的精准度,核心思路是:定义传球事件、判定斜长传、判定是否精准、汇总统计。

java案例统计斜长传精准度如何?


数据模型

import java.util.*;
import java.util.stream.*;
/**
 * 传球事件
 */
class PassEvent {
    private String playerName;      // 传球球员
    private double startX, startY;  // 起点坐标(球场 105x68,原点左下角)
    private double endX, endY;      // 终点坐标
    private boolean reachedTeammate; // 是否被队友接到(未被断/未出界)
    private String receiverTeam;    // 接球方队伍(用于判定是否队友)
    public PassEvent(String playerName, double startX, double startY,
                     double endX, double endY,
                     boolean reachedTeammate, String receiverTeam) {
        this.playerName = playerName;
        this.startX = startX;
        this.startY = startY;
        this.endX = endX;
        this.endY = endY;
        this.reachedTeammate = reachedTeammate;
        this.receiverTeam = receiverTeam;
    }
    // getter
    public String getPlayerName() { return playerName; }
    public double getStartX() { return startX; }
    public double getStartY() { return startY; }
    public double getEndX() { return endX; }
    public double getEndY() { return endY; }
    public boolean isReachedTeammate() { return reachedTeammate; }
    public String getReceiverTeam() { return receiverTeam; }
    /** 传球距离(米) */
    public double getDistance() {
        return Math.hypot(endX - startX, endY - startY);
    }
    /** 传球方向与场地横向(x轴)的夹角(度) */
    public double getAngleToXAxis() {
        return Math.toDegrees(Math.atan2(endY - startY, endX - startX));
    }
}

斜长传判定 + 精准度统计

class PassAnalyzer {
    /** 长传阈值:>=25 米 */
    private static final double LONG_PASS_MIN_DISTANCE = 25.0;
    /** 斜传判定:与 x 轴夹角在 [30°, 150°] 之间(即明显带纵向分量) */
    private static final double MIN_ANGLE = 30.0;
    private static final double MAX_ANGLE = 150.0;
    /**
     * 判断是否为斜长传
     */
    public static boolean isDiagonalLongPass(PassEvent p) {
        double dist = p.getDistance();
        double angle = Math.abs(p.getAngleToXAxis());
        // 归一到 [0,180]
        if (angle > 180) angle = 360 - angle;
        boolean isLong = dist >= LONG_PASS_MIN_DISTANCE;
        boolean isDiagonal = angle >= MIN_ANGLE && angle <= MAX_ANGLE;
        return isLong && isDiagonal;
    }
    /**
     * 统计某球员斜长传精准度
     * 精准定义:球被本方队友接到
     */
    public static void printPlayerStats(List<PassEvent> allPasses, String teamName) {
        Map<String, List<PassEvent>> byPlayer = allPasses.stream()
            .filter(PassAnalyzer::isDiagonalLongPass)
            .collect(Collectors.groupingBy(PassEvent::getPlayerName));
        System.out.printf("%-10s %6s %6s %8s%n",
                "球员", "总数", "成功", "精准度");
        System.out.println("-------------------------------------");
        byPlayer.forEach((name, list) -> {
            long total = list.size();
            long success = list.stream()
                    .filter(p -> p.isReachedTeammate()
                              && teamName.equals(p.getReceiverTeam()))
                    .count();
            double accuracy = total == 0 ? 0 : success * 100.0 / total;
            System.out.printf("%-10s %6d %6d %7.2f%%%n",
                    name, total, success, accuracy);
        });
    }
    /**
     * 统计全队总体斜长传精准度
     */
    public static double teamAccuracy(List<PassEvent> allPasses, String teamName) {
        List<PassEvent> diag = allPasses.stream()
                .filter(PassAnalyzer::isDiagonalLongPass)
                .collect(Collectors.toList());
        long total = diag.size();
        long success = diag.stream()
                .filter(p -> p.isReachedTeammate() && teamName.equals(p.getReceiverTeam()))
                .count();
        return total == 0 ? 0 : success * 100.0 / total;
    }
}

测试用例

public class DiagonalPassDemo {
    public static void main(String[] args) {
        List<PassEvent> passes = new ArrayList<>();
        // 球员A:3次斜长传,2次成功
        passes.add(new PassEvent("A", 30, 20, 60, 50, true,  "Home")); // 斜长传 ✔
        passes.add(new PassEvent("A", 20, 10, 55, 45, true,  "Home")); // 斜长传 ✔
        passes.add(new PassEvent("A", 40, 30, 70, 10, false, "Away")); // 斜长传,失败
        // 球员B:2次斜长传,1次成功
        passes.add(new PassEvent("B", 50, 55, 80, 25, true,  "Home")); // 斜长传 ✔
        passes.add(new PassEvent("B", 10, 60, 40, 30, false, "Away")); // 斜长传 ✘
        // 干扰项:直长传(角度接近0),不算斜长传
        passes.add(new PassEvent("C", 20, 34, 60, 34, true, "Home"));
        // 干扰项:短斜传(距离<25),不算
        passes.add(new PassEvent("C", 30, 30, 40, 40, true, "Home"));
        PassAnalyzer.printPlayerStats(passes, "Home");
        System.out.println("-------------------------------------");
        System.out.printf("全队斜长传精准度:%.2f%%%n",
                PassAnalyzer.teamAccuracy(passes, "Home"));
    }
}

输出结果

球员             总数   成功    精准度
-------------------------------------
A                 3      2   66.67%
B                 2      1   50.00%
-------------------------------------
全队斜长传精准度:60.00%

关键设计点说明

环节 说明
斜长传定义 距离 ≥ 25m 且 与 x 轴夹角在 30°~150°(保留横向推进又带纵向变化)
精准判定 优先看 reachedTeammate;进一步验证 receiverTeam == 本方,避免把“对方解围后本方捡到”算成功
角度归一化 atan2 会返回负角度,需要 Math.abs 或 360-angle 处理,避免方向判断遗漏
球场地坐标 采用 105×68 米真实球场尺度,便于把像素坐标换算
可扩展 isDiagonalLongPass 中的阈值可配置;如需按“传向进攻三区”进一步筛选,只需再加禁区判定

进一步优化建议

  1. 按传球方向细分:区分“左路→右路”和“右路→左路”,统计习惯脚差异。
  2. 加入压力因子:距离传球起点最近的对方球员 < 3m 时标记为“受压迫传球”,单独统计精准度。
  3. 落点热区:用 (endX, endY) 做 2D 直方图,分析斜长传常见落点是否集中(常用于分析“斜长传找边锋/中锋”战术)。
  4. 接入真实数据:可用 StatsBomb 或 Wyscout 的 JSON 事件流,替换 PassEvent 构造即可复用整套逻辑。

如果需要加入落点热区绘制或接入 StatsBomb 数据解析的代码,我可以继续补充。

友情链接

欧易欧易欧易欧易欧易欧易欧易欧易直播帮篮球控NBA帮比赛虎聊足球

友情链接

欧易欧易欧易欧易欧易欧易欧易欧易

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

友情链接攻略窝 · 礼包窝 · 配置窝 · 换算窝 · 算法窝 · 查文窝 · 手册窝 · 问答窝 · 全文搜 · 手册库
相关站点礼包窝 · 查文窝