我建立並保持在每個事件後每個玩家的等級的一個獨立的運行列表。這樣你可以在下一個事件中引用它來計算。
首先,加載所有的數據,參數和包。
library(tidyr)
library(dplyr)
crosstab <- read.table(header=T,
text=" a1 a2 a3 a4 a5 h1 h2 h3 h4 h5 a.evt.score h.evt.score
3311 4003 2737 3784 4177 2632 726 633 438 5444 0 1
1696 371 4471 2119 274 1947 5745 3622 438 5444 1 0
1696 371 4471 1199 2230 1947 5745 3622 5034 4166 1 0
3191 4471 2737 274 2230 3598 633 5034 5444 3485 1 0
3191 3685 3486 3784 4177 2632 726 633 438 5444 0 1
127 713 1609 5444 4166 3311 371 4471 1199 2230 1 0
127 713 1609 2345 3485 1696 4003 2737 1199 2230 1 0
127 713 1609 2345 3485 1696 4003 2737 1199 2230 1 0
1947 5745 3622 438 5444 3311 371 4471 3784 4177 1 0
2632 726 633 5444 4166 3191 3685 3486 274 2230 0 1
2632 726 633 438 5444 3191 3685 3486 3784 4177 0 1
5745 3598 5198 4166 3485 1696 4003 2737 274 2230 0 1
2632 726 633 2345 5034 3311 371 4471 3784 4177 1 0
127 3859 726 438 5444 1696 4003 2737 2119 274 1 0
2632 713 633 5034 4166 3191 3685 3486 3784 4177 1 0")
#parameters
k <- 30
seed.rating <- 2200 # default used if a player is not found on ratings table
接下來,執行期望計算的兩個本地幫助函數。
# calculate expected win against an opponent
calcExpect <- function(rating, opp.rating) {
return(1/(1+10^((opp.rating-rating)/400)))
}
# calculate average expectation of a player against all opponents in current event
compileExpect <- function(id) {
rowno <- which(roster$playerid==id)
opp <- roster %>% filter(ah!=roster$ah[rowno])
all.expected <- sapply(opp$rating,
function(x) calcExpect(roster$rating[rowno], x))
return(mean(all.expected))
}
然後設置在每個事件(即評級列表,以及可選結果在每個事件之後)後更新的列表。這裏我們從一個空的評級列表開始,但是如果你有一個現有的評級列表,你可以很容易地從那個數據框開始,作爲列表中的第一個元素。
# start with a blank rating list; can always start with the latest ELO table
ratings <- list(data.frame(playerid=integer(0), rating=numeric(0)))
# optional for logging result for every round, for error checking
rosters <- NULL
現在主要肉:通過整個事件數據,即crosstab
和處理每個事件循環,產生的每個事件後在ratings
(和任選rosters
)一個條目。
你會注意到,在我建立名冊之後,我沒有不同的代碼行來計算「a」或「h」球隊球員的評分或期望值。這應該使代碼更容易適應於超過2個團隊的事件(例如聯盟)。
for (i in seq_len(nrow(crosstab))) {
# get latest ratings
elo <- as.data.frame(tail(ratings, 1))
# take one row of data corresponding to an event
event <- crosstab[i, ]
# spread the row into a player roster
roster <- event %>% gather(key=no, value=playerid, a1:h5) %>%
mutate(ah = substr(no, 1, 1), # away or home team
score = ifelse(ah=="a", a.evt.score, h.evt.score)) %>% #win or lose
select(playerid, ah, score) %>%
left_join(elo) # get current rating
# unrated players assigned base rating
roster$rating[is.na(roster$rating)] <- seed.rating
# calculate expected and new ratings of event participants
roster$expected <- sapply(roster$playerid, compileExpect)
roster$new.rating <- with(roster, rating + k*(score-expected))
# calculate new overall ratings
new.ratings <- roster %>% select(playerid, new.rating) %>%
rename(rating=new.rating) %>%
rbind(elo) %>%
filter(!duplicated(playerid)) # remove old ratings of player
#update ratings
ratings <- c(ratings, list(new.ratings))
# Optional for error checking: update log of result every round
rosters <- c(rosters, list(roster))
}
的輸出將是一個列表ratings
用16族元素,和rosters
具有15個元素。 ratings
中的元素x是在事件編號x之前的等級,而rosters
中的元素x是在事件編號x之後的結果。
我們以事件2爲例(即表中的第二行)。
> rosters[[2]]
playerid ah score rating expected new.rating
1 1696 a 1 2200 0.4913707 2215.259
2 371 a 1 2200 0.4913707 2215.259
3 4471 a 1 2200 0.4913707 2215.259
4 2119 a 1 2200 0.4913707 2215.259
5 274 a 1 2200 0.4913707 2215.259
6 1947 h 0 2200 0.5000000 2185.000
7 5745 h 0 2200 0.5000000 2185.000
8 3622 h 0 2200 0.5000000 2185.000
9 438 h 0 2215 0.5215733 2199.353
10 5444 h 0 2215 0.5215733 2199.353
初步檢查似乎一切都是爲了:8個的球員誰不玩早在啓動的2200等級,誰是對冠軍球隊的兩名球員更早有等級> 2200展望爲球隊的新球員「h」爲0.5,因爲他們與團隊「a」中的所有玩家(他們都是新玩家)的評分相同。事件2之後
評分。將事件3之前的收視率(其包括來自事件1和事件2個玩家):
> ratings[[3]]
playerid rating
1 438 2199.353
2 1947 2185.000
3 2632 2215.000
4 2119 2215.259
5 3622 2185.000
6 3311 2185.000
7 4003 2185.000
8 726 2215.000
9 5444 2215.000
10 1696 2215.259
11 371 2215.259
12 274 2215.259
13 3784 2185.000
14 4471 2215.259
15 4177 2185.000
16 5745 2185.000
17 633 2215.000
18 2737 2185.000
在這一切結束時,有在ratings[[16]]
33個額定播放器,這應該與表中唯一玩家號碼的總數相匹配。
編輯:我錯過了期望的輸出是球員評級歷史的向量(謝謝@Tensibai指出)。要做到這一點,我創建了一個輔助函數,通過他的ID來提取任何球員的歷史記錄。
getPlayerHistory <- function(id) {
# pull all ratings of the player
temp <- lapply(ratings, function(x) x$rating[x$playerid==id])
# coerce into vector with same length as the list, forcing parts with no values into NA
vec <- do.call(c, lapply(temp, function(x) {length(x) <- 1; return(x)}))
return(vec)
}
您可以直接調用例如
getPlayerHistory("5034")
[1] NA NA NA 2185.395 2171.403 2171.403 2171.403 2171.403 2171.403
[10] 2171.403 2171.403 2171.403 2171.403 2186.862 2186.862 2202.293
注意,有16個值在這個載體,因爲它們是事件之前評級。所以第一個NA
是因爲沒有開始評級,接下來的兩個NA
是因爲玩家「5034」在事件3中第一次玩,所以第一個評級在事件4之前。當玩家沒有玩時一個事件,他的評價保持不變。
您可以使用助手功能將整個評分歷史記錄拖到列表中。
idList <- tail(ratings, 1)[[1]]$playerid # get the latest ratings list
ratList <- lapply(idList, getPlayerHistory)
names(ratList) <- idList
然後通過調用列表可以得到相同的結果。
> ratList[["5034"]]
[1] NA NA NA 2185.395 2171.403 2171.403 2171.403 2171.403 2171.403
[10] 2171.403 2171.403 2171.403 2171.403 2186.862 2186.862 2202.293
示例中的第二行最後一行的'a.evt.score'和'h.evt.score'都是'1'。我如何解釋? – Ricky
另外我想你會需要一個開始評級,這是有意義的?即所有球員的第一個「R_old」?或者我們只是假設每個人都從0分開始(在這種情況下,你會看到團隊中的每個人在第一輪之後都有相同的評分)?如果你提供了一個樣本初始'R_old'向量,它將爲表中所有唯一標識賦予向量,這將有所幫助。 – Ricky
謝謝你抓到裏奇。每位選手都以2200的評分開始。 – Colin