bskyvision RSS 태그 관리 글쓰기 방명록
2021-03-11 19:07:04
728x90

지난 시간에 저희는 맥크라켄이 고안한 BABIP이란 스탯에 대해서 살펴봤습니다. BABIP은 인플레이 타구가 안타가 될 확률을 뜻합니다. 맥크라켄의 주장은 간단히 말해서 타자가 친 공이 인플레이 타구가 되었을 때, 투수는 그 타구가 아웃이 되게 할 수 있는 능력이 거의 없다는 것이었습니다. 투수의 역량보다는 수비력과 운에 의해 안타가 될지, 아웃이 될지 결정된다는 것이었습니다. 즉, A급 투수나 C급 투수나 BABIP은 비슷하다는 것이 그의 주장이었습니다. 그는 기존의 ERA(평균자책점)나 피안타율과 같은 전통적인 지표는 투수의 실력을 충분히 반영해내지 못한다는 결론을 이끌어냈습니다. 

 

DIPS

그래서 맥크라켄이 2001년에 새롭게 만든 투수 평가 지표가 DIPS입니다. DIPS는 defense independent pitching statistics의 축약어로, 수비 독립적 투구 통계 이렇게 번역이 가능할 것 같습니다. 즉, 수비적 요소를 제거하고 투수의 실력만을 측정할 수 있는 투수 스탯을 만들자는 것이 그의 생각이었습니다. 

 

지난 시간에 살펴봤지만, 인플레이 타구(BIP)의 결과는 투수가 통제하기 힘들다고 말씀드렸습니다. 그래서 맥크라켄은 인플레이가 아닌 타구(BNIP)의 결과만 가지고 투수의 능력을 평가했습니다. BIP와 BNIP가 무엇을 의미하는지 잘 모르겠다면, 이전 글을 참고해주세요. BNIP 중에는 삼진, 사사구, 홈런이 있습니다. 이 세가지 기록만을 가지고 투수를 평가하는 것이 바로 DIPS입니다. 

 

FIP

그런데 맥크라켄은 DIPS를 간단한 공식으로 만들어내지 못했습니다. 계산 절차가 너무 복잡합니다. 그러다보니 사람들이 사용하기가 어려웠고, 이후에 톰 탱고라는 사람이 2006년에 DIPS의 정신을 계승하여 FIP라는 스탯을 발표합니다. FIP는 fielding independent pitching stats의 약자입니다. DIPS와 거의 비슷한 뜻이라고 보시면 됩니다. FIP의 공식은 다음과 같습니다. 

 

FIP = (13*홈런 + 3*볼넷 + 3*몸에맞는볼 -2*삼진)/이닝 + C

 

이 공식을 함께 들여다 보겠습니다. 홈런과 볼넷, 몸에 맞는 공을 많이 내주는 투수는 좋은 투수라고 말하기 힘듭니다. 특히 홈런은 더 치명적이죠. 그래서 톰 탱고는 홈런에는 13의 가중치를, 볼넷과 몸에 맞는 볼에는 각각 3의 가중치를 부여했습니다. 삼진을 많이 잡아낼 수 있다는 것은 좋은 투수의 요건이므로 -2의 가중치를 줬습니다. 이런 가중치들을 부여해서 가중합을 해준 것을 이닝수로 나눕니다. 그 다음에 FIP 상수, C를 더합니다. 상수 C의 역할에는 두가지가 있는데, 첫번째는 FIP가 ERA와 비슷한 범위의 값을 갖게 하는 것이고, 두번째는 타고투저, 타저투고 등의 시즌 요소를 반영해주는 것입니다. FIP 상수는 다음과 같이 구합니다. 

 

C = 리그ERA - (13*리그홈런 + 3*리그볼넷 + 3*리그몸에맞는볼 - 2*삼진)/리그이닝

 

ERA vs FIP

그러면 연속한 두 시즌 ERA 사이의 상관관계와 연속한 두 시즌 FIP 사이의 상관관계 중 어떤 것이 더 강한지 살펴보겠습니다. 수비력과 운의 요소를 제거하려고 노력한 FIP가 ERA보다 더 강한 자기상관(autocorrelation)을 갖는다면 맥크라켄의 주장은 일리가 있는 것입니다. 저는 MLB 데이터에서 1990년부터 2019년까지의 투수 기록 중에 한 시즌에 250 타자 이상을 상대한 투수의 기록을 대상으로 분석을 실시했습니다. 너무 적은 타자만을 상대한 시즌의 결과를 포함시키면 의미있는 결과를 얻기 어렵기 때문입니다. 

 

 

 

보시다시피 FIP의 자기상관(0.5024)이 ERA의 자기상관(0.3443)보다 강했습니다. 상관계수가 약 0.15 차이가 납니다. 적지 않은 차이입니다. 즉, 투수의 FIP가 ERA에 비해 시즌간 변동이 적다는 것을 의미합니다. 따라서 투수의 미래 성적에 대해 좀 더 나은 예측력을 갖는 것이 FIP입니다. 해당 시즌의 성적을 평가하기에는 ERA가 더 나을 수 있으나, 다음 시즌의 성적을 예측하기에는 FIP가 더 낫다고 볼 수 있습니다. ERA보다 FIP가 좀 더 운의 영향을 제거한 스탯이기 때문입니다. 

 

저는 위 결과를 얻기 위해 다음과 같은 파이썬 코드를 작성했습니다. MLB 데이터를 취급하기 위해 레먼 데이터베이스를 사용했습니다. 

 

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import sqlite3
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
from matplotlib import font_manager, rc
 
# 한글 폰트 사용을 위해서 세팅
font_path = "C:/Windows/Fonts/ngulim.ttf"
font = font_manager.FontProperties(fname=font_path).get_name()
rc('font', family=font)
 
with sqlite3.connect("lahmansbaseballdb.sqlite"as con:
    cur = con.cursor()
    cur.execute('''
            SELECT playerID, yearID, 
            ERA,
            round( (13*HR + 3*BB - 2*SO + 0.00) / ((IPOuts + 0.00)/3), 3) AS fip_left 
            FROM pitching WHERE yearID >= 1990 and BFP >= 250 ORDER BY playerID;
            ''')
    data1 = cur.fetchall()
 
    cur.execute('''
                SELECT yearID, (lgER*9 + 0.00)/lgIP - (13*lgHR + 3*lgBB - 2*lgSO)/lgIP as fip_C FROM (
                SELECT yearID, sum(HR) as lgHR, sum(BB) as lgBB, sum(SO) as lgSO, sum(ER) as lgER, sum((IPOuts + 0.00)/3) as lgIP
                FROM pitching WHERE yearID >= 1990 GROUP BY yearID);
                ''')
    data2 = cur.fetchall()
 
    FIP_C = np.zeros((len(data2), 2))
 
    for i in range(len(data2)):
        FIP_C[i][0= data2[i][0]
        FIP_C[i][1= data2[i][1]
 
    player = []
    year = np.zeros((len(data1)))
    ERA = np.zeros((len(data1)))
    FIP = np.zeros((len(data1)))
 
    for i in range(len(data1)):
        player.append(data1[i][0])
        year[i] = data1[i][1]
        ERA[i] = data1[i][2]
        FIP[i] = data1[i][3+ data2[np.where(FIP_C==year[i])[0][0]][1]
 
    beforeERA = []
    afterERA = []
 
    for i in range(len(data1) - 1):
        if data1[i + 1][0== data1[i][0]:
            if data1[i + 1][1== data1[i][1+ 1:
                beforeERA.append(ERA[i])
                afterERA.append(ERA[i + 1])
 
    beforeFIP = []
    afterFIP = []
 
    for i in range(len(data1) - 1):
        if data1[i + 1][0== data1[i][0]:
            if data1[i + 1][1== data1[i][1+ 1:
                beforeFIP.append(FIP[i])
                afterFIP.append(FIP[i + 1])
 
    plcc1 = round(stats.pearsonr(beforeERA, afterERA)[0], 4)
    plcc2 = round(stats.pearsonr(beforeFIP, afterFIP)[0], 4)
 
    plt.scatter(beforeERA, afterERA, c='b', s=10, alpha=0.3)
    plt.title('연속된 두 시즌 동안 기록한 ERA 사이의 상관관계')
    plt.xlabel('특정 시즌 ERA')
    plt.ylabel('그 다음 시즌 ERA')
    plt.text(62"상관계수: " + str(plcc1), fontsize=20, color='red')
    plt.grid(True)
    plt.show()
 
    plt.scatter(beforeFIP, afterFIP, c='b', s=10, alpha=0.3)
    plt.title('연속된 두 시즌 동안 기록한 FIP 사이의 상관관계')
    plt.xlabel('특정 시즌 FIP')
    plt.ylabel('그 다음 시즌 FIP')
    plt.text(41"상관계수: " + str(plcc2), fontsize=20, color='red')
    plt.grid(True)
    plt.show()
cs

 

bskyvision의 추천글

[세이버메트릭스] 인플레이 타구가 안타가 될 확률, BABIP

[세이버메트릭스] 타석에서 나올 수 있는 결과 중 타자와 투수의 실력의 영향을 많이 받는 것은? 반면 운에 영향을 많이 받는 것은?

 

참고자료

[1] namu.wiki/w/DIPS, 나무위키, "DIPS"

[2] library.fangraphs.com/pitching/fip/, 팬그래프, "FIP"

댓글

방문해주신 모든 분들을 환영합니다.

* 글을 읽던 중에 궁금했던 부분은 질문해주세요.

* 칭찬, 지적, 의문, 격려, 감사표현 등을 남겨주세요.

* 최대한 답변 드리도록 노력하겠습니다.

* 욕설과 광고를 담은 댓글은 가차없이 삭제합니다.


guest@이름 ~$
guest@패스워드 ~$
guest@홈페이지주소작성 ~$

guest@댓글작성 ~$




bskyvision. Designed by bskyvision.