根据php项目,传球成功率与胜率相关性?

wen PHP项目 1

PHP项目中传球成功率与胜率的相关性分析

下面给你一套完整的 PHP 实现方案,从数据准备到相关性计算再到可视化。

根据php项目,传球成功率与胜率相关性?


核心思路

传球成功率(Pass Success Rate)与胜率(Win Rate)的相关性,通常用 皮尔逊相关系数(Pearson r) 来衡量:

$$r = \frac{\sum (x_i - \bar{x})(y_i - \bar{y})}{\sqrt{\sum (x_i - \bar{x})^2 \sum (y_i - \bar{y})^2}}$$

  • x = 球队某场比赛的传球成功率
  • y = 该场比赛结果(胜=1,平=0.5,负=0)

PHP 实现代码

数据表结构(MySQL)

CREATE TABLE match_stats (
    id INT AUTO_INCREMENT PRIMARY KEY,
    team_id INT NOT NULL,
    match_id INT NOT NULL,
    passes_total INT,
    passes_completed INT,
    result ENUM('win','draw','loss'),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

数据模型与查询

<?php
class MatchStatsRepository
{
    private PDO $pdo;
    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }
    /**
     * 获取球队每场比赛的传球成功率与胜负结果
     * @return array<array{pass_rate: float, result_score: float}>
     */
    public function getPassRateAndResult(int $teamId): array
    {
        $sql = "SELECT 
                    passes_completed / NULLIF(passes_total, 0) AS pass_rate,
                    CASE result
                        WHEN 'win'  THEN 1.0
                        WHEN 'draw' THEN 0.5
                        WHEN 'loss' THEN 0.0
                    END AS result_score
                FROM match_stats
                WHERE team_id = :team_id
                  AND passes_total > 0";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([':team_id' => $teamId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
}

相关系数计算工具类

<?php
class CorrelationCalculator
{
    /**
     * 皮尔逊相关系数
     * @param float[] $x
     * @param float[] $y
     * @return array{r: float, p_value: float, n: int, interpretation: string}
     */
    public static function pearson(array $x, array $y): array
    {
        $n = count($x);
        if ($n < 3 || $n !== count($y)) {
            throw new InvalidArgumentException('样本量不足或维度不匹配');
        }
        $meanX = array_sum($x) / $n;
        $meanY = array_sum($y) / $n;
        $num = 0.0;
        $denX = 0.0;
        $denY = 0.0;
        for ($i = 0; $i < $n; $i++) {
            $dx = $x[$i] - $meanX;
            $dy = $y[$i] - $meanY;
            $num  += $dx * $dy;
            $denX += $dx * $dx;
            $denY += $dy * $dy;
        }
        $denominator = sqrt($denX * $denY);
        $r = $denominator == 0 ? 0.0 : $num / $denominator;
        // t 检验求 p 值(双侧)
        $t = $r * sqrt(($n - 2) / max(1e-9, 1 - $r * $r));
        $p = self::tDistTwoTailed(abs($t), $n - 2);
        return [
            'r' => round($r, 4),
            'p_value' => round($p, 6),
            'n' => $n,
            'interpretation' => self::interpret($r, $p),
        ];
    }
    private static function interpret(float $r, float $p): string
    {
        $strength = match (true) {
            abs($r) >= 0.8 => '极强',
            abs($r) >= 0.6 => '强',
            abs($r) >= 0.4 => '中等',
            abs($r) >= 0.2 => '弱',
            default        => '可忽略',
        };
        $direction = $r > 0 ? '正' : '负';
        $sig = $p < 0.05 ? '显著' : '不显著';
        return "{$direction}相关,强度{$strength}({$sig})";
    }
    /**
     * t 分布双侧 p 值(Abramowitz & Stegun 近似)
     */
    private static function tDistTwoTailed(float $t, int $df): float
    {
        $x = $df / ($df + $t * $t);
        return self::incompleteBeta($x, $df / 2, 0.5); // 即 p 值
    }
    private static function incompleteBeta(float $x, float $a, float $b): float
    {
        // 简化:使用连分式实现(略,可用开源库如 pear/pear 替代)
        // 建议生产环境使用:composer require markrogoyski/math-php
        $bt = exp(self::lnGamma($a + $b) - self::lnGamma($a) - self::lnGamma($b)
                + $a * log($x) + $b * log(1 - $x));
        return $bt / $a * self::betacf($x, $a, $b);
    }
    private static function lnGamma(float $z): float
    {
        // Lanczos 近似
        $g = 7;
        $c = [0.99999999999980993, 676.5203681218851, -1259.1392167224028,
              771.32342877765313, -176.61502916214059, 12.507343278686905,
              -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7];
        if ($z < 0.5) return log(M_PI / sin(M_PI * $z)) - self::lnGamma(1 - $z);
        $z -= 1;
        $x = $c[0];
        for ($i = 1; $i < $g + 2; $i++) $x += $c[$i] / ($z + $i);
        $tmp = $z + $g + 0.5;
        return 0.5 * log(2 * M_PI) + ($z + 0.5) * log($tmp) - $tmp + log($x);
    }
    private static function betacf(float $x, float $a, float $b): float
    {
        $MAXIT = 100; $EPS = 3e-7; $FPMIN = 1e-30;
        $qab = $a + $b; $qap = $a + 1; $qam = $a - 1;
        $c = 1.0; $d = 1 - $qab * $x / $qap;
        if (abs($d) < $FPMIN) $d = $FPMIN;
        $d = 1 / $d; $h = $d;
        for ($m = 1; $m <= $MAXIT; $m++) {
            $m2 = 2 * $m;
            $aa = $m * ($b - $m) * $x / (($qam + $m2) * ($a + $m2));
            $d = 1 + $aa * $d; if (abs($d) < $FPMIN) $d = $FPMIN;
            $c = 1 + $aa / $c;  if (abs($c) < $FPMIN) $c = $FPMIN;
            $d = 1 / $d; $h *= $d * $c;
            $aa = -($a + $m) * ($qab + $m) * $x / (($a + $m2) * ($qap + $m2));
            $d = 1 + $aa * $d; if (abs($d) < $FPMIN) $d = $FPMIN;
            $c = 1 + $aa / $c;  if (abs($c) < $FPMIN) $c = $FPMIN;
            $d = 1 / $d; $del = $d * $c; $h *= $del;
            if (abs($del - 1) < $EPS) break;
        }
        return $h;
    }
}

业务服务层

<?php
class PassWinAnalysisService
{
    public function __construct(private MatchStatsRepository $repo) {}
    public function analyze(int $teamId): array
    {
        $rows = $this->repo->getPassRateAndResult($teamId);
        if (count($rows) < 5) {
            return ['error' => '样本量不足(少于5场)'];
        }
        $x = array_column($rows, 'pass_rate');
        $y = array_column($rows, 'result_score');
        $corr = CorrelationCalculator::pearson($x, $y);
        // 附加:平均传球成功率对比胜/平/负
        $grouped = [];
        foreach ($rows as $row) {
            $grouped[$row['result_score']][] = $row['pass_rate'];
        }
        $avgByResult = [];
        foreach ($grouped as $score => $rates) {
            $avgByResult[$score] = round(array_sum($rates) / count($rates), 4);
        }
        return array_merge($corr, [
            'avg_pass_rate_by_result' => $avgByResult,
        ]);
    }
}

调用示例

<?php
$pdo = new PDO('mysql:host=localhost;dbname=football;charset=utf8mb4', 'user', 'pass', [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
$repo = new MatchStatsRepository($pdo);
$service = new PassWinAnalysisService($repo);
$result = $service->analyze(teamId: 12);
print_r($result);

输出示例:

Array (
    [r] => 0.5231
    [p_value] => 0.003421
    [n] => 30
    [interpretation] => 正相关,强度中等(显著)
    [avg_pass_rate_by_result] => Array (
        [1]   => 0.8742   // 胜
        [0.5] => 0.8510   // 平
        [0]   => 0.8295   // 负
    )
)

结果解读

r 值范围 相关性强度 典型足球场景)
6 ~ 1.0 强正相关 传球成功率越高胜率越高(如曼城、巴萨)
4 ~ 0.6 中等正相关 有一定影响,但不是决定性
2 ~ 0.4 弱相关 单个指标解释力有限
-0.2 ~ 0.2 可忽略 基本无关
< -0.2 负相关 反常现象(样本问题?)

典型经验值(欧洲五大联赛数据):

  • 顶级技术型球队:r ≈ 45 ~ 0.65(中等偏强)
  • 完整联赛 380 场样本:r ≈ 35 ~ 0.50
  • 单支球队 30~38 场:波动较大,需配合 p 值判断

进阶建议

  1. 区分主客场:客队传球成功率普遍低 3~5%,需分层分析
  2. 对手强弱加权:用 Elo 差值作为协变量,做偏相关分析
  3. 用胜率而非比赛结果:若按赛季聚合,需先算赛季平均传球成功率 → 赛季胜率
  4. 多因子回归:胜率 ~ β0 + β1·传球成功率 + β2·射正数 + β3·控球率 + ...
  5. 安装现成库(推荐,不必手写 t 分布):
    composer require markrogoyski/math-php
    use MathPHP\Statistics\Correlation;
    $result = Correlation::pearson($x, $y); // ['r', 'ρ', ...]

一句话结论

在 PHP 项目中,计算传球成功率与胜率的皮尔逊相关系数是可行的,代码量约 100 行;实战数据通常显示 中等正相关(r≈0.4~0.6),说明传球成功率是胜率的重要但非唯一因素,建议结合 p 值、样本量、队内分组均值综合判断,避免得出"传球成功率高必然赢"的误导结论。

如果你把数据表结构或具体样本贴出来,我可以帮你把 SQL 和 PHP 代码调整为完全匹配的版本。

友情链接

欧易

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

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