#2-4 나만의 레이팅 시스템 만들기(elo rating)

목차

    2017. 8. 11. 18:39




    렛츠런파크의 레이팅 시스템은 전체 말에 대해 점수를 메기는 시스템입니다. 현재 마사회에서도 레이팅 시스템을 운영하고 있고, 이는 경주성적, 착차, 편성강도 등 여러가지를 고려해서 부여한다고 합니다. 


    레이팅 시스템 중에서 제일 유명한 것은 엘로 레이팅 시스템(ELO rating system)입니다.

    이 시스템은 헝가리 출신인 엘뢰 아르파드 전 미국 마케트대 물리학과 교수가 제안한 시스템으로, 체스 선수들의 레이팅을 부여하기 위해 고안하여 사용되었고, 현재는 컴퓨터 게임의 래더 시스템에서 사용자 실력을 측정할 때 많이 쓰인다고 합니다. 


    이 시스템은 애초에 체스 선수를 대상으로 제안한 시스템이다보니, 1:1 경기에 적용되는 시스템이지만, 여기저기 찾아보면 경마와 같이 다수의 사람들이 겨루는 경기에도 엘로 시스템을 적용한 사례를 찾을 수 있습니다.




    (알페드 엘로(Arpad ELO) - 출처: 위키피디아)



    관련 글 보기

    파이썬을 위해 pycharm(파이참)을 써야만 하는 이유 5가지!

    KNN 군집화 알고리즘 사용하기 (질병 정보 분류하기)

    경마에서 말은 거리에 따라 속력이 다를까 - histogram



    저도 경마 예측에 관한 다수의 글을 찾다가 외국 글에서 이 엘로 레이팅 점수를 변수로 활용한 사례를 보게 되었고, 그것에 참조하여 엘로 레이팅 점수를 메기고자 하였습니다.



    엘로 레이팅 점수를 계산하는 프로세스는 아래와 같습니다. 수식을 계산하는 것이 조금 복잡합니다.


    1. 최초로 경기를 하는 사람1500점이 부여된다.

    2. 이길 경우 점수가 증가하고, 질 경우 점수가 감소한다. 

        (보통은 400점으로 한다고 하는데 경기에 따라 점수는 조정할 수 있습니다.)

    3. 점수 차가 큰 사람을 이길 경우 증가하는 점수의 폭이 크고, 점수차가 작은 사람을 이길 경우 증가하는 점수의 폭이 작다. 지는 경우는 그 반대이다.



    이 레이팅 서로간의 비교를 점수로 하기 때문에, 쉽고 간편하다는 장점이 있습니다. 


    또한 서로 겨뤄보지 못한 말들의 승률을 예측할 수 있습니다. 점수 차이를 통해서 그 확률을 계산할 수 있습니다. 말, 기수, 조교사별로 각각 레이팅 점수를 구하게 되는데 여기서는 말의 레이팅 점수를 구해보도록 하겠습니다.


    기수와 조교사는 동일한 방법으로 코딩하시면 됩니다.



    우선 경주결과 DB를 dataframe으로 불러옵니다.

    1
    2
    3
    4
    5
    con = sqlite3.connect("./data/race_db_bu.db")
    cur = con.cursor()
    query = cur.execute("SELECT * From total_hn_bu")
    cols = [column[0for column in query.description]
    race = pd.DataFrame.from_records(data=query.fetchall(), columns=cols)

    cs


    순위가 없는 경기는 삭제하고, Object타입으로 되어 있는 변수들을 숫자 타입으로 변경해 줍니다.
    1
    2
    3
    4
    race.groupby("순위").size()
    race=race[race["순위"]!=""]
    race["순위"]=race["순위"].astype(float)
    race["date"]=race["date"].astype(int)
    cs


    각 경기마다 값을 구해주어야 하기 때문에, date(날짜), no(경기번호), location(서울/부산) 별로 for문을 돌리려고 합니다.

    해당하는 변수들을 뽑아낸 다음 drop_duplicates문을 이용하여 중복을 제거하고, sorting합니다.

    reset_index 문을 사용하여, index를 0부터 다시 부여합니다.

    1
    2
    3
    4
    date_list=race[["date","no","location"]]
    date_list=date_list.drop_duplicates(keep="first")
    date_list=date_list.sort_values(["date","no","location"])
    date_list=date_list.reset_index(drop=True)
    cs


    계산한 레이팅 값을 저장할 dataframe을 하나 만들어 놓습니다. k=6000이라고 되어 있는 부분은 경기의 승패에 따라 점수를 조정할 때 기준 점수입니다.

    1
    2
    3
    4
    horse_list=pd.DataFrame(columns=["date","horse_no","r"])
    horse_list["r"]=horse_list["r"].astype(float)
     
    k = 6000
    cs


    아까 만든 각 경기 리스트를 이용하여 for문을 돌립니다. 먼저 temp에 해당 경기 리스트에 해당하는 경기들을 저장합니다. 뒤에 참가한 말별로 레이팅을 부여하기 위해 temp의 행갯수를 len_horses에 저장합니다.

    1
    2
    3
    4
    5
    6
    for date_for in range(len(date_list)):
        temp=race[(race["date"]==date_list["date"][date_for]) & /
             (race["no"]==date_list["no"][date_for])  & 
             (race["location"]==date_list["location"][date_for])]
        temp=temp.reset_index(drop=True)
        len_horses=len(temp)
    cs


    계산한 레이팅 값은 horse_list에 저장하는데, 해당 리스트에 지금 계산하려고 하는 말이 있는지 확인합니다.

    1
    2
    3
    4
    for x in range(len_horses):
        if len(horse_list[horse_list["horse_no"]==temp["horse_no"][x]])==0:
            horse_list=pd.concat([horse_list,pd.DataFrame([[temp["date"][x]-1/
            temp["horse_no"][x], 1500]], columns=["date""horse_no""r"])])
    cs


    확인하고 없으면 1500점을 부여하고, horse_list에 넣습니다. 날짜를 입력할 때는 -1을 하고, 넣습니다. 뒤에 예측에 활용할 때, 해당 말의 레이팅 점수 중 가장 최근 날짜를 가져와서 사용합니다.

    그래서 -1을 합니다. 그렇게 하지 않으면, 처음 출전하는 말의 경우 해당 날짜 이전에 레이팅 값이 없는 것으로 나오게 되어 있습니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    new_horse_list=pd.DataFrame(columns=["date","horse_no","r"])
    # x=0
    for x in range(len_horses):
        # 가장 최근의 R을 가져온다.
        horse=horse_list[horse_list["horse_no"]==temp["horse_no"][x]]
        horse=horse.sort_values("date",ascending=False)
        horse=horse.drop_duplicates("horse_no",keep="first")
     
        s=(len_horses - temp["순위"][x]) / (len_horses*(len_horses-1)/2)
     
        e=0
        for y in range(len_horses):
            if x==y:
                pass
            else:
                ri = horse_list[horse_list["horse_no"== temp["horse_no"][y]]
                ri = ri.sort_values(by="date",ascending=False)
                ri = ri.reset_index(drop=True)
                ri = ri.loc[0,"r"]
     
                rx = horse_list[horse_list["horse_no"== temp["horse_no"][x]]
                rx = rx.sort_values(by="date",ascending=False)
                rx = rx.reset_index(drop=True)
                rx = rx.loc[0"r"]
     
                pre_e=1/(1+10**(ri-rx))
                e=e+pre_e
          e= e / (len_horses * (len_horses - 1/ 2)
          new_r = rx + k * (s-e)
          new_horse_list=pd.concat([new_horse_list,pd.DataFrame([[temp["date"][x], /
                           temp["horse_no"][x], new_r]], columns=["date""horse_no""r"])])
    cs


    new_horse_list에 이번 경기를 통해 계산된 레이팅 값을 입력할 계획입니다.

    그 아래 for문은 해당 경기에 참여한 말들수 만큼 반복시켜 각 말의 점수를 계산합니다.

    엘로 레이팅을 구하는 수식은 추후에 추가하도록 하겠습니다. 


    참여한 각각의 말들과 승패 점수를 다 부여해 주어야 하기 때문에, for문을 다시 사용하였습니다.


    최종적으로 계산된 점수인 new_r을 new_horse_list에 날짜와 말번호와 같이 입력합니다.

    1
    2
    3
    horse_list=pd.concat([horse_list,new_horse_list])
    # print(horse_list)
    print(str(date_for)+" is completed!")
    cs


    하나의 경기에 대한 레이팅 점수가 다 계산되면, new_horse_list에 적재됩니다.

    이렇게 적재된 데이터를 이전에 레이팅 점수가 계산되어 있는 horse_list와 합쳐서 새로운 레이팅 점수로 보관합니다.

    (기수와 조교에 대해서는, k(승패에 따른 반영 기본 점수)를 50으로 계산합니다.



    이렇게 계산한 엘로 레이팅 값을 가지고 상위 10마리의 말의 점수를 보도록 하겠습니다.


    순위 

    말 이름 

    엘로 레이팅 점수 

     무후대제

    4057.1점

    아임유어파더 

    4046.4점 

    3

     생일기쁨

    4007.8점 

    4

     트리플나인

    4005.9점 

    5

    소통시대 

    3975.6점 

    6

    샴로커 

    3817.5점 

    7

    강성대국 

    3815.7점 

    8

    밧세바파크 

    3780.1점 

    9

     신조대협

    3770.7점 

    10 

     실버울프

    3764.9점 




    아. 근데 말이름 너무 웃기네요. 아임유어파더, 생일기쁨, 소통시대(ㅋ) .....

    아임유어파더 성적을 한 번 볼까요.

    ( 아임유어파더 성적 )



    헉... 성적이 정말 어마무시하네요. 3등 밑으로 간 적이 없는 극강의 성적을 보여주고 있습니다. 

    레이팅 점수가 잘 부여된 걸로 볼 수 있겠습니다.


    경마에서 말은 거리에 따라서 속력이 다를까요? 실제로 거리차는 얼마 되지 않는데 말입니다. 자세한 내용이 궁금하시면 아래 링크를 클릭해주세요.
    (참조: 경마에서 말은 거리에 따라 속력이 다를까?)