본문 바로가기
내일배움캠프/축구팀 관리 프로젝트

축구팀 관리 프로젝트 - 경기 데이터 기반 추천 포메이션 기능

by 코드스니펫 2024. 3. 12.
반응형

축구팀 관리 프로젝트 - 경기 데이터 기반 추천 포메이션 기능

 

축구 포메이션 아이콘

 

축구 경기의 승리는 수많은 변수에 의해 결정되며, 이 중에서도 '포메이션'은 경기의 결과에 큰 영향을 미칩니다. 그러나, 많은 팀들이 데이터 기반 의사결정을 통해 최적의 포메이션을 찾는 데 어려움을 겪고 있습니다.

 

본 글에서는 프로젝트의 시간적 제약과 기술적 난이도를 고려하여, 머신러닝 대신 기존의 경기 데이터를 활용한 경험적 분석 방법을 통해 최적의 포메이션을 찾는 과정을 소개합니다. 이 접근 방식은 고도의 데이터 분석 기법을 적용하지 않았음에도 불구하고, 사용자에게 실질적인 가치를 제공하는 현실적인 해결책을 찾는 데 성공했습니다.

 

 

경기 데이터 기반 추천 포메이션

 

문제 소개

포메이션 전술 화면
축구팀 포메이션 설정 화면

 

프로젝트의 목표 중 하나로, 각 팀의 경기결과(승, 무, 패)와 포메이션을 분석하여 최적의 포메이션을 추천하는 기능을 개발하는 것이었습니다. 이 과정에서 가장 큰 도전은 머신러닝 기술을 도입하는 것이었습니다. 그러나, 프로젝트 기한 내에 필요한 파이썬과 머신러닝 스킬을 숙달하는 것이 현실적으로 어려웠습니다.

 

 

문제 해결 방안

축구장 형상화한 아이콘

 

이 문제를 해결하기 위해, 저희 팀은 기존의 경기 데이터를 분석하여 경험적으로 최적의 포메이션을 추천하는 방법을 선택했습니다. 이 과정은 크게 세 단계로 이루어집니다.

 

 

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
        };
        
    }

 

 

개선할 수 있는 풀이 방법

이 방법은 프로젝트의 시간적 제약과 기술적 난이도를 고려한 현실적인 접근이었지만, 미래에는 머신러닝과 같은 고도의 데이터 분석 기법을 통해 포메이션 추천의 정확도를 높일 수 있습니다. 이를 위해, 경기 데이터 외에도 선수의 개인 기량, 날씨, 경기장 조건 등 다양한 변수를 고려하는 복잡한 모델을 개발할 수 있습니다.

 


 

끝으로

데이터 기반 의사결정은 스포츠 경기의 결과를 예측하고 최적의 전략을 세우는 데 매우 중요하다고 생각합니다. 본 글에서 소개한 경험적 분석 방법은 고도의 기술을 요구하지 않으면서도 실질적인 가치를 제공하는 해결책을 제시했습니다. 이 접근법은 향후 데이터 분석과 의사결정 과정에 있어 중요한 발판이 될 것입니다. 독자 여러분도 이러한 접근법을 통해 복잡한 문제를 해결하는 데 있어 실용적인 해결책을 찾을 수 있기를 바랍니다.

 

▼ 프로젝트간 발생한 다른 이슈들 ▼

 

 

축구팀 관리 프로젝트 - 데이터베이스 인덱싱으로 성능 최적화

축구팀 관리 프로젝트 - 데이터베이스 인덱싱으로 성능 최적화 프로젝트에서 데이터 조회 속도는 사용자 경험에 직접적인 영향을 미치는 중요한 요소였니다. 특히 경기장 정보를 조회할 때, 방

lemonlog.tistory.com

 

 

축구팀 관리 프로젝트 15일차 - 경기 후 기록 등록하는 로직 수정

축구팀 관리 프로젝트 15일차 - 경기 후 기록 등록하는 로직 오늘 한 일은 경기 후 기록 등록하는 저장 로직을 수정했습니다. 기존 저장 방식을 다시 확인해보니 중복되는 부분이 있었습니다. 그

lemonlog.tistory.com