판타지 풋볼에서 예측 가능성은 얼마나 될까요? 저를 비롯한 많은 풋볼 팬들은 매년 판타지 리그에서 꾸준히 성공할 수 있는 방법이 있는지 궁금해합니다. JMP를 통해 알아보겠습니다!
JMP의 파이썬 통합 기능이 향상됨에 따라 판타지 풋볼 리그 데이터를 JMP로 가져오는 것이 그 어느 때보다 쉬워졌습니다. 이 발표에서는 ESPN, Sleeper와 같은 판타지 풋볼 제공업체의 공식 API에 접근하기 위해 JMP의 기본 HTTP 요청 기능을 사용하는 방법을 간략하게 보여준 후, 파이썬 커뮤니티에서 제공하는 기존 API 래퍼를 활용하여 수년간의 판타지 리그 데이터를 가져오는 방법을 중점적으로 다룹니다. 리그 순위, 플레이오프 결과, NFL 선수 통계, 판타지 경기 결과, 판타지 드래프트의 숨겨진 보석과 실패 사례 등 탐색할 데이터가 무궁무진합니다!
데이터를 가져와 표 형식으로 정리한 후에는 JMP의 통계 분석 도구를 사용하여 리그별 데이터를 탐색하고 분석하여 경쟁 우위를 확보할 수 있는 잠재적 추세와 패턴을 찾아낼 수 있습니다.
판타지 리그에서 성공은 주로 드래프트에서 좋은 선수를 뽑는 데 달려 있을까요, 아니면 시즌 내내 트레이드와 선수 영입을 통해 좋은 성적을 내는 데 달려 있을까요? 뛰어난 활약을 펼치는 선수들은 매년 같은 활약을 보여줄 수 있을까요? 팀에 판타지 리그 상위 3위 안에 드는 선수를 보유하는 것은 얼마나 중요할까요? 러닝백은 여전히 판타지 리그에서 가장 큰 영향력을 발휘하는 포지션일까요, 아니면 와이드 리시버와 쿼터백이 그 자리를 차지하게 되었을까요? JMP가 여러분의 다음 리그 우승에 도움이 될 수 있는지 알아보기 위해 이러한 질문들을 살펴보겠습니다.
안녕하세요 여러분. 저는 JMP에서 테스트 엔지니어로 일하는 닉 홈즈입니다. 오늘 함께해 주셔서 감사합니다. 오늘은 JMP를 활용하여 판타지 풋볼 리그에서 우승하는 방법에 대해 이야기해 보겠습니다. 물론 판타지 풋볼에서는 어떤 것도 보장할 수 없기 때문에 '어쩌면'이라고 말씀드렸습니다. 오늘은 판타지 풋볼 데이터를 JMP에 입력하고 분석을 실행하여 어떤 결과를 얻을 수 있는지 살펴보겠습니다.
제 소개를 간단히 드리겠습니다. 저는 판타지 풋볼에 푹 빠진 사람이라고 할 수 있습니다. 2015년부터 운영 중인 12인 리드래프트 리그의 커미셔너를 맡고 있습니다. 오늘 발표와 분석에 사용할 데이터는 주로 이 리그의 데이터입니다. 이 발표 또는 프로젝트를 시작하게 된 계기는, 2022년에는 리그 우승을 차지했지만 2023년에는 최하위로 마감했기 때문입니다. 판타지 풋볼에서 제 성적을 예측하는 데 도움이 될 만한 분석 방법을 찾고 싶었습니다.
저는 통계 전문가는 아니지만 최선을 다해 분석해 보겠습니다. ESPN과 Sleeper의 데이터를 활용할 예정입니다. ESPN은 아마 다들 잘 아시겠지만, 판타지 풋볼 서비스를 제공하는 업체입니다. Sleeper도 마찬가지로 판타지 풋볼 리그를 운영하는 플랫폼입니다. 두 플랫폼에서 데이터를 가져와 분석에 사용할 것입니다.
저는 2015년부터 2020년까지 SPN을 사용했고, 2021년부터 현재까지는 Sleeper를 사용하고 있습니다. 그래서 두 플랫폼 모두에서 데이터를 가져와야 했습니다. 오늘 우리가 할 작업은 이 데이터를 JMP로 가져와 분석하는 것입니다. 먼저 API를 사용하여 데이터를 가져와야 합니다. 이 작업은 두 가지 방법으로 진행할 예정인데, 하나는 새로운 HTTP 요청을 사용하는 것이고 다른 하나는 JMP-Python 통합을 사용하는 것입니다.
다음으로 이러한 요청들을 JMP 클래스 또는 JMP 클래스 객체로 구성하는 방법을 보여드리겠습니다. JMP에 데이터가 준비되면, 이를 JMP 데이터 테이블로 구성해야 합니다. 이를 위한 몇 가지 방법이 있는데, JMP에 내장된 함수인 JSON을 데이터 테이블로 변환하는 방법과 연관 배열을 사용하는 방법, 그리고 JSL을 사용하여 JMP 데이터 테이블을 생성하는 방법을 간략하게 설명하겠습니다.
JMP 데이터 테이블에 데이터가 입력되면 탐색 및 분석을 진행합니다. 먼저 데이터 테이블을 조작해 보겠습니다. 워크플로 빌더를 사용하여 이 과정을 보여드린 후, JMP 플랫폼을 활용하여 시각화 및 분석 작업을 수행하겠습니다.
자, 그럼 API란 무엇일까요? 옆에 API 정의가 나와 있지만, 간단히 말해서 API는 웹 서버에서 데이터를 가져오는 방법입니다. HTTP에는 GET, POST, DELETE, PUT과 같은 몇 가지 메서드가 있지만, 오늘은 가장 간단한 GET 메서드에 집중해 보겠습니다. URL 또는 API 엔드포인트로 GET 요청을 보내면 서버는 JSON 문자열을 반환해 줍니다. 이 JSON 문자열을 사용하여 JMP에서 연관 배열을 만들고 JMP 데이터 테이블도 생성할 것입니다.
`new HTTP request`는 JMP에서 JSL을 사용하여 REST API와 통신할 수 있도록 해주는 함수입니다. 이 함수를 사용하면 앞서 언급했듯이 GET, POST, PUT, DELETE 등의 작업을 수행할 수 있습니다. 오늘은 주로 GET 작업에 초점을 맞춰 `new HTTP request`를 이용해 데이터를 JMP로 가져오는 간단한 방법을 살펴보겠습니다.
먼저 판타지 리그 슬리퍼(Fantasy League Sleeper)에서 데이터를 가져오는 방법을 보여드리겠습니다. 구글에서 슬리퍼 API를 검색해 보시면, 어떤 HTTP 요청 엔드포인트를 사용해야 하는지, 어떤 유형의 데이터를 반환받을 수 있는지 자세히 설명하는 훌륭한 문서를 찾으실 수 있을 겁니다.
사실 API를 사용할 때 가장 이상적인 시나리오는 제공업체가 매우 명확한 API와 반환되는 데이터 유형, 그리고 매우 명확한 문서를 제공하는 것입니다. 보시다시피, 문서에는 리그 데이터와 로스터 데이터를 JMP로 가져오는 방법이 간략하게 설명되어 있습니다. 대부분의 경우 URL에 적용해야 하는 정보는 리그 ID뿐인 것 같습니다.
이 예시에서 보시는 것처럼, JMP에서 이 과정이 어떻게 진행될지 개략적으로 보여드리겠습니다. 먼저 리그 ID를 입력하고 이를 URL 변수로 정의합니다. 그런 다음 `new HTTP request` 메서드를 사용하여 해당 URL을 전달하고 요청을 보냅니다. 요청을 보낸 후에는 JMP로 반환된 JSON 데이터를 파싱합니다.
이제 이러한 요청이 어떤 형태인지 살펴보았으니, JMP에서 어떻게 동작하는지 확인해 보겠습니다. 여기 JMP 스크립트 창이 열려 있습니다. 정의된 URL을 보면 리그 ID와 슬라이드에서 보셨던 요청이 포함되어 있습니다. 이 코드를 실행하면 JSON 문자열이 반환됩니다. JMP에서 이 데이터를 더 효율적으로 사용하려면 JMP 연관 배열로 변환해야 하는데, 이것이 바로 이 Parsed JSON 함수의 목적입니다.
보시다시피 연관 배열이 할당됩니다. 이 연관 배열을 살펴보고 리그 이름을 가져오기 위해 인덱싱하면 'league-certified fantasy football'이라는 리그 이름이 반환되는 것을 볼 수 있습니다. 이제 간단한 데모를 보여드렸으니, HTTP 요청을 구성하기 위해 쿼리를 JMP 클래스로 구성하는 방법을 보여드리겠습니다. 이 창을 닫고 프로젝트 파일을 열어 보겠습니다. 이 프로젝트 파일을 보면 쿼리를 구성하기 위해 클래스가 정의되어 있는 것을 확인할 수 있습니다.
리그 클래스가 있습니다. 여기 참조에서 볼 수 있듯이, 이 클래스는 제가 개요를 가져온 Python API에서 가져온 것이며, JMP Python 통합에 대해 이야기할 때 좀 더 자세히 살펴보겠습니다. 이 클래스를 살펴보면 리그 사용자, 선수, 드래프트, 통계 관련 클래스가 있는 것을 알 수 있습니다. 백엔드의 이러한 모든 클래스는 상위 클래스 기반 API를 호출하며, 이 API가 백그라운드에서 새로운 HTTP 요청을 생성하는 역할을 합니다.
이 클래스를 사용하여 데이터를 가져오는 방법을 살펴보겠습니다. 클래스 정의를 실행한 다음 아래로 스크롤하여 예제를 몇 가지 보여드리겠습니다. 스크롤 때문에 어지러우시더라도 양해 부탁드립니다. 이제 리그 객체를 정의하는 것을 보실 수 있습니다. 다시 한번 리그 ID를 제공했습니다. 여기에서 클래스 정의를 볼 수 있으며, 이제 해당 객체에 정의된 메서드를 사용하여 리그의 선수 명단, 사용자 등을 가져올 수 있습니다. 이 모든 데이터는 연관 배열로 반환됩니다.
지금은 그 안에 정확히 어떤 내용이 있는지 살펴보지는 않겠지만, 반환되는 데이터를 보여드리겠습니다. 이제 사용자, 플레이어, 드레이프에 대해 동일한 방식으로 데이터를 가져올 수 있습니다. 이 모든 내용을 슬라이드와 함께 프레젠테이션 자료로 JMP 커뮤니티에 공유할 예정입니다. 나중에 보여드릴 자료에서 이러한 클래스를 사용하여 데이터를 JMP로 가져올 수 있을 것입니다.
Sleeper의 경우, 데이터를 얻는 것이 매우 쉬웠습니다. HTTP 요청을 생성하는 데 사용할 수 있는 잘 정의된 API가 있었습니다. 하지만 ESPN은 그렇지 않은 것 같습니다. ESPN API 또는 ESPN 판타지 API를 검색해 보면, 누군가가 만든 파이썬 클래스만 나올 뿐입니다. ESPN API에 대한 공개 문서는 없는 것 같습니다.
그럼 이제 어떻게 해야 할까요? 바로 지금이 JMP 18의 새로운 Python 통합 기능에 대해 이야기하기에 완벽한 시점입니다. JMP 18에서는 Python이 JMP와 함께 설치됩니다. 따라서 최소한의 설정만으로 Python 패키지를 활용할 수 있습니다. 이전에 Sleeper에서 보여드렸던 클래스나 Python 클래스에서 영감을 받은 클래스들을 예로 들면, 클래스와 메서드 정의를 직접 작성하는 대신, 여기 보이는 스크립트 한 줄을 사용하여 Python 패키지를 설치하고 Python과 상호 작용하여 데이터를 JMP로 가져올 수 있습니다.
ESPN에서 이미 이러한 기능이 제공되므로 우리도 ESPN에 동일한 작업을 수행할 수 있습니다. ESPN에 대한 제대로 된 문서가 없기 때문에 누군가가 이미 작성한 파이썬 API를 활용할 수 있습니다. 파이썬 통합의 장점은 무엇일까요? 모든 어려운 작업이 이미 완료되어 있다는 것입니다. API 호출 로직이나 원하는 기능을 위한 데이터 조작 코드를 직접 작성할 필요가 없습니다. 다른 사람들이 파이썬으로 작성한 함수와 문서를 활용하기만 하면 됩니다.
파이썬 함수를 호출하고 해당 데이터를 JMP로 다시 전송하기만 하면 됩니다. 간단한 예시를 보여드리겠습니다. 화면에 모두 담으려고 하다 보니 잘 안 보일 수도 있습니다. 이 슬라이드는 나중에 참고하실 수 있도록 제공되겠지만, 함수 정의의 크기만 봐도 쉽게 이해하실 수 있을 겁니다. 직접 스코어보드 함수를 찾는 것은 파이썬 통합을 통해 이미 누군가가 파이썬으로 정의해 놓은 `get_scoreboard` 함수를 호출하는 것보다 훨씬 복잡합니다.
이제 ESP의 슬리퍼 클래스와 파이썬 통합이 어떻게 구현되는지 살펴보겠습니다. 이것으로 마무리하고, 파이썬 통합을 적용한 슬리퍼 클래스를 다시 한번 살펴보겠습니다. 리그 클래스를 보면 기존 파이썬 API와 상호작용하기 위해 일련의 파이썬 submit 및 get 문을 사용하고 있음을 알 수 있습니다.
리그 정보를 얻으려면 파이썬 submit 문을 실행하여 리그 정보 변수를 정의하면 됩니다. 그런 다음 해당 리그 정보 변수를 사용하여 get_league 함수로 변수에 대한 정보를 가져오고, 파이썬 get 함수를 사용하여 해당 변수를 JMP로 다시 전달합니다.
이 모든 기능은 이전에 사용했던 JSL 구현의 새로운 HTTP 요청과 완전히 동일하게 작동합니다. 다만 HTTP 요청이 추상화되어 이제 파이썬 레벨에서 처리되므로 해당 클래스가 더 이상 필요하지 않습니다. 덕분에 클래스의 메서드 정의가 훨씬 간소화되었습니다. 이제 여러분이 가장 잘 알고 계실 ESPN 플랫폼을 살펴보겠습니다. ESPN에서도 동일하거나 유사한 클래스를 확인할 수 있습니다.
파이썬 API는 객체 지향적인 방식으로 설계되어 있어 리그는 팀 객체, 선수 객체 등을 반환합니다. 그리고 이러한 객체들을 사용하여 속성에 접근하는 메서드를 활용할 수 있습니다. 이제 클래스 정의를 실행하여 ESPN에서 데이터를 가져오는 방법과 이러한 클래스 객체와 상호 작용하는 몇 가지 예시를 살펴보겠습니다.
여기서 제 개인 리그 정보를 가져오는 것을 보실 수 있습니다. 보시다시피 해당 객체가 정의되어 있습니다. 리그 객체의 `getTeams` 메서드를 사용하여 팀 정보를 가져올 것입니다. 이제 연관 배열이 정의되었습니다. `forEach`를 사용하여 이 연관 배열을 순회하면서 모든 팀 이름을 출력해 보겠습니다. 보시다시피, 이것이 제 팀 이름입니다. 제 팀에 접근하려면 `teams` 변수에 인덱싱하여 제 팀 정보를 가져오면 됩니다. 이제 해당 팀의 `getWins` 메서드를 실행하면 이번 시즌 제 팀의 승리 횟수를 확인할 수 있습니다. 7승으로, 그다지 좋은 성적은 아닙니다.
자, 만약 제가 그 팀의 선수 명단을 보고 싶다면, `get roster` 함수를 사용해서 명단을 분석하고 선수 이름을 반환받으면 됩니다. 보시다시피, 저는 그 시즌에 제가 등록했던 모든 선수들을 볼 수 있습니다. 앞서 말씀드렸듯이, 이 클래스들은 여러분이 사용할 수 있도록 준비되어 있습니다. 제가 꽤 꼼꼼하게 주석을 달아 놓았으니, 여러분도 이 클래스들을 이용해서 자신만의 팀 정보를 쉽게 찾아볼 수 있을 겁니다.
이제 JMP에 데이터를 입력했으니 분석을 위해 JMP 데이터 테이블로 변환해야 합니다. 몇 가지 방법이 있는데, 앞서 언급했듯이 API에서 데이터를 호출하면 일반적으로 JSON 문자열 형태로 반환됩니다. JMP에는 JSON을 데이터 테이블로 변환하는 내장 함수가 있으며, 이 예제에서 볼 수 있듯이 이 함수를 사용하여 JSON 문자열에서 데이터 테이블을 생성할 수 있습니다.
이게 아마 가장 간단한 방법일 겁니다. JMP에는 JSON 가져오기 마법사가 있어서 가져올 열과 테이블에서 가져올 특정 데이터를 사용자 지정할 수 있습니다. 또 다른 방법은, ESPN 데이터처럼 객체 지향적인 구조를 가진 데이터를 다룰 때 더 유용할 수 있는데, JSL을 사용해서 테이블을 직접 정의한 다음 객체 메서드를 이용해 해당 열들을 채우는 것입니다. 아, 죄송합니다. 채우는 게 아니라, 채우는 겁니다.
보시는 것처럼, 여기서는 팀 객체를 순회하면서 리그에 속한 팀들을 가져오고 있습니다. 팀 객체를 순회하면서 특정 열에 필요한 데이터를 정의하려면, 필요한 메서드를 사용하면 됩니다. 예를 들어 승리 횟수 열에는 `getwins`, 패배 횟수 열에는 `getloses` 등을 사용하면 됩니다. 만약 중첩된 객체가 있다면, 해당 객체들을 순회하면서 특정 열에 사용할 선수 ID 목록을 가져올 수도 있습니다.
각 표현식에 대해 이를 확인할 수 있습니다. 이제 JMP에서 이러한 데이터 테이블을 어떻게 가져오는지 보여드리겠습니다. 슬리퍼 데이터와 ESPN 데이터를 가져오는 데 각각 별도의 파일을 사용했습니다. 슬리퍼 데이터의 경우, 데이터를 가져오는 데 사용하는 메서드인 클래스를 임포트하고 있습니다. 빠르게 스크롤하면서 앞서 설명한 JSON to data table 메서드를 강조 표시하겠습니다. 여기에서 이 메서드를 사용하여 열을 정의하고 리그 팀에 대한 데이터 테이블을 생성하는 것을 볼 수 있습니다.
다시 한번 말씀드리지만, 이 파일들을 여러분과 공유하겠습니다. 슬리퍼 리그 데이터가 있는 경우, 리그가 존재해 온 모든 연도의 데이터를 얻으려면 바로 이 마지막 줄을 실행하기만 하면 됩니다. 이 줄은 이 파일에 있는 앞서 정의된 모든 함수를 활용하며, 이 함수들은 나중에 직접 살펴보실 수 있습니다. ESPN의 경우에도 비슷한 방식이 있습니다. 슬라이드에서 강조 표시된 것처럼, 여기서는 테이블을 생성하는 방식이 조금 다릅니다. JSL을 사용하여 테이블을 정의한 다음 객체 메서드를 사용하여 해당 테이블을 채웁니다.
다시 말씀드리지만, ESPN 데이터를 모두 얻으려면 마지막 명령어 하나만 실행하면 될 것입니다. JMP에서 이 데이터가 어떻게 보이는지 살펴보겠습니다. 해당 파일을 실행하면 리그 이름으로 된 디렉토리가 생성됩니다. 리그의 각 연도별로 드래프트 테이블, 멤버 테이블, 팀 테이블, 그리고 리그에 참여한 각 사용자의 개별 로스터가 포함된 테이블들이 있을 것입니다.
자, 한번 살펴보죠. 보시다시피 팀 목록이 있습니다. 열을 좀 더 넓혀볼게요. 잘 보이시죠? 각 팀에는 사용자 이름이 표시됩니다. 더 잘 보이도록 크기를 조금 줄여볼게요. 각 팀별로 리그 구성원과 승패 기록이 있습니다. 선수 열을 클릭하면 특정 팀의 선수 목록을 볼 수 있습니다.
드래프트와 드래프트된 선수들을 라운드 및 픽 번호별로 정리한 데이터 테이블을 만들 예정입니다. 사용자 이름을 클릭하면 해당 선수가 어떤 라운드에서 지명되었는지 확인할 수 있으며, 리그 회원 목록도 제공합니다. 이러한 테이블을 통해 어떤 분석을 할 수 있을지 상상해 보세요.
본격적인 분석에 앞서, 앞서 언급했던 것처럼 이번 분석의 동기를 설명드리겠습니다. 2022년에는 제가 속한 리그에서 1위를 차지했지만, 안타깝게도 2023년에는 꼴찌를 했습니다. 여기서 예측 가능성과 결과의 흐름을 살펴보려고 합니다. 왼쪽 산점도를 보시면, 2022년에는 최고의 타이트엔드와 최고의 쿼터백을 보유했던 반면, 꼴찌를 했던 리그에서는 이 두 포지션 모두 상위 5위 안에 들지 못했던 것을 알 수 있습니다.
하지만 저는 와이드 리시버 포지션이 더 좋았기 때문에 이 두 그래프만으로는 어느 해에 더 성공적이었는지 정확히 알 수 없어서, 이번 분석의 계기가 되었습니다. 오늘은 무엇을 살펴볼까요? 먼저 판타지 리그는 드래프트로 시작하는데, 드래프트 순서마다 유리한 부분이 있습니다.
다음으로 살펴볼 것은 지난 10년간 판타지 풋볼에서 어떤 트렌드와 변화가 있었는지입니다. 어떤 드래프트 전략이 우승 확률을 가장 높여줄까요? 보수적인 접근 방식과 위험을 감수하는 접근 방식 중 어떤 것이 좋을까요? 맨 아래에는 제가 운영하는 리그 구성이 있는데, 꽤 일반적인 구성입니다. 쿼터백 1명, 오른쪽 리시버 2명, 러닝백 2명, 플렉스 자리 2개, 키커 1명, 그리고 하프 PPR 스코어링을 적용한 수비진입니다. 이 정도면 상당히 표준적인 리그 구성이라고 생각합니다.
저희 분석에서 얻은 결과가 여러분의 리그에도 도움이 되기를 바랍니다. 드래프트 순위가 성공에 영향을 미칠까요? 앞서 보여드린 데이터 테이블을 활용하여 분석을 진행해 보겠습니다. 이를 위해 워크플로 빌더를 사용하여 테이블을 정리하고 분석을 수행하는 단계를 살펴보겠습니다.
여기서 워크플로를 살펴보실 수 있습니다. 초기 설정 단계에서는 리그 이름을 정의하고 워크플로가 데이터를 어디에서 가져올지 지정합니다. 제가 단계별로 설명해 드리겠습니다. 리그의 각 연도별로 다음 단계를 진행합니다. 드래프트 테이블을 열고, 팀 테이블을 엽니다.
이제 드래프트 테이블에서 리그 참가자 수를 기준으로 데이터를 추출하여 특정 참가자의 드래프트 순번을 가져오겠습니다. (죄송하지만, 추출된 데이터는 다음과 같습니다.) 이제 이 데이터 테이블을 팀 테이블과 결합하여 다음과 같은 테이블을 만들겠습니다. 이 테이블에는 해당 참가자(또는 리그 구성원)의 승리 횟수와 드래프트 순번이 모두 표시됩니다. 리그의 모든 연도에 대해 이 작업을 반복하고, 최종적으로 테이블을 연결하여 분석을 진행합니다.
나머지 단계를 여기서 설명드리겠습니다. 일부 표는 다른 화면에 나타나지만, 최종 결과 표와 분석 내용만 보여드리겠습니다. 이 그래프에서 보시다시피, X축에는 선택 번호가, Y축에는 팀의 승리 횟수와 최종 순위가 표시되어 있습니다.
이 그래프를 보면, 드래프트 순위와 최종 성적 사이에 큰 차이나 상관관계는 보이지 않습니다. 저는 여기서 승리 횟수를 보고 있습니다. 드래프트 초반에 지명권을 행사할수록 리그 역사 전체를 통틀어 평균 승리 횟수가 약간 더 높은 경향이 있고, 드래프트 초중반에는 승리 횟수가 다소 줄어드는 것을 볼 수 있습니다. 제가 해석하기로는, 드래프트 순위와 상관없이, 자신이 속한 리그에서 우승할 가능성은 충분히 있는 것 같습니다.
이제 제 리그의 9년 역사 동안 드래프트 전략이 어떻게 변화했는지 살펴보겠습니다. 시간을 절약하기 위해 저희가 사용하는 단계를 보여드리겠습니다. 기본적으로 모든 드래프트 테이블을 열고 연결한 다음 일부 항목을 추출하는 방식입니다. 하지만 시간을 아끼기 위해 워크플로를 실행하고 결과를 보여드리겠습니다.
여기 두 개의 출력 그래프가 있습니다. 좀 더 자세히 살펴보겠습니다. 먼저 이 주요 그래프를 설명하고, 관심 있으시면 옆에 있는 그래프도 함께 보시죠. 이 그래프는 드래프트 순위 변동을 보여주는 분석 자료입니다. 간단히 말해, X축은 연도이고, Y축은 상위 4라운드에서 특정 순위로 지명된 선수 수를 나타냅니다.
이 그래프를 보면 리그 전체를 기준으로 상위 4라운드에서 지명된 쿼터백, 러닝백, 타이트엔드, 와이드 리시버의 수를 알 수 있습니다. 2015년부터 2020년까지 러닝백 지명 수가 증가하다가 최근 3년간은 상당히 감소했습니다. 즉, 상위 4라운드에서 러닝백 지명이 줄어든 것입니다. 리시버 역시 러닝백과 비슷한 추세를 보이며, 지명 빈도가 정체되다가 러닝백보다 약간 높은 수준을 유지하고 있습니다. 쿼터백은 드래프트 초반에 지명되는 빈도가 감소하다가 최근 들어 약간 증가한 것을 볼 수 있습니다.
타이트엔드 포지션은 지난 몇 년간 약간의 증가세를 보인 것을 제외하면 전반적으로 안정적인 흐름을 유지해 왔습니다. 이 그래프에서 보시는 것처럼 라운드별 분석 자료가 있는데, 더 자세히 살펴보고 싶으신 분들을 위해 커뮤니티에 공유해 드리겠습니다.
지금까지 드래프트 전략이 시간이 지남에 따라 어떻게 변화해 왔는지 살펴보았는데요, 이제 드래프트 전략의 변화가 실제로 효과가 있는지 알아보겠습니다. 이 분석에서는 지난 3년간 6라운드까지 각 포지션별로 지명된 선수 수를 살펴보겠습니다. 분석 단계는 여기에서 확인하실 수 있습니다. 먼저 설정 단계를 실행하겠습니다. 각 연도별로 드래프트 테이블과 팀 테이블을 열고, 6라운드까지만 추출합니다. 그런 다음 각 사용자별로 포지션별 지명 선수 수를 보여주는 요약 테이블을 만듭니다. 이 과정을 매년 반복한 후, 모든 데이터를 합쳐서 분석을 진행합니다.
먼저 이 그래프 빌더 플롯을 살펴보겠습니다. 첫 6라운드에서 지명된 러닝백의 수는 승리 횟수와 약간의 음의 상관관계를 보이는 것을 알 수 있습니다. 이는 지난 분석에서 러닝백 지명 횟수가 감소하는 추세를 보였던 것과 일맥상통합니다. 와이드 리시버의 경우 첫 6라운드에서 2명이 지명되면서 정점을 찍은 후, 3명으로 다소 감소하는 것을 볼 수 있습니다.
여기서 가장 가파른 기울기를 보이는 포지션은 타이트 엔드입니다. 잠시 후에 다시 살펴보겠지만, 이는 드래프트 첫 6라운드에서 타이트 엔드를 선택하는 것이 시즌 승리 횟수와 상당히 긍정적인 상관관계를 보인다는 것을 의미합니다. 쿼터백도 마찬가지이지만, 기울기는 조금 더 완만합니다. 이제 적합 모델을 사용하여 좀 더 통계적인 방식으로 살펴보겠습니다. 여기에서 타이트 엔드의 경우 매우 가파른 기울기를 볼 수 있습니다. 왼쪽 하단에 있는 매개변수 추정치를 살펴보세요.
통계적으로 유의미한 결과를 보면, 드래프트 첫 6라운드에서 타이트엔드를 선택하는 것과 시즌 승수 사이에 상당히 강한 양의 상관관계가 있음을 알 수 있습니다. 이 분석에서 가장 중요한 점은 러닝백은 적게 선택해야 하지만, 첫 6라운드에서 타이트엔드를 선택하는 것이 시즌 승수를 예측하는 데 상당히 높은 상관관계를 보인다는 것입니다.
이제 저는 상위권 선수들의 활약이 매년 예측 가능한지 살펴보고 싶습니다. 더 구체적으로 말하자면, 포지션별 판타지 포인트 상위 12명 중 매년 동일한 선수의 비율을 확인하고, 이를 통해 드래프트에서 어떤 선수에 집중해야 할지 통찰력을 얻을 수 있는지 알아보고자 합니다.
다시 한번, 시간을 조금 절약하기 위해 워크플로를 간단히 설명드리겠습니다. 실행되는 동안 측면에 단계가 표시됩니다. 실행하는 데 약간의 시간이 소요됩니다. 분석에 워크플로를 사용하는 이점에 대해 말씀드리겠습니다. 워크플로를 사용하면 분석의 여러 단계를 반복적으로 수행하면서 문제 영역을 파악할 수 있습니다.
또한 동료들이 여러분이 작업하는 데이터에 쉽게 접근할 수 있다면, 여러분의 분석과 그 과정을 매우 쉽게 재현할 수 있습니다. 여기에서 이 분석 결과로 나온 그래프와 데이터를 보여드리겠습니다. 옆쪽 데이터를 보시면, 각 순위별 상위 12명 중 전년도와 동일한 비율이 매년 어떻게 변하는지 알 수 있습니다.
이 그래프를 보면, 파란색 선으로 표시된 러닝백의 수가 지난 몇 년간 감소하는 추세를 보이고 있습니다. 이는 러닝백의 연간 성적을 예측하기가 전년도에 비해 다소 어려워지고 있음을 의미합니다. 반면 와이드 리시버와 타이트 엔드는 예측 가능성이 높아졌습니다. 여기서 주목할 점은, 예를 들어 2023년에 활약했던 상위 12명의 러닝백 중 지난 2년간 캐리 횟수가 33%에 불과했다는 것입니다. 따라서 러닝백 포지션은 와이드 리시버나 타이트 엔드 포지션보다 예측하기가 조금 더 어려울 수 있습니다.
자, 이제 마지막 분석입니다. 드래프트에서 어떤 선수를 뽑아야 할지 살펴보겠습니다. 모든 트렌드와 패턴을 분석하고, 어떤 드래프트 전략을 세워야 할까요? 이번 분석에서는 드래프트 가치와 드래프트 순위에 비해 기대 이상의 활약을 보여주는 선수들이 팀의 성공에 미치는 영향에 대해 알아보겠습니다. 이 분석은 꽤 흥미로운 부분이라 좀 더 자세히 살펴보겠습니다.
설정 단계를 마친 후, 다시 한 번 매년 반복 작업을 진행할 것입니다. 먼저 드래프트 테이블과 팀 테이블을 엽니다. 다음 두 단계는 단순한 정리 작업이므로 무시하셔도 됩니다. 이제 드래프트 테이블에서 이변량 분석을 실행하여 특정 선수의 드래프트 순위와 총 득점 간의 관계를 포지션별로 살펴보겠습니다.
변수 분석을 통해 살펴보면, 각 포지션별로 X축에는 드래프트 순위, R축에는 해당 선수가 획득한 총 득점이 표시됩니다. 저는 특히 회귀 분석에서 예측한 해당 포지션의 예상값보다 높거나 낮은 지점에 주목하고 있습니다.
예측된 선형 회귀선보다 높거나 낮은 성적이 승리 가능성에 어떤 영향을 미치는지 살펴보고 싶습니다. 나머지 분석 과정은 바로 이 부분을 다룰 것입니다. 보시는 것처럼, 이러한 회귀 값과 잔차를 그래프에서 추출하여 초안 표에 저장할 것입니다. 그런 다음 라운드를 초반, 중반, 후반으로 그룹화합니다. 여기서 라운드 열을 빠르게 재코딩해 보겠습니다.
이제 초반, 중반, 후반으로 나누어 분석했습니다. 각 라운드별로 기대 이상의 성적을 낸 선수와 그렇지 못한 선수가 승리에 어떤 영향을 미치는지 확인할 수 있습니다. 나머지 분석 과정을 진행하겠습니다. 잠시만 기다려 주세요. 지연에 대해 사과드립니다. 거의 다 왔습니다. 이제 모든 연도의 데이터가 통합된 데이터 테이블을 볼 수 있습니다. 각 사용자별로 특정 연도 동안 라운드별로 극단적인 기대 이상의 성적을 낸 선수의 수를 확인할 수 있으며, 이 데이터는 모든 연도에 걸쳐 있습니다. 이 분석의 목적은 드래프트된 특정 라운드에서 극단적인 기대 이상의 성적을 낸 선수 또는 그렇지 못한 선수가 승리 확률에 어떤 영향을 미치는지 알아보는 것입니다.
여기 적합 모델 플롯을 살펴보겠습니다. 제 생각에 이것이 오늘 가장 흥미로운 분석입니다. 레버리지 플롯을 살펴보면서 특히 중반 라운드에서 극단적으로 뛰어난 성적을 낸 선수들과 초반 라운드에서 극단적으로 부진한 성적을 낸 선수들에 주목해 보겠습니다. 여기 매개변수 추정치를 보면 통계적으로 유의미하게 중반 라운드에서 뛰어난 성적을 낸 선수가 많을수록 시즌 승리 횟수와 양의 상관관계를 보인다는 것을 알 수 있습니다. 즉, 중반 라운드에서 뛰어난 성적을 낸 선수가 많을수록 판타지 리그에서 더 많은 승리를 거둘 가능성이 높아집니다.
반대로, 초반 라운드에서 기대 이하의 성적을 내는 선수를 뽑는 것은 시즌 예상 승리 횟수에 매우 부정적인 영향을 미칩니다. 여기서 얻을 수 있는 교훈은 중반 라운드에서는 과감하게 높은 목표를 세우고 선수를 선택해야 한다는 것입니다. 예를 들어, 저는 홈런을 칠 수 있는 타자를 뽑고, 실패하더라도 크게 걱정하지 않으려고 합니다. 보시다시피, 중반 라운드에서 기대 이하의 성적을 내는 선수를 뽑더라도 그 손실이 생각만큼 크지 않은 것 같습니다.
리그 우승 가능성을 높여줄 선수들을 영입하는 데 집중하는 것이 좋습니다. 특히 그런 선수들을 놓치더라도 큰 손해를 보지 않으니까요. 하지만 반대로 드래프트 초반에 부상 위험이 높은 선수를 뽑는 것은 판타지 풋볼 성적에 매우 큰 타격을 줄 수 있습니다.
분석 내용을 끝까지 함께해 주셔서 감사합니다. 몇 가지 핵심 사항을 말씀드리자면, 작년과 올해는 절대 반복되지 않습니다. 저희 분석에서도 확인했듯이, 특히 러닝백의 경우 한 시즌 최고의 판타지 포인트 생산자는 매 시즌 예측하기가 매우 어렵습니다.
일반적인 리그 형식에서 러닝백은 가치가 떨어지고 예측하기 어려워지는 반면, 와이드 리시버는 가치가 조금 더 높아지고 러닝백보다 훨씬 예측하기 쉬워졌습니다. 쿼터백과 특히 타이트엔드 포지션을 더 이상 간과할 수 없습니다. 드래프트 초반에 타이트엔드를 뽑는 것이 리그 성공으로 이어지는 경향이 있다는 것을 우리는 이미 확인했습니다. 물론 드래프트 초반에 뽑는다고 해서 리그 우승이 보장되는 것은 아니지만, 리그를 망칠 수도 있습니다. 리그의 승패는 중반 라운드에서 결정됩니다.
앞서 설명드린 이유들 때문에 잠재력이 높은 선수에게 과감하게 투자하세요. 이제 여러분 차례입니다. 제가 JMP에 Sleeper와 ESPN 데이터를 가져오는 데 사용하는 클래스와 API를 제공해 드렸습니다. 이 API를 활용하여 JMP 커뮤니티에 여러분의 분석 결과를 공유해 보세요. 만약 여러분의 판타지 리그가 Sleeper나 ESPN을 사용하지 않는다면, 파이썬 통합 개요를 참고하여 원하는 파이썬 저장소를 활용해 JMP에 데이터를 가져오고 분석을 진행할 수 있습니다.
오늘 시간 내주셔서 정말 감사합니다. 여러분들이 어떤 분석을 내놓으실지 정말 기대되고, 여러분들의 도움으로 다음 판타지 풋볼 리그에서 우승할 수 있기를 바랍니다. 감사합니다.