Skip to contents

Scores each shot from [load_shots()] against a baseline of how many strokes a PGA Tour player needs to hole out from a given lie and distance. A shot's strokes gained is the expected strokes before it, minus the expected strokes after it, minus the strokes it cost (one, plus any penalty strokes before the next swing).

Usage

calculate_strokes_gained(shots, baseline = NULL)

Arguments

shots

A tibble from [load_shots()]. Holes without a holed final stroke are dropped.

baseline

Optional data frame with columns lie, distance (yards) and expected (strokes to hole out) to use in place of the bundled PGA Tour baseline, for example one fit to a different tour.

Value

A tibble with one row per stroke hit (penalty and drop rows are folded into the shot that caused them), with the columns of `shots` plus:

  • start_lie: tee, fairway, rough, sand, recovery (water or unknown) or green

  • start_distance: Yards to the hole before the shot

  • strokes_used: Strokes the shot cost, including penalties

  • expected_before, expected_after: Baseline strokes to hole out before and after the shot

  • sg: Strokes gained by the shot

  • sg_category: "off_tee", "approach", "around_green" or "putt"

Details

The baseline is fit from ShotLink strokes and ships with the package. Over the 2025 and 2026 seasons, round totals built from it and adjusted with [summarise_strokes_gained()] track the PGA Tour's official strokes gained total closely (correlation 0.97 to 0.98, mean difference about 0.1 strokes per round). The split between off-the-tee and approach is less exact (correlation 0.82 to 0.92). Native area and trees count as rough, and "around the green" is defined as within 30 yards of the hole rather than of the green's edge.

See also

[summarise_strokes_gained()] for round, event and season totals.

Examples

shots <- tibble::tibble(
  tournament_id = "R1", player_id = "1", round = 1L, course_id = "C1",
  hole = 1L, par = 4L, yardage = 420L, hole_score = 4L, stroke = 1:4,
  stroke_type = "STROKE", from_code = c("OTB", "OFW", "OGR", "OGR"),
  distance_remaining = c(150, 10, 1, 0),
  final_stroke = c(FALSE, FALSE, FALSE, TRUE)
)
calculate_strokes_gained(shots)[, c("stroke", "start_lie", "sg", "sg_category")]
#> # A tibble: 4 × 4
#>   stroke start_lie      sg sg_category
#>    <int> <chr>       <dbl> <chr>      
#> 1      1 tee        0.0854 off_tee    
#> 2      2 fairway    0.0661 approach   
#> 3      3 green     -0.229  putt       
#> 4      4 green      0.0511 putt       
# \donttest{
sg <- calculate_strokes_gained(load_shots(2026, "Sony"))
# }