php项目统计斜长传精准度如何?

wen PHP项目 3

PHP 项目统计斜长传精准度的实现方案

在足球数据统计系统中,斜长传精准度是一个常见的战术指标,下面给你一套完整的 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、要不要做实时计算)细化出可直接落地的代码吗?告诉我你的技术栈,我可以给出更贴合项目的版本。

上一篇这个php项目是否参考了过往同盘数据?

下一篇当前分类已是最新一篇

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

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