Skip to contents

Estimates each player's chance of winning after every hole, and the win probability added (WPA) by each hole, for PGA Tour stroke play events.

Usage

add_win_probability(
  holes,
  sims = 10000,
  seed = 1,
  ratings = NULL,
  cut = 65,
  rounds = 4,
  start = NULL
)

Arguments

holes

Hole-by-hole data with tournament_id, player_id, round, hole, par and score, such as the output of [load_holes()]. Several events can be passed at once.

sims

Number of simulations used while a cut is still to come. After the last cut the calculation is exact.

seed

Random seed, so results are repeatable.

ratings

Optional skill ratings: a named numeric vector (names are ESPN player IDs) or a data frame with player_id and rating. By default the published pre-event ratings are used, or each player's latest rating for events not yet in them.

cut

Cut rule ("top cut and ties" after round 2) assumed for an event whose cut has not happened yet. Use NA for events with no cut. Cuts already made are read from the data.

rounds

Number of regulation rounds in the event: 3 for an event shortened to 54 holes, 5 for the old 90-hole Bob Hope Classic.

start

Optional starting strokes as a named numeric vector (names are ESPN player IDs), as at the 2019-2024 Tour Championship.

Value

holes with these columns added, and the current chances in attr(, "current"):

  • win_prob: Chance the player wins, after this hole

  • win_prob_before: Chance before this hole

  • wpa: win_prob - win_prob_before

  • skill_rating: Pre-event rating, strokes per round better than an average tour player

Details

Hole files from [load_holes()] already carry these columns for completed events from 2004 on. Use this function for an event in progress or for your own hole data.

For an event in progress, players who are behind in their round (or have not teed off) stay in the field at their latest score, and each player's chance right now is returned as attr(x, "current").

The model plays out the rest of the event from each hole. Each remaining hole is drawn from how the field played that hole that day, shifted for the player's skill rating (strokes per round better than the field, from every earlier round since 2004) and for how the player has done so far this week and this round. Cuts are applied and ties for first are settled as a playoff (split evenly). Once no cut is left the answer is exact; before that it is simulated.

Fit on 2004-2019 and checked on 2020-2026, the probabilities are calibrated (for example, players given 10-20 those given 70-90 is 3.31 in rounds 1-2 and 1.62 in rounds 3-4, against 3.79 and 1.79 for a logistic model on strokes behind the leader and holes left.

Holes are assumed to be played 1 to 18. Players who start on the 10th tee are therefore placed in the wrong order within a round; their totals at the end of each round are unaffected. Playoff holes (rounds after rounds) split the chances evenly among the players still in until the playoff is decided. Modified Stableford and team events get `NA`.

Examples

# \donttest{
holes <- load_holes(2025, "Sony")
holes <- add_win_probability(holes, sims = 2000)
# }