축구팀 관리 프로젝트 - 경기 데이터 기반 추천 포메이션 기능
축구 경기의 승리는 수많은 변수에 의해 결정되며, 이 중에서도 '포메이션'은 경기의 결과에 큰 영향을 미칩니다. 그러나, 많은 팀들이 데이터 기반 의사결정을 통해 최적의 포메이션을 찾는 데 어려움을 겪고 있습니다.
본 글에서는 프로젝트의 시간적 제약과 기술적 난이도를 고려하여, 머신러닝 대신 기존의 경기 데이터를 활용한 경험적 분석 방법을 통해 최적의 포메이션을 찾는 과정을 소개합니다. 이 접근 방식은 고도의 데이터 분석 기법을 적용하지 않았음에도 불구하고, 사용자에게 실질적인 가치를 제공하는 현실적인 해결책을 찾는 데 성공했습니다.
경기 데이터 기반 추천 포메이션
문제 소개
프로젝트의 목표 중 하나로, 각 팀의 경기결과(승, 무, 패)와 포메이션을 분석하여 최적의 포메이션을 추천하는 기능을 개발하는 것이었습니다. 이 과정에서 가장 큰 도전은 머신러닝 기술을 도입하는 것이었습니다. 그러나, 프로젝트 기한 내에 필요한 파이썬과 머신러닝 스킬을 숙달하는 것이 현실적으로 어려웠습니다.
문제 해결 방안
이 문제를 해결하기 위해, 저희 팀은 기존의 경기 데이터를 분석하여 경험적으로 최적의 포메이션을 추천하는 방법을 선택했습니다. 이 과정은 크게 세 단계로 이루어집니다.
1단계. 경기 결과 및 포메이션 조회 (getTeamMatchInfo)
이 함수는 특정 팀의 홈 및 원정 경기 결과와 포메이션을 조회합니다. 데이터베이스에서 해당 팀이 참가한 모든 경기를 찾아, 각 경기의 승/무/패 결과와 사용된 포메이션을 집계합니다.
/**
* 해당팀 경기결과(승,무,패), 포메이션 조회
* @param teamId
* @param matchId
* @returns
*/
async getTeamMatchInfo(homeTeamId:number) {
const homeMatchList = await this.matchRepository.find({where: [{ home_team_id: homeTeamId }, { away_team_id: homeTeamId }],})
// const homePlayerStats = await this.playerStatsRepository.find({where:{team_id:homeTeamId}});
// const opponentPlayerStats = await this.playerStatsRepository.find({where:{team_id:opponent_team_id}});
// 경기 결과를 담을 배열
const matchResults = [];
for (const match of homeMatchList) {
// 현재 경기의 모든 선수 통계 가져오기
const playerStats = await this.playerStatsRepository.find({
where: { match_id: match.id }
});
// 경기별 팀별로 골수 합산
let homeGoals = 0;
let awayGoals = 0;
for (const stat of playerStats) {
if (stat.team_id === homeTeamId && stat.match_id === match.id) {
homeGoals += stat.goals;
} else {
awayGoals += stat.goals;
}
}
// 승무패 결과 계산
let result;
if (homeGoals > awayGoals) {
result = 1; // 승리
} else if (homeGoals === awayGoals) {
result = 0; // 무승부
} else {
result = -1; // 패배
}
// 현재 경기의 포메이션 정보 조회
const formation = await this.getMatchFormation(homeTeamId, match.id);
// formation 배열의 첫 번째 요소에서 formation 필드 값 추출
const teamFormation = formation.length > 0 ? formation[0].formation : "정보 없음";
// 경기 ID, 결과, 그리고 포메이션 정보를 배열에 추가
matchResults.push({
match_id: match.id,
result: result,
formation: teamFormation // 여기에 포메이션 정보 추가
});
}
return matchResults;
}
2단계. 포메이션별 승률 계산 (calculateFormationWinRate)
조회된 경기 결과를 바탕으로 각 포메이션별 승률을 계산합니다. 이는 포메이션별 성공률을 분석하여 가장 효과적인 전략을 선택하는 데 도움을 줍니다.
// 포메이션별 승률 계산
async calculateFormationWinRate(matchList: MatchResultDetail[]): Promise<Map<string, { winRate: number, loseRate: number, games: number }>> {
const formationStats: Map<string, { wins: number; losses: number; games: number }> = new Map();
matchList.forEach(match => {
const formation = match.formation;
if (!formationStats.has(formation)) {
formationStats.set(formation, { wins: 0, losses: 0, games: 0 });
}
const stats = formationStats.get(formation);
stats.games += 1;
if (match.result === 1) { // 승리
stats.wins += 1;
} else if (match.result === -1) { // 패배
stats.losses += 1;
}
});
// 승률과 패배율 계산
const formationWinLoseRate: Map<string, { winRate: number; loseRate: number; games: number }> = new Map();
formationStats.forEach((value, key) => {
formationWinLoseRate.set(key, {
winRate: value.wins / value.games,
loseRate: value.losses / value.games,
games: value.games
});
});
return formationWinLoseRate;
}
3단계. 최적 포메이션 조회 (getBestFormation)
각 팀의 승률이 가장 높은 포메이션을 찾아 추천합니다. 이는 상대 팀의 약점을 공략하고, 자신의 강점을 최대한 활용하는 전략을 세우는 데 중요한 정보를 제공합니다.
/**
* 최적 포메이션 조회
* @param teamId
* @param matchId
* @returns
*/
async getBestFormation(homeTeamId:number, opponent_team_id: number) {
const homeMatchList = await this.getTeamMatchInfo(homeTeamId);
const opponentMatchList = await this.getTeamMatchInfo(opponent_team_id);
const homeWinLoseRate = await this.calculateFormationWinRate(homeMatchList);
const opponentWinLoseRate = await this.calculateFormationWinRate(opponentMatchList);
// 홈팀의 최고 승률 포메이션 찾기
let homeBestFormation = "";
let highestWinRate = 0;
homeWinLoseRate.forEach((value, formation) => {
if (value.winRate > highestWinRate) {
homeBestFormation = formation;
highestWinRate = value.winRate;
}
});
// 상대팀의 최고 패배율 포메이션 찾기
let opponentHighestLoseFormation = "";
let highestLoseRate = 0;
opponentWinLoseRate.forEach((value, formation) => {
if (value.loseRate > highestLoseRate) {
opponentHighestLoseFormation = formation;
highestLoseRate = value.loseRate;
}
});
// 승리 확률 계산 (단순화된 모델을 가정)
let winProbability = (highestWinRate - highestLoseRate + 1) / 2; // 가정된 계산식
winProbability = Math.max(0, Math.min(winProbability, 1)); // 확률은 0과 1 사이의 값이어야 함
return {
formation1:homeBestFormation,
formation2:opponentHighestLoseFormation,
winProbability
};
}
개선할 수 있는 풀이 방법
이 방법은 프로젝트의 시간적 제약과 기술적 난이도를 고려한 현실적인 접근이었지만, 미래에는 머신러닝과 같은 고도의 데이터 분석 기법을 통해 포메이션 추천의 정확도를 높일 수 있습니다. 이를 위해, 경기 데이터 외에도 선수의 개인 기량, 날씨, 경기장 조건 등 다양한 변수를 고려하는 복잡한 모델을 개발할 수 있습니다.
끝으로
데이터 기반 의사결정은 스포츠 경기의 결과를 예측하고 최적의 전략을 세우는 데 매우 중요하다고 생각합니다. 본 글에서 소개한 경험적 분석 방법은 고도의 기술을 요구하지 않으면서도 실질적인 가치를 제공하는 해결책을 제시했습니다. 이 접근법은 향후 데이터 분석과 의사결정 과정에 있어 중요한 발판이 될 것입니다. 독자 여러분도 이러한 접근법을 통해 복잡한 문제를 해결하는 데 있어 실용적인 해결책을 찾을 수 있기를 바랍니다.
▼ 프로젝트간 발생한 다른 이슈들 ▼
'내일배움캠프 > 축구팀 관리 프로젝트' 카테고리의 다른 글
축구팀 관리 프로젝트 - 데이터베이스 인덱싱으로 성능 최적화 (0) | 2024.03.12 |
---|---|
축구팀 관리 프로젝트 - WebSocket 활용한 실시간 채팅 전송 과정 (1) | 2024.02.28 |
축구팀 관리 프로젝트 - 대규모 트래픽 관리 위해 스케일업, 캐싱 적용 (1) | 2024.02.25 |
축구팀 관리 프로젝트 40일차 - 최종발표, 공부는 계속된다, 취업도 (1) | 2024.02.21 |
축구팀 관리 프로젝트 38일차 - soft delete된 팀 멤버가 조회되는 오류 (0) | 2024.02.19 |