PHP 项目统计斜长传精准度的实现方案
在足球数据统计系统中,斜长传精准度是一个常见的战术指标,下面给你一套完整的 PHP 实现思路和代码。

先明确数据模型
统计斜长传精准度,核心是传球事件的字段设计:
CREATE TABLE pass_events (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
match_id INT NOT NULL,
player_id INT NOT NULL,
team_id INT NOT NULL,
-- 传球类型: short/long/cross/diagonal_long(斜长传)
pass_type VARCHAR(32) NOT NULL,
start_x DECIMAL(5,2), -- 起点坐标(0-100 归一化)
start_y DECIMAL(5,2),
end_x DECIMAL(5,2), -- 终点坐标
end_y DECIMAL(5,2),
distance DECIMAL(6,2), -- 传球距离(米)
angle DECIMAL(6,2), -- 传球方向角(度)
is_success TINYINT(1) DEFAULT 0, -- 是否成功(队友接到)
target_player_id INT, -- 目标球员
created_at DATETIME
);
斜长传的判定逻辑
斜长传 = 距离足够长 + 方向为斜向,PHP 里这样判断:
class PassClassifier
{
const MIN_LONG_DISTANCE = 30; // 长传最小距离(米)
const DIAGONAL_MIN_ANGLE = 15; // 与球场纵轴/横轴夹角阈值
const DIAGONAL_MAX_ANGLE = 75;
/**
* 判断是否为斜长传
*/
public static function isDiagonalLongPass(array $pass): bool
{
if ($pass['distance'] < self::MIN_LONG_DISTANCE) {
return false;
}
// 计算传球方向与球场纵轴的夹角
$dx = $pass['end_x'] - $pass['start_x'];
$dy = $pass['end_y'] - $pass['start_y'];
if (abs($dx) < 0.001) {
return false; // 纯纵向不算斜长传
}
$angle = abs(rad2deg(atan($dy / $dx))); // 0~90
return $angle >= self::DIAGONAL_MIN_ANGLE
&& $angle <= self::DIAGONAL_MAX_ANGLE;
}
}
精准度统计核心逻辑
class DiagonalPassStats
{
private PDO $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
/**
* 统计某球员/球队的斜长传精准度
*
* @param int|null $playerId 传 null 则统计全队
* @param int|null $matchId
*/
public function getAccuracy(?int $playerId = null, ?int $matchId = null): array
{
$sql = "SELECT
COUNT(*) AS total,
SUM(is_success) AS success,
AVG(distance) AS avg_distance
FROM pass_events
WHERE pass_type = 'diagonal_long'";
$params = [];
if ($playerId) {
$sql .= " AND player_id = :pid";
$params[':pid'] = $playerId;
}
if ($matchId) {
$sql .= " AND match_id = :mid";
$params[':mid'] = $matchId;
}
$stmt = $this->pdo->prepare($sql);
$stmt->execute($params);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$total = (int)($row['total'] ?? 0);
$success = (int)($row['success'] ?? 0);
return [
'total' => $total,
'success' => $success,
'accuracy' => $total > 0 ? round($success / $total * 100, 2) : 0.0,
'avg_distance' => round((float)($row['avg_distance'] ?? 0), 2),
];
}
/**
* 按球员分组,输出排行榜
*/
public function rankByPlayer(int $matchId, int $minAttempts = 3): array
{
$sql = "SELECT
player_id,
COUNT(*) AS total,
SUM(is_success) AS success,
ROUND(SUM(is_success) / COUNT(*) * 100, 2) AS accuracy
FROM pass_events
WHERE pass_type = 'diagonal_long'
AND match_id = :mid
GROUP BY player_id
HAVING total >= :min
ORDER BY accuracy DESC, total DESC";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([':mid' => $matchId, ':min' => $minAttempts]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
数据入库时自动分类
不要依赖人工录入 pass_type,在写入时自动判定:
function savePass(PDO $pdo, array $pass): void
{
// 统一计算距离与角度
$dx = $pass['end_x'] - $pass['start_x'];
$dy = $pass['end_y'] - $pass['start_y'];
$pass['distance'] = round(sqrt($dx * $dx + $dy * $dy), 2);
if (empty($pass['pass_type'])) {
$pass['pass_type'] = PassClassifier::isDiagonalLongPass($pass)
? 'diagonal_long'
: 'other';
}
$stmt = $pdo->prepare(
"INSERT INTO pass_events
(match_id, player_id, team_id, pass_type,
start_x, start_y, end_x, end_y,
distance, angle, is_success, target_player_id, created_at)
VALUES (:match_id, :player_id, :team_id, :pass_type,
:start_x, :start_y, :end_x, :end_y,
:distance, :angle, :is_success, :target_player_id, NOW())"
);
$stmt->execute([
':match_id' => $pass['match_id'],
':player_id' => $pass['player_id'],
':team_id' => $pass['team_id'],
':pass_type' => $pass['pass_type'],
':start_x' => $pass['start_x'],
':start_y' => $pass['start_y'],
':end_x' => $pass['end_x'],
':end_y' => $pass['end_y'],
':distance' => $pass['distance'],
':angle' => $pass['angle'] ?? 0,
':is_success' => $pass['is_success'] ? 1 : 0,
':target_player_id' => $pass['target_player_id'] ?? null,
]);
}
性能与工程建议
| 关注点 | 建议 |
|---|---|
| 索引 | (match_id, pass_type)、(player_id, pass_type) 复合索引 |
| 预聚合 | 高并发下建 player_match_stats 汇总表,异步更新 |
| 缓存 | Redis 缓存排行榜,TTL 60s |
| 坐标系 | 统一 0-100 归一化,避免不同数据源坐标不一致 |
| 数据口径 | 明确"成功"定义:队友接到 / 未被对手拦截 / 形成射门(细分选项) |
| 可配置 | MIN_LONG_DISTANCE、DIAGONAL_MIN/MAX_ANGLE 做成配置项,不同赛事可调 |
如果你想扩展的指标
- 前场斜长传占比:
start_x > 60的斜长传比例 - 纵向推进成功率:
end_x - start_x的期望值 - 对手半场精准度 vs 本方半场精准度
- 接球后 3 秒内射门率(需要关联后续事件)
需要我针对具体场景(比如你已有的表结构、ORM 用的是 Laravel/Eloquent 还是原生 PDO、要不要做实时计算)细化出可直接落地的代码吗?告诉我你的技术栈,我可以给出更贴合项目的版本。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播