java案例如何量化主队球迷人数影响?

wen java案例 10

量化主队球迷人数对比赛影响的Java案例分析

问题定义

在体育数据分析中,"主队球迷人数影响"通常指主场观众数量对主队表现(胜率、进球数、得分等)的量化影响,下面给出一个完整的Java实现思路与代码。

java案例如何量化主队球迷人数影响?


分析框架

维度 说明
自变量 X 主队球迷人数(上座人数)
因变量 Y 比赛结果(胜=1/平=0.5/负=0,或进球差)
控制变量 对手实力、赛季、天气、球队近期状态
模型 线性回归 / 逻辑回归 / 相关性分析

数据结构设计

public class MatchRecord {
    private String matchId;
    private String homeTeam;
    private String awayTeam;
    private int attendance;        // 主队球迷人数
    private int homeGoals;
    private int awayGoals;
    private int homeRank;          // 主队联赛排名(控制变量)
    private int awayRank;          // 客队联赛排名
    private String weather;        // 天气
    private String season;
    // 构造器、getter、setter 省略
}

核心量化指标

胜率与上座率分组对比

import java.util.*;
import java.util.stream.Collectors;
public class AttendanceAnalyzer {
    /** 按上座人数分档,统计主队胜率 */
    public static Map<String, Double> winRateByAttendanceBucket(List<MatchRecord> matches) {
        // 计算上座人数的分位数
        List<Integer> attendances = matches.stream()
                .map(MatchRecord::getAttendance)
                .sorted()
                .collect(Collectors.toList());
        int q1 = attendances.get((int) (attendances.size() * 0.25));
        int q2 = attendances.get((int) (attendances.size() * 0.50));
        int q3 = attendances.get((int) (attendances.size() * 0.75));
        Map<String, List<MatchRecord>> buckets = new HashMap<>();
        buckets.put("低上座(<Q1)", new ArrayList<>());
        buckets.put("中低(Q1-Q2)", new ArrayList<>());
        buckets.put("中高(Q2-Q3)", new ArrayList<>());
        buckets.put("高上座(>Q3)", new ArrayList<>());
        for (MatchRecord m : matches) {
            int att = m.getAttendance();
            if (att <= q1) buckets.get("低上座(<Q1)").add(m);
            else if (att <= q2) buckets.get("中低(Q1-Q2)").add(m);
            else if (att <= q3) buckets.get("中高(Q2-Q3)").add(m);
            else buckets.get("高上座(>Q3)").add(m);
        }
        Map<String, Double> result = new LinkedHashMap<>();
        for (Map.Entry<String, List<MatchRecord>> e : buckets.entrySet()) {
            long wins = e.getValue().stream()
                    .filter(m -> m.getHomeGoals() > m.getAwayGoals())
                    .count();
            double rate = e.getValue().isEmpty() ? 0 : (double) wins / e.getValue().size();
            result.put(e.getKey(), rate);
        }
        return result;
    }
}

皮尔逊相关系数(上座人数 vs 净胜球)

public class CorrelationUtil {
    public static double pearson(List<Double> x, List<Double> y) {
        int n = x.size();
        double sumX = x.stream().mapToDouble(Double::doubleValue).sum();
        double sumY = y.stream().mapToDouble(Double::doubleValue).sum();
        double sumXY = 0, sumX2 = 0, sumY2 = 0;
        for (int i = 0; i < n; i++) {
            sumXY += x.get(i) * y.get(i);
            sumX2 += x.get(i) * x.get(i);
            sumY2 += y.get(i) * y.get(i);
        }
        double num = n * sumXY - sumX * sumY;
        double den = Math.sqrt((n * sumX2 - sumX * sumX) * (n * sumY2 - sumY * sumY));
        return den == 0 ? 0 : num / den;
    }
    public static void main(String[] args) {
        List<Double> attendance = Arrays.asList(20000.0, 35000.0, 45000.0, 60000.0, 75000.0);
        List<Double> goalDiff   = Arrays.asList(0.0, 1.0, 1.0, 2.0, 3.0);
        System.out.println("相关系数 r = " + pearson(attendance, goalDiff));
    }
}

多元线性回归(控制对手实力)

单纯相关无法排除"强队吸引更多球迷"这一反向因果,需引入控制变量。

import org.apache.commons.math3.linear.*;
public class LinearRegressionAnalyzer {
    /**
     * Y = b0 + b1*attendance + b2*homeRank + b3*awayRank
     * 返回系数数组 [b0, b1, b2, b3]
     */
    public static double[] fit(List<MatchRecord> data) {
        int n = data.size();
        double[][] design = new double[n][4];
        double[] y = new double[n];
        for (int i = 0; i < n; i++) {
            MatchRecord m = data.get(i);
            design[i][0] = 1;                                        // 截距
            design[i][1] = m.getAttendance() / 10000.0;              // 万人为单位,便于解读
            design[i][2] = m.getHomeRank();
            design[i][3] = m.getAwayRank();
            y[i] = m.getHomeGoals() - m.getAwayGoals();              // 净胜球
        }
        RealMatrix X = new Array2DRowRealMatrix(design);
        RealVector Y = new ArrayRealVector(y);
        // 最小二乘:beta = (X'X)^-1 X'Y
        RealMatrix Xt = X.transpose();
        RealMatrix XtX = Xt.multiply(X);
        RealVector XtY = Xt.operate(Y);
        DecompositionSolver solver = new LUDecomposition(XtX).getSolver();
        return solver.solve(XtY).toArray();
    }
    public static void main(String[] args) {
        List<MatchRecord> data = loadData();  // 加载历史数据
        double[] beta = fit(data);
        System.out.println("截距 b0 = " + beta[0]);
        System.out.println("球迷效应 b1 = " + beta[1] + " (每增加1万人,净胜球平均+  " + beta[1] + ")");
        System.out.println("主队实力 b2 = " + beta[2]);
        System.out.println("客队实力 b3 = " + beta[3]);
    }
}

进阶:控制球队固定效应

由于不同球队基础实力差异大,可做去均值处理(固定效应):

public static List<MatchRecord> demeanByTeam(List<MatchRecord> data) {
    Map<String, Double> teamAvgAttendance = data.stream()
        .collect(Collectors.groupingBy(
            MatchRecord::getHomeTeam,
            Collectors.averagingDouble(MatchRecord::getAttendance)));
    Map<String, Double> teamAvgGoalDiff = data.stream()
        .collect(Collectors.groupingBy(
            MatchRecord::getHomeTeam,
            Collectors.averagingDouble(m -> m.getHomeGoals() - m.getAwayGoals())));
    data.forEach(m -> {
        m.setAttendance((int) (m.getAttendance() - teamAvgAttendance.get(m.getHomeTeam())));
        // goalDiff 同样去均值后参与回归
    });
    return data;
}

完整量化流程

数据采集    →  爬取/导入比赛数据(含上座、比分、排名)
2. 数据清洗    →  剔除空场、疫情赛季异常值
3. 描述统计    →  分组胜率、相关系数
4. 多元回归    →  加入控制变量,得出边际效应 b1
5. 稳健性检验  →  去均值、分赛季子样本、工具变量
6. 结果解读    →  "每增加1万球迷,主队净胜球平均 +0.15"

结果解读示例

指标 数值 含义
r 42 中等正相关
b1 +0.15 每增加1万球迷,净胜球+0.15
p-value <0.01 显著

主队球迷人数对比赛结果存在统计显著的正向影响,符合"主场优势"理论。


注意事项(坑点)

  1. 反向因果:强队吸引更多球迷 → 需控制球队实力。
  2. 混淆变量:周末/节假日比赛人多且对手弱 → 需加入时间/对手固定效应。
  3. 疫情空场数据:2020-2021赛季应单独分析(天然实验)。
  4. 样本量:少于100场时回归系数不稳定。
  5. 容量上限效应:球场容量饱和后效应可能非线性 → 可尝试对数或分段回归。

如果你有具体数据集格式(CSV字段、联赛、年份),我可以帮你写针对性的 loadData() 和完整可运行 Demo。

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