Functions to compute rating and ranking using Keener method.

rate_keener(cr_data, ..., fill = 0, force_nonneg_h2h = TRUE,
  skew_fun = skew_keener, normalize_fun = normalize_keener, eps = 0.001)

rank_keener(cr_data, ..., fill = 0, force_nonneg_h2h = TRUE,
  skew_fun = skew_keener, normalize_fun = normalize_keener, eps = 0.001,
  keep_rating = FALSE, ties = c("average", "first", "last", "random", "max",
  "min"), round_digits = 7)


normalize_keener(mat, cr_data)



Competition results in format ready for as_longcr().


Head-to-Head expression (see h2h_mat()).


A single value to use instead of NA for missing pairs.


Whether to force nonnegative values in Head-to-Head matrix.


Skew function.


Normalization function.


Coefficient for forcing irreducibility.


Whether to keep rating column in ranking output.


Value for ties in round_rank().


Value for round_digits in round_rank().


Argument for skew_keener().


Argument for normalize_keener().


rate_keener() returns a tibble with columns player (player identifier) and rating_keener (Keener rating). Sum of all ratings should be equal to 1. Bigger value indicates better player performance.

rank_keener() returns a tibble with columns player, rating_keener (if keep_rating = TRUE) and ranking_keener (Keener ranking computed with round_rank()).

skew_keener() returns skewed vector of the same length as x.

normalize_keener() returns normalized matrix with the same dimensions as mat.


Keener rating method is based on Head-to-Head matrix of the competition results. Therefore it can be used for competitions with variable number of players. Its algorithm is as follows:

  1. Compute Head-to-Head matrix of competition results based on Head-to-Head expression supplied in ... (see h2h_mat() for technical details and section Design of Head-to-Head values for design details). Head-to-Head values are computed based only on the games between players of interest (see Players). Ensure that there are no NAs by using fill argument. If force_nonneg_h2h is TRUE then the minimum value is subtracted (in case some Head-to-Head value is strictly negative).

  2. Update raw Head-to-Head values (denoted as S) with the pair-normalization: a_ij = (S_ij + 1) / (S_ij + S_ji + 2). This step should make comparing different players more reasonable.

  3. Skew Head-to-Head values with applying skew_fun to them. skew_fun should take numeric vector as only argument. It should return skewed vector. The default skew function is skew_keener(). This step should make abnormal results not very abnormal. To omit this step supply skew_fun = NULL.

  4. Normalize Head-to-Head values with normalize_fun using cr_data. normalize_fun should take Head-to-Head matrix as the first argument and cr_data as second. It should return normalized matrix. The default normalization is normalize_keener() which divides Head-to-Head value of 'player1'-'player2' matchup divided by the number of games played by 'player1' (error is thrown if there are no games). This step should take into account possibly not equal number of games played by players. To omit this step supply normalize_keener = NULL.

  5. Add small value to Head-to-Head matrix to ensure its irreducibility. If all values are strictly positive then this step is omitted. In other case small value is computed as the smallest non-zero Head-to-Head value multiplied by eps. This step is done to ensure applicability of Perron-Frobenius theorem.

  6. Compute Perron-Frobenius vector of the resultant matrix, i.e. the strictly positive real eigenvector (which values sum to 1) for eigenvalue (which is real) of the maximum absolute value. This vector is Keener rating vector.

If using normalize_keener() in normalization step, ensure to analyze players which actually played games (as division by a number of played games is made). If some player didn't play any game, en error is thrown.

Design of Head-to-Head values

Head-to-Head values in these functions are assumed to follow the property which can be equivalently described in two ways:

  • In terms of matrix format: the more Head-to-Head value in row i and column j the better player from row i performed than player from column j.

  • In terms of long format: the more Head-to-Head value the better player1 performed than player2.

This design is chosen because in most competitions the goal is to score more points and not less. Also it allows for more smooth use of h2h_funs from comperes package.


comperank offers a possibility to handle certain set of players. It is done by having player column (in longcr format) as factor with levels specifying all players of interest. In case of factor the result is returned only for players from its levels. Otherwise - for all present players.


James P. Keener (1993) The Perron-Frobenius theorem and the ranking of football teams. SIAM Review, 35(1):80–93, 1993.


rate_keener(ncaa2005, sum(score1))
#> # A tibble: 5 x 2 #> player rating_keener #> <chr> <dbl> #> 1 Duke 0.0671 #> 2 Miami 0.351 #> 3 UNC 0.158 #> 4 UVA 0.161 #> 5 VT 0.263
rank_keener(ncaa2005, sum(score1))
#> # A tibble: 5 x 2 #> player ranking_keener #> <chr> <dbl> #> 1 Duke 5 #> 2 Miami 1 #> 3 UNC 4 #> 4 UVA 3 #> 5 VT 2
rank_keener(ncaa2005, sum(score1), keep_rating = TRUE)
#> # A tibble: 5 x 3 #> player rating_keener ranking_keener #> <chr> <dbl> <dbl> #> 1 Duke 0.0671 5 #> 2 Miami 0.351 1 #> 3 UNC 0.158 4 #> 4 UVA 0.161 3 #> 5 VT 0.263 2
# Impact of skewing rate_keener(ncaa2005, sum(score1), skew_fun = NULL)
#> # A tibble: 5 x 2 #> player rating_keener #> <chr> <dbl> #> 1 Duke 0.0898 #> 2 Miami 0.295 #> 3 UNC 0.165 #> 4 UVA 0.189 #> 5 VT 0.261
# Impact of normalization. rate_keener(ncaa2005[-(1:2), ], sum(score1))
#> # A tibble: 5 x 2 #> player rating_keener #> <chr> <dbl> #> 1 Duke 0.162 #> 2 Miami 0.335 #> 3 UNC 0.136 #> 4 UVA 0.149 #> 5 VT 0.219
rate_keener(ncaa2005[-(1:2), ], sum(score1), normalize_fun = NULL)
#> # A tibble: 5 x 2 #> player rating_keener #> <chr> <dbl> #> 1 Duke 0.128 #> 2 Miami 0.300 #> 3 UNC 0.153 #> 4 UVA 0.161 #> 5 VT 0.257