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

需求分析
高位逼抢夺回球权的定义(可自定义):
- 发生区域:对方半场(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),我可以帮你定制解析器。