php项目如何分析赛季末的斗志差异?

wen PHP项目 1

PHP项目分析赛季末斗志差异的方案

"赛季末斗志差异"通常出现在体育竞技、游戏对战、销售竞赛等场景,下面给出一个完整的 PHP 分析思路和实现方案。

php项目如何分析赛季末的斗志差异?

明确业务定义

"斗志"是抽象概念,必须量化成可计算指标,常见代理指标:

维度 指标示例
出勤/参与 出战场次、活跃天数、签到率
投入度 平均时长、操作次数、任务完成率
胜负欲 关键时刻胜率、逆风翻盘次数、加时赛表现
稳定性 表现方差、连败后反弹率
赛季末特化 冲榜行为、最后一轮胜率、弃赛率

先和业务方对齐:赛季末 = N 轮/X 天,基准期 = 赛季中期。

数据模型设计

-- 赛季表
CREATE TABLE seasons (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    start_date DATE,
    end_date DATE
);
-- 参赛者赛季记录
CREATE TABLE season_records (
    id BIGINT PRIMARY KEY,
    season_id INT,
    player_id INT,
    match_date DATE,
    round_no INT,              -- 第几轮
    is_win TINYINT,
    duration_sec INT,          -- 出场时长
    actions INT,               -- 操作数/贡献值
    quit TINYINT DEFAULT 0,    -- 是否弃权
    INDEX idx_season_player (season_id, player_id),
    INDEX idx_date (match_date)
);

核心分析逻辑(PHP 实现)

划分阶段

class SeasonPhaseDivider
{
    public function divide(array $season): array
    {
        $start = new DateTime($season['start_date']);
        $end   = new DateTime($season['end_date']);
        $total = $start->diff($end)->days;
        // 赛季末定义:20% 时间
        $endPhaseStart = (clone $end)->modify('-' . (int)($total * 0.2) . ' days');
        // 赛季中期:中间 40%
        $midPhaseStart = (clone $start)->modify('+' . (int)($total * 0.3) . ' days');
        $midPhaseEnd   = (clone $start)->modify('+' . (int)($total * 0.7) . ' days');
        return [
            'end' => [$endPhaseStart->format('Y-m-d'), $end->format('Y-m-d')],
            'mid' => [$midPhaseStart->format('Y-m-d'), $midPhaseEnd->format('Y-m-d')],
        ];
    }
}

提取每人分阶段指标

class MoraleMetricsExtractor
{
    public function __construct(private PDO $pdo) {}
    public function extract(int $seasonId, string $from, string $to): array
    {
        $sql = "SELECT player_id,
                       COUNT(*)                 AS games,
                       SUM(is_win)              AS wins,
                       AVG(duration_sec)        AS avg_duration,
                       AVG(actions)             AS avg_actions,
                       SUM(quit) / COUNT(*)     AS quit_rate
                FROM season_records
                WHERE season_id = :sid AND match_date BETWEEN :from AND :to
                GROUP BY player_id";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([':sid' => $seasonId, ':from' => $from, ':to' => $to]);
        $rows = [];
        foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $r) {
            $rows[$r['player_id']] = [
                'games'        => (int)$r['games'],
                'win_rate'     => $r['games'] ? $r['wins'] / $r['games'] : 0,
                'avg_duration' => (float)$r['avg_duration'],
                'avg_actions'  => (float)$r['avg_actions'],
                'quit_rate'    => (float)$r['quit_rate'],
            ];
        }
        return $rows;
    }
}

计算"斗志指数"(相对变化)

class MoraleDiffAnalyzer
{
    // 各指标的权重,可按业务调
    private array $weights = [
        'win_rate'     => 0.3,
        'avg_actions'  => 0.3,
        'avg_duration' => 0.2,
        'quit_rate'    => -0.2, // 反向指标
    ];
    public function analyze(array $mid, array $end): array
    {
        $result = [];
        foreach ($end as $pid => $endMetrics) {
            if (!isset($mid[$pid])) continue;
            $score = 0.0;
            $deltas = [];
            foreach ($this->weights as $metric => $w) {
                $m = $mid[$pid][$metric] ?? 0;
                $e = $endMetrics[$metric] ?? 0;
                // 相对变化,避免大数值指标主导
                $delta = $m > 0 ? ($e - $m) / $m : ($e > 0 ? 1 : 0);
                $delta = max(-1, min(1, $delta)); // 裁剪到 [-1,1]
                $deltas[$metric] = round($delta, 4);
                $score += $w * $delta;
            }
            $result[$pid] = [
                'morale_score' => round($score * 100, 2), // 转成 -100~100
                'deltas'       => $deltas,
                'trend'        => $this->classify($score),
            ];
        }
        return $result;
    }
    private function classify(float $score): string
    {
        return match (true) {
            $score >  0.15 => '冲刺型',   // 末段发力
            $score < -0.15 => '疲软型',   // 末段掉链
            default        => '稳定型',
        };
    }
}

汇总与输出

$divider   = new SeasonPhaseDivider();
$phases    = $divider->divide($season);
$extractor = new MoraleMetricsExtractor($pdo);
$mid = $extractor->extract($season['id'], ...$phases['mid']);
$end = $extractor->extract($season['id'], ...$phases['end']);
$analyzer = new MoraleDiffAnalyzer();
$report   = $analyzer->analyze($mid, $end);
// 按士气分排序
uasort($report, fn($a, $b) => $b['morale_score'] <=> $a['morale_score']);

进阶分析方法

  1. 统计显著性检验:对胜负率做卡方 / t 检验,避免小样本误判(可引入 PHPStatistics 或调 R/Python 微服务)。
  2. 时间序列趋势:用移动平均观察末段是否加速/放缓,而非仅比两段。
  3. 分组对比:按排名段(保级组 vs 争冠组)分组,斗志模式往往不同——保级区通常末段斗志最高。
  4. 控制变量:以"赛程强度/对手水平"为协变量回归,剔除强队对手造成的假性下滑。
  5. 可视化:输出雷达图(各指标 delta)+ 时间趋势折线,前端用 ECharts 展示。

工程注意事项

  • 小样本保护:场次 < 5 的参赛者标记为"数据不足",不进入排名。
  • 缓存结果:分析结果落 morale_analysis 表,随赛季结束/每日批量重算,避免实时跑全表。
  • 可配置权重:把 weights 和"末段定义"放配置表,业务方可调,避免写死。
  • 口径一致性:弃权、退赛如何处理需明确(作为负指标还是剔除)。
  • 防作弊:刷操作数、故意弃赛等行为要单独规则识别。

推荐目录结构

app/
├── Service/
│   └── Morale/
│       ├── SeasonPhaseDivider.php
│       ├── MoraleMetricsExtractor.php
│       ├── MoraleDiffAnalyzer.php
│       └── ReportBuilder.php
├── Console/
│   └── AnalyzeMoraleCommand.php   # php think/craft 批量任务
└── config/morale_weights.php

需要我针对你的具体场景(体育联赛 / 游戏排位 / 销售竞赛)细化指标和权重设计,或补上"统计显著性检验 + 可视化接口"的完整代码吗?告诉我你的数据源结构即可。

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