本文目录导读:

我将为您设计一个Java案例,用于统计和分析体育赛事中伤病停赛对球队表现的影响,这个系统将包括数据模型、统计分析和可视化展示。
系统架构设计
// 1. 基础数据模型
public class Player {
private int id;
private String name;
private String position;
private int age;
private double marketValue;
private double performanceRating;
// 构造函数、getter/setter
public Player(int id, String name, String position, int age,
double marketValue, double performanceRating) {
this.id = id;
this.name = name;
this.position = position;
this.age = age;
this.marketValue = marketValue;
this.performanceRating = performanceRating;
}
// getters and setters...
}
public class InjuryRecord {
private int playerId;
private String injuryType;
private LocalDate injuryDate;
private LocalDate returnDate;
private int daysOut;
private String severity; // "轻度", "中度", "严重"
public InjuryRecord(int playerId, String injuryType,
LocalDate injuryDate, LocalDate returnDate) {
this.playerId = playerId;
this.injuryType = injuryType;
this.injuryDate = injuryDate;
this.returnDate = returnDate;
this.daysOut = ChronoUnit.DAYS.between(injuryDate, returnDate);
this.severity = classifySeverity(daysOut);
}
private String classifySeverity(int days) {
if (days < 7) return "轻度";
else if (days < 30) return "中度";
else return "严重";
}
}
public class Team {
private String name;
private Map<Integer, Player> players;
private List<MatchResult> matchResults;
public Team(String name) {
this.name = name;
this.players = new HashMap<>();
this.matchResults = new ArrayList<>();
}
}
public class MatchResult {
private LocalDate matchDate;
private String opponent;
private int goalsFor;
private int goalsAgainst;
private List<Integer> injuredPlayersDuringMatch;
private double possession;
private int shotsOnTarget;
private int tackles;
public MatchResult(LocalDate matchDate, String opponent,
int goalsFor, int goalsAgainst,
List<Integer> injuredPlayersDuringMatch,
double possession, int shotsOnTarget, int tackles) {
this.matchDate = matchDate;
this.opponent = opponent;
this.goalsFor = goalsFor;
this.goalsAgainst = goalsAgainst;
this.injuredPlayersDuringMatch = injuredPlayersDuringMatch;
this.possession = possession;
this.shotsOnTarget = shotsOnTarget;
this.tackles = tackles;
}
}
统计分析引擎
public class InjuryImpactAnalyzer {
private Team team;
private Map<Integer, List<InjuryRecord>> playerInjuries;
public InjuryImpactAnalyzer(Team team) {
this.team = team;
this.playerInjuries = new HashMap<>();
}
// 核心统计方法
public ImpactStatistics analyzeImpact() {
ImpactStatistics stats = new ImpactStatistics();
// 1. 基本统计
stats.setTotalMatches(team.matchResults.size());
stats.setAverageInjuriesPerMatch(calculateAvgInjuriesPerMatch());
// 2. 胜率对比
stats.setWinRateWithInjuries(calculateWinRate(true));
stats.setWinRateWithoutInjuries(calculateWinRate(false));
// 3. 进球效率
stats.setAvgGoalsWithInjuries(calculateAvgGoals(true));
stats.setAvgGoalsWithoutInjuries(calculateAvgGoals(false));
// 4. 防守效率
stats.setAvgConcededWithInjuries(calculateAvgConceded(true));
stats.setAvgConcededWithoutInjuries(calculateAvgConceded(false));
// 5. 控球率影响
stats.setPossessionImpact(calculatePossessionImpact());
// 6. 关键球员影响
stats.setKeyPlayerImpact(analyzeKeyPlayers());
return stats;
}
private int calculateAvgInjuriesPerMatch() {
if (team.matchResults.isEmpty()) return 0;
int totalInjuries = team.matchResults.stream()
.mapToInt(m -> m.injuredPlayersDuringMatch.size())
.sum();
return totalInjuries / team.matchResults.size();
}
private double calculateWinRate(boolean withInjuries) {
List<MatchResult> matches = filterMatches(withInjuries);
if (matches.isEmpty()) return 0;
long wins = matches.stream()
.filter(m -> m.goalsFor > m.goalsAgainst)
.count();
return (double) wins / matches.size() * 100;
}
private List<MatchResult> filterMatches(boolean withInjuries) {
return team.matchResults.stream()
.filter(m -> withInjuries ?
!m.injuredPlayersDuringMatch.isEmpty() :
m.injuredPlayersDuringMatch.isEmpty())
.collect(Collectors.toList());
}
private double calculateAvgGoals(boolean withInjuries) {
List<MatchResult> matches = filterMatches(withInjuries);
if (matches.isEmpty()) return 0;
return matches.stream()
.mapToInt(m -> m.goalsFor)
.average()
.orElse(0);
}
private double calculateAvgConceded(boolean withInjuries) {
List<MatchResult> matches = filterMatches(withInjuries);
if (matches.isEmpty()) return 0;
return matches.stream()
.mapToInt(m -> m.goalsAgainst)
.average()
.orElse(0);
}
private double calculatePossessionImpact() {
List<MatchResult> matchesWithInjuries = filterMatches(true);
List<MatchResult> matchesWithoutInjuries = filterMatches(false);
if (matchesWithInjuries.isEmpty() || matchesWithoutInjuries.isEmpty()) return 0;
double avgPossessionWithInjuries = matchesWithInjuries.stream()
.mapToDouble(m -> m.possession)
.average().orElse(0);
double avgPossessionWithoutInjuries = matchesWithoutInjuries.stream()
.mapToDouble(m -> m.possession)
.average().orElse(0);
return avgPossessionWithoutInjuries - avgPossessionWithInjuries;
}
private Map<String, Double> analyzeKeyPlayers() {
Map<String, Double> keyPlayerImpact = new HashMap<>();
// 分析每位球员缺阵时的球队表现
for (Player player : team.players.values()) {
String playerName = player.getName();
List<MatchResult> matchesWithoutPlayer = team.matchResults.stream()
.filter(m -> m.injuredPlayersDuringMatch.contains(player.getId()))
.collect(Collectors.toList());
if (!matchesWithoutPlayer.isEmpty()) {
double winRate = matchesWithoutPlayer.stream()
.filter(m -> m.goalsFor > m.goalsAgainst)
.count() / (double) matchesWithoutPlayer.size() * 100;
keyPlayerImpact.put(playerName, winRate);
}
}
return keyPlayerImpact;
}
}
数据统计结果类
public class ImpactStatistics {
private int totalMatches;
private int averageInjuriesPerMatch;
private double winRateWithInjuries;
private double winRateWithoutInjuries;
private double avgGoalsWithInjuries;
private double avgGoalsWithoutInjuries;
private double avgConcededWithInjuries;
private double avgConcededWithoutInjuries;
private double possessionImpact;
private Map<String, Double> keyPlayerImpact;
// getters and setters...
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("=== 伤病停赛影响统计分析 ===\n");
sb.append(String.format("总比赛场次: %d\n", totalMatches));
sb.append(String.format("平均每场伤病人数: %.2f\n", averageInjuriesPerMatch));
sb.append("\n【胜率对比】\n");
sb.append(String.format("有伤病: %.1f%% | 无伤病: %.1f%%\n",
winRateWithInjuries, winRateWithoutInjuries));
sb.append(String.format("影响差: %.1f%%\n",
winRateWithoutInjuries - winRateWithInjuries));
sb.append("\n【进球效率】\n");
sb.append(String.format("有伤病: %.2f球/场 | 无伤病: %.2f球/场\n",
avgGoalsWithInjuries, avgGoalsWithoutInjuries));
sb.append("\n【防守表现】\n");
sb.append(String.format("有伤病失球: %.2f球/场 | 无伤病失球: %.2f球/场\n",
avgConcededWithInjuries, avgConcededWithoutInjuries));
sb.append("\n【控球率影响】\n");
sb.append(String.format("平均降低: %.1f%%\n", possessionImpact));
if (!keyPlayerImpact.isEmpty()) {
sb.append("\n【关键球员影响】\n");
keyPlayerImpact.forEach((player, winRate) ->
sb.append(String.format("%s缺阵时胜率: %.1f%%\n", player, winRate)));
}
return sb.toString();
}
}
数据可视化组件
import java.awt.*;
import javax.swing.*;
public class ImpactChartPanel extends JPanel {
private ImpactStatistics stats;
public ImpactChartPanel(ImpactStatistics stats) {
this.stats = stats;
setPreferredSize(new Dimension(800, 500));
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// 绘制柱状图
drawBarChart(g2d);
// 绘制饼图
// drawPieChart(g2d);
}
private void drawBarChart(Graphics2D g2d) {
int margin = 50;
int barWidth = 40;
int chartHeight = 300;
int maxValue = 100;
// 标题
g2d.setColor(Color.BLACK);
g2d.setFont(new Font("Arial", Font.BOLD, 16));
g2d.drawString("伤病对胜率的影响", 300, 30);
// 数据条
int xPos = margin;
// Win rate with injuries
g2d.setColor(Color.RED);
int height = (int)(stats.getWinRateWithInjuries() / maxValue * chartHeight);
g2d.fillRect(xPos, 300 - height, barWidth, height);
g2d.drawString(String.format("%.1f%%", stats.getWinRateWithInjuries()),
xPos, 320);
xPos += 100;
// Win rate without injuries
g2d.setColor(Color.GREEN);
height = (int)(stats.getWinRateWithoutInjuries() / maxValue * chartHeight);
g2d.fillRect(xPos, 300 - height, barWidth, height);
g2d.drawString(String.format("%.1f%%", stats.getWinRateWithoutInjuries()),
xPos, 320);
// 图例
g2d.setColor(Color.RED);
g2d.fillRect(margin, 350, 20, 20);
g2d.setColor(Color.BLACK);
g2d.drawString("有伤病", margin + 25, 365);
g2d.setColor(Color.GREEN);
g2d.fillRect(margin + 100, 350, 20, 20);
g2d.setColor(Color.BLACK);
g2d.drawString("无伤病", margin + 125, 365);
}
}
主程序入口
public class InjuryImpactAnalysisSystem {
public static void main(String[] args) {
// 1. 创建球队数据
Team team = createSampleTeam();
// 2. 创建分析器
InjuryImpactAnalyzer analyzer = new InjuryImpactAnalyzer(team);
// 3. 执行分析
ImpactStatistics stats = analyzer.analyzeImpact();
// 4. 输出结果
System.out.println(stats);
// 5. 生成可视化图表
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("伤病影响分析");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new ImpactChartPanel(stats));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
// 6. 导出CSV报告
exportToCSV(stats);
}
private static Team createSampleTeam() {
Team team = new Team("示例足球俱乐部");
// 创建球员
addPlayer(team, 1, "前锋A", "前锋", 25, 8000000, 85);
addPlayer(team, 2, "中场B", "中场", 28, 6500000, 88);
addPlayer(team, 3, "后卫C", "后卫", 26, 7000000, 82);
// ... 更多球员
// 添加比赛数据
addMatchResult(team, LocalDate.of(2024, 1, 15), "对手1",
2, 1, Arrays.asList(), 55.5, 15, 20);
addMatchResult(team, LocalDate.of(2024, 1, 22), "对手2",
1, 2, Arrays.asList(1), 48.2, 10, 18);
addMatchResult(team, LocalDate.of(2024, 2, 1), "对手3",
3, 0, Arrays.asList(), 60.1, 18, 22);
// ... 更多比赛
// 添加伤病记录
analyzer.addInjuryRecord(1, "大腿肌肉拉伤",
LocalDate.of(2024, 1, 20),
LocalDate.of(2024, 2, 5));
return team;
}
private static void exportToCSV(ImpactStatistics stats) {
try (FileWriter writer = new FileWriter("injury_impact_report.csv")) {
writer.append("指标,有伤病,无伤病,差异\n");
writer.append(String.format("胜率,%s,%s,%s\n",
formatPercent(stats.getWinRateWithInjuries()),
formatPercent(stats.getWinRateWithoutInjuries()),
formatPercent(stats.getWinRateWithoutInjuries() -
stats.getWinRateWithInjuries())));
writer.append(String.format("场均进球,%s,%s,%s\n",
stats.getAvgGoalsWithInjuries(),
stats.getAvgGoalsWithoutInjuries(),
stats.getAvgGoalsWithoutInjuries() -
stats.getAvgGoalsWithInjuries()));
writer.append(String.format("场均失球,%s,%s,%s\n",
stats.getAvgConcededWithInjuries(),
stats.getAvgConcededWithoutInjuries(),
stats.getAvgConcededWithoutInjuries() -
stats.getAvgConcededWithInjuries()));
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
private static String formatPercent(double value) {
return String.format("%.1f%%", value);
}
}
扩展功能
// 1. 预测模型
public class InjuryRiskPredictor {
public double predictImpact(Player player, int daysOut) {
// 基于历史数据建立预测模型
double baseImpact = 0.3;
double severityMultiplier = daysOut > 30 ? 1.5 :
daysOut > 15 ? 1.2 : 1.0;
double positionMultiplier = player.getPosition().equals("前锋") ?
0.35 : 0.25;
return baseImpact * severityMultiplier *
(player.getPerformanceRating() / 100);
}
}
// 2. 可视化增强
public class RadarChart extends JPanel {
private double[] values;
private String[] labels;
// 雷达图绘制逻辑
}
// 3. 时间序列分析
public class TimeSeriesAnalyzer {
public Map<LocalDate, Double> calculateTrend(List<MatchResult> matches) {
// 计算滚动平均胜率
int windowSize = 5;
Map<LocalDate, Double> trends = new LinkedHashMap<>();
for (int i = windowSize; i <= matches.size(); i++) {
double avg = matches.subList(i - windowSize, i).stream()
.mapToDouble(m -> m.goalsFor > m.goalsAgainst ? 100 : 0)
.average().orElse(0);
trends.put(matches.get(i).getMatchDate(), avg);
}
return trends;
}
}
使用建议
- 数据准备:确保数据完整,包括球员历史表现、伤病记录和完整比赛数据
- 指标选择:根据项目需求选择关键指标,如胜率、进球效率、防守稳定性等
- 可视化选择:根据受众选择图表类型,管理层面板推荐使用折线图和柱状图
- 定期更新:系统应支持数据定期更新,保持统计分析的时效性
- 结合业务:分析结果应结合俱乐部实际运营情况,给出针对性建议
这个系统可以很好地帮助教练组和管理层了解伤病对球队的影响,为阵容调整和伤病管理提供数据支持。