Tee to Title: A Statistical Comparison of Rory McIlroy’s 2025 & 2026 Masters Performances

Tidying Up Augusta

Author
Affiliation

Kjersti Warwick

Daniels College of Business, University of Denver

Published

June 2026

First Steps Down Magnolia Lane

This project examines Rory McIlroy’s performance across his back-to-back Masters championships in 2025 and 2026, both earning him golf’s most coveted prize: the Green Jacket.

McIlroy, a Northern Irish professional competing on the PGA Tour and DP World Tour, is one of the game’s most decorated players. A former world No. 1 who held the ranking for 100 weeks across his career, he entered Augusta in 2025 as one of the sport’s greatest players still missing a Masters title. That changed when he defeated Justin Rose in a sudden-death playoff, securing his sixth major and completing the career Grand Slam — making him only the sixth player and first European to achieve the feat. He returned in 2026 and won by one stroke at 12-under 276, becoming just the fourth player in history — and the first since Tiger Woods in 2001–2002 — to win back-to-back titles at Augusta National.

My interest in this topic is personal. I played golf on my high school team for three years and later worked at Erin Hills Golf Course, host of the 2017 U.S. Men’s Open and the 2025 U.S. Women’s Open. Immersed in the history of that tournament and the players who competed there, I developed a deeper appreciation for the analytical side of professional golf — which directly inspired this project.

Strokes gained is the same framework PGA analysts, caddies, and coaches use to evaluate player performance. Understanding it puts individuals in conversation with how the sport can actually be analyzed at a professional level. McIlroy’s back-to-back wins created a natural controlled comparison – same course, same player, and consecutive years – which is rare in sports data. This structure lets us ask whether player improvement was real for those two years and find where the improvement came from, rather than just describing the outcomes.

The Approach

TipGolf Terminology

The approach shot is the critical moment where you commit to your strategy and aim directly at the target.

Does McIlroy’s back-to-back Masters success reflect absolute improvement or relative dominance?

Teeing Off

Packages & .CSV

#Packages Used
library(readr)
library(ggplot2)
library(tidyr)
library(dplyr)
library(stringr)
library(fmsb)
library(ggrepel)
library(DT)
library(purrr)

#Reading in 2025 and 2026 Data
Masters2025 <- read_csv("/Users/kjerstiwarwick/Desktop/Course Work/INFO 3320/Final Project/2025MastersGolf.csv")
Masters2026 <- read_csv('/Users/kjerstiwarwick/Desktop/Course Work/INFO 3320/Final Project/2026MastersGolf.csv')

#McIlroy Filtered Data
mcilroy_2025 <- Masters2025 |> filter(str_detect(player_name, "McIlroy"))
mcilroy_2026 <- Masters2026 |> filter(str_detect(player_name, "McIlroy"))

Key Colors

Color Code
“Masters Green” #006747
Gold #EFBF04
Scarlet Red #E31B23
Light Green #87B87F
Gray N/A

The Green Book - Data & Variables

TipGolf Terminology

Augusta’s Green Book is legendary in golf as the secret, highly detailed course notes that only a handful of caddies possess.

The Scorecards - The Data

datatable(Masters2025, filter = "top")
datatable(Masters2026, filter = "top")

Variables Explained

Strokes Gained is a performance metric that measures how many shots are gained or lost against a predetermined bench mark.

Strokes gained gives a breakdown of every part of the round. Instead of counting total putts or fairway hits, golf uses strokes gained to tell you where you are losing or saving shots.

SG: Total Benchmark

At the Masters, players will typically need to benchmark at least +1.50-2.00 SG: Total per round against the field. If the field scoring average for the day is 72 and you shoot a 69, your SG: Total is +3. If you shoot a 75, your total is -3.

Augusta National

The two non shots gained variables are specific to Par 4s and 5s. There are ten par 4s and four par 5s at August National Golf Club, meaning there are 56 holes analyzed for drive distance and accuracy in the data set.

Official Term Variable Defined
SG:Putt sg_putt Measures how many strokes a player gains (or loses) on the putting green.
SG:Around the Green (ARG) sg_arg Measures a player’s performance on all shots played within 30-50 yards of the hole. Includes tee shots, approach shots from further out, and puts on the green.
SG:Approach (APP) sg_app Measures shot hit into the green, including Par 3s and any shot meant to reach the green.
SG:Off the Tee (OTT) sg_ott Measures a player’s performance on all tee shots (excluding Par 3s), evaluating how many strokes a player gains or loses against the field average by combining both the distance and accuracy of drives.
SG:Tee to Goal (TTG) sg_t2g Combines OTT, App, ATG to measure all non-putting play.
SG:Total sg_total Measures how many strokes a player gained or lost against the field average over an entire round of tournament. A positive number means the player played better than the field average, while a negative numbers means they performed worse.
SG:Ball Striking (BS) sg_bs Evaluates a player’s combined performance for hitting the ball off the tee and during their approach shots into the green. Combines SG:OTT and SG:APP.
Distance distance Average distance of all drives on Par 4s and 5s. Reloads are included.
Accuracy accuracy Percentage of fairways hit on Par 4s and 5s. Reloads not included.

Whiffs - Null Values

Please note for 41 players that did not make the cut to round 4, null values will fill in for scores, measurements, and position. I felt it was important to still include these 41 players for larger calculations that encompass the entire tournament and event totals.

Lee Wybranski Art & Design

The 19th Hole - Summary Stats

TipGolf Terminology

The 19th Hole commonly refers to going to the bar after that round of golf. It is when the game and scores are reflected on.

#Summary Stats
summary_vars <- c("total_score", "sg_putt", "sg_arg", "sg_app", "sg_ott",
                  "sg_t2g", "sg_total", "sg_bs", "distance", "accuracy")
summary(Masters2025[, summary_vars])
  total_score         sg_putt            sg_arg           sg_app       
 Min.   :-11.000   Min.   :-3.1100   Min.   :-2.750   Min.   :-3.1600  
 1st Qu.: -2.000   1st Qu.:-0.8900   1st Qu.:-0.470   1st Qu.:-0.8100  
 Median :  4.000   Median :-0.0600   Median :-0.030   Median :-0.0600  
 Mean   :  2.516   Mean   :-0.1507   Mean   :-0.106   Mean   :-0.1685  
 3rd Qu.:  6.000   3rd Qu.: 0.6800   3rd Qu.: 0.470   3rd Qu.: 0.6700  
 Max.   : 17.000   Max.   : 2.3300   Max.   : 1.630   Max.   : 2.3100  
     sg_ott             sg_t2g           sg_total           sg_bs        
 Min.   :-4.46000   Min.   :-7.6100   Min.   :-7.3700   Min.   :-5.9300  
 1st Qu.:-0.48000   1st Qu.:-1.2800   1st Qu.:-1.5700   1st Qu.:-0.9200  
 Median : 0.10000   Median :-0.1600   Median :-0.3700   Median : 0.0200  
 Mean   :-0.04242   Mean   :-0.3178   Mean   :-0.4684   Mean   :-0.2108  
 3rd Qu.: 0.54500   3rd Qu.: 0.8750   3rd Qu.: 1.1800   3rd Qu.: 0.6900  
 Max.   : 1.50000   Max.   : 3.7400   Max.   : 3.4300   Max.   : 2.9700  
    distance        accuracy     
 Min.   :255.3   Min.   :0.4300  
 1st Qu.:293.7   1st Qu.:0.6900  
 Median :299.8   Median :0.7300  
 Mean   :298.6   Mean   :0.7342  
 3rd Qu.:305.4   3rd Qu.:0.7900  
 Max.   :317.9   Max.   :0.9300  
summary(Masters2026[, summary_vars])
  total_score         sg_putt            sg_arg            sg_app       
 Min.   :-12.000   Min.   :-2.9500   Min.   :-2.3800   Min.   :-4.3100  
 1st Qu.: -2.500   1st Qu.:-0.8650   1st Qu.:-0.5150   1st Qu.:-0.7550  
 Median :  3.000   Median :-0.1100   Median : 0.0100   Median :-0.0400  
 Mean   :  2.736   Mean   :-0.1649   Mean   :-0.1012   Mean   :-0.1977  
 3rd Qu.:  8.000   3rd Qu.: 0.5300   3rd Qu.: 0.4400   3rd Qu.: 0.6300  
 Max.   : 18.000   Max.   : 2.8800   Max.   : 2.7700   Max.   : 2.4000  
     sg_ott           sg_t2g           sg_total          sg_bs        
 Min.   :-4.240   Min.   :-5.8800   Min.   :-7.250   Min.   :-6.4400  
 1st Qu.:-0.670   1st Qu.:-1.8500   1st Qu.:-2.250   1st Qu.:-1.2400  
 Median : 0.050   Median :-0.1400   Median :-0.200   Median :-0.1100  
 Mean   :-0.129   Mean   :-0.4271   Mean   :-0.594   Mean   :-0.3263  
 3rd Qu.: 0.535   3rd Qu.: 1.1350   3rd Qu.: 1.175   3rd Qu.: 0.8500  
 Max.   : 1.830   Max.   : 3.1600   Max.   : 3.550   Max.   : 3.0000  
    distance        accuracy     
 Min.   :261.8   Min.   :0.3200  
 1st Qu.:295.4   1st Qu.:0.6500  
 Median :303.3   Median :0.7100  
 Mean   :302.5   Mean   :0.7084  
 3rd Qu.:309.8   3rd Qu.:0.7900  
 Max.   :330.5   Max.   :0.9100  

In both years, the summary stats reflect the difficulty of Augusta National. The media SG:Total sits below zero which confirms most players in the field will lose strokes to the course average. The 2026 field played slightly harder overall, the media total score rose from +4 to +3 relative to par and the mean SG:Total dropped from -0.47 to -0.59. This suggests the course played more difficult or the field was less competitive.

Driving distance increased in 2026 with a median of 303.3 yards while fairway accuracy dipped about 3%. This reflects a field that swung harder yet less precisely. We will explore this further in a moment. Putting was also a weakness in both fields. The mean for SG:Putting was -0.15 and -0.16 indicating Augusta’s famously challenging greens cost the average player strokes both years. Those who putt well were able to stand out.

Score Distributions

ggplot() +
  geom_histogram(data = Masters2025, 
                 aes(x = total_score,
                     fill = "2025"),
                 binwidth = 1,
                 alpha = 0.6, 
                 color = "white") +
  geom_histogram(data = Masters2026, 
                 aes(x = total_score,
                     fill = "2026"),
                 binwidth = 1,
                 alpha = 0.6,
                 color = "white") +
  scale_fill_manual(values = c("2025" = "#006747", "2026" = "#FFD700"),
                    name = "Year") +
  labs(title = "Score Distribution: 2025 vs. 2026 Masters",
       x = "Total Score (relative to par)",
       y = "Number of Players") +
  theme_minimal()

The 2025 score distribution is tighter and bell shaped, clustering heavily at +3 to +5, pointing to a relatively consistent field. The 2026 distribution is flatter and wider, showing more players scored well under par but at the same time more players played over-par. 2026 was a more polarized tournament. The best players were more separated from the rest while the bottom of the leaderboard struggled more than in 2025.

Stokes Gained by Category

sg_levels <- c("sg_ott", "sg_app", "sg_arg", "sg_putt", "sg_t2g", "sg_bs", "sg_total")
sg_labels <- c("Off Tee", "Approach", "Around Green", "Putting", "Tee-to-Green", "Ball Striking", "Total")

sg_means_2025 <- Masters2025 |>
  summarise(across(all_of(sg_levels), mean, na.rm = TRUE)) |>
  pivot_longer(cols = everything(),
               names_to = "category",
               values_to = "mean_sg") |>
  mutate(category = factor(category,
                           levels = sg_levels,
                           labels = sg_labels))

sg_means_2026 <- Masters2026 |>
  summarise(across(all_of(sg_levels), mean, na.rm = TRUE)) |>
  pivot_longer(cols = everything(),
               names_to = "category",
               values_to = "mean_sg") |>
  mutate(category = factor(category,
                           levels = sg_levels,
                           labels = sg_labels))

ggplot() +
  geom_col(data = sg_means_2025,
           aes(x = as.numeric(category) - 0.2,
               y = mean_sg,
               fill = "2025"),
           width = 0.35) +
  geom_col(data = sg_means_2026,
           aes(x = as.numeric(category) + 0.2,
               y = mean_sg,
               fill = "2026"),
           width = 0.35) +
  geom_hline(yintercept = 0,
             linetype = "dashed",
             color = "gray") +
  scale_x_continuous(breaks = seq_along(sg_labels),
                     labels = sg_labels) +
  scale_fill_manual(values = c("2025" = "#006747", "2026" = "#87B87F"),
                    name = "Year") +
  labs(title = "Average Strokes Gained by Category: 2025 vs. 2026 Masters",
       x = NULL,
       y = "Mean Strokes Gained") +
  theme_minimal()

The 2025 field under performed 2026 in all categories but SG:Around the Green. The difference was most notable in SG:Approach, SG:Tee to Green, and SG:Total. The average played in 2025 lost more strokes to the benchmark than in 2026 in most skill areas. This reinforces the idea that 2026 saw greater variance.

Rory & The Field

Driving Distance vs. Accuracy

ggplot() +
  geom_point(data = Masters2025, aes(x = distance,
                                     y = accuracy,
                                     color = "2025"),
             alpha = 0.6, size = 2) +
  geom_point(data = Masters2026, aes(x = distance,
                                     y = accuracy,
                                     color = "2026"),
             alpha = 0.6, size = 2) +
  geom_point(data = mcilroy_2025, aes(x = distance,
                                      y = accuracy,
                                      color = "2025"),
             size = 5, shape = 18) +
  geom_point(data = mcilroy_2026, aes(x = distance,
                                      y = accuracy,
                                      color = "2026"),
             size = 5, shape = 18) +
  geom_text(data = mcilroy_2025, aes(x = distance,
                                     y = accuracy,
                                     label = "McIlroy '25"),
            vjust = -1,
            size = 3,
            color = "#006747") +
  geom_text(data = mcilroy_2026, aes(x = distance,
                                     y = accuracy,
                                     label = "McIlroy '26"),
            vjust = -1,
            size = 3,
            color = "#EFBF04") +
  scale_color_manual(values = c("2025" = "#006747", "2026" = "#EFBF04"),
                     name = "Year") +
  labs(title = "Driving Distance vs. Accuracy: 2025 vs. 2026 Masters",
       x = "Average Drive Distance (yards)",
       y = "Driving Accuracy (fairways hit %)"
  ) +
  theme_minimal()

As driving distance increases, the fairway accuracy tends to decrease which forms a mild downward trend for both years. McIlroy’s positioning tells us that in 2025 he was at about 315 yards with ~63% accuracy and already among the longest hitters in the field. In 2026 he pushed his distance to 330 yards but his accuracy dipped to ~54%. This makes him once of the longest but least accurate drivers in the tournament. The drop in accuracy shows how he can effectively recover in his approach and short game.

Radar Charts: Strokes Gained

radar_vars <- c("sg_ott", "sg_app", "sg_arg", "sg_putt")
radar_labels <- c("Off Tee", "Approach", "Around Green", "Putting")

mcilroy_radar_2025 <- as.numeric(mcilroy_2025[, radar_vars])
mcilroy_radar_2026 <- as.numeric(mcilroy_2026[, radar_vars])
field_mean_2025 <- colMeans(Masters2025[, radar_vars], na.rm = TRUE)
field_mean_2026 <- colMeans(Masters2026[, radar_vars], na.rm = TRUE)

radar_max <- ceiling(max(c(mcilroy_radar_2025, mcilroy_radar_2026,
                           field_mean_2025, field_mean_2026)) * 1.2)
radar_min <- floor(min(c(mcilroy_radar_2025, mcilroy_radar_2026,
                         field_mean_2025, field_mean_2026)) * 1.2)

radar_df <- as.data.frame(rbind(
  rep(radar_max, 4),
  rep(radar_min, 4),
  mcilroy_radar_2025,
  field_mean_2025,
  mcilroy_radar_2026,
  field_mean_2026))
colnames(radar_df) <- radar_labels
op <- par(mar = c(1, 1, 3, 1))

radarchart(radar_df[c(1, 2, 3, 4), ],
           axistype = 1,
           pcol = c("#006747", "gray60"),
           pfcol = c(adjustcolor("#006747", alpha.f = 0.25),
                     adjustcolor("gray60",  alpha.f = 0.15)),
           plwd = 2.5,
           cglcol = "gray80",
           cglty = 1,
           axislabcol = "gray40",
           vlcex = 0.9,
           title = "Strokes Gained Radar: McIlroy vs. Field — 2025 Masters")

legend("topright",
       legend = c("McIlroy 2025", "Field Mean 2025"),
       col = c("#006747", "gray60"),
       lty = 1, lwd = 2.5,
       bty = "n", cex = 0.85)

op <- par(mar = c(1, 1, 3, 1))

radarchart(radar_df[c(1, 2, 5, 6), ],
           axistype = 1,
           pcol = c("#EFBF04", "gray60"),
           pfcol = c(adjustcolor("#EFBF04", alpha.f = 0.25),
                     adjustcolor("gray60",  alpha.f = 0.15)),
           plwd = 2.5,
           cglcol = "gray80",
           cglty = 1,
           axislabcol = "gray40",
           vlcex = 0.9,
           title = "Strokes Gained Radar: McIlroy vs. Field — 2026 Masters")

legend("topright",
       legend = c("McIlroy 2026", "Field Mean 2026"),
       col = c("#EFBF04", "gray60"),
       lty = 1, lwd = 2.5,
       bty = "n", cex = 0.85)

op <- par(mar = c(1, 1, 3, 1))

radarchart(radar_df[c(1, 2, 3, 5), ],
           axistype  = 1,
           pcol = c("#006747", "#EFBF04"),
           pfcol = c(adjustcolor("#006747", alpha.f = 0.20),
                     adjustcolor("#EFBF04", alpha.f = 0.20)),
           plwd = 2.5,
           cglcol = "gray80",
           cglty = 1,
           axislabcol = "gray40",
           vlcex = 0.9,
           title = "McIlroy SG Components: 2025 vs. 2026 Masters")

legend("topright",
       legend = c("McIlroy 2025", "McIlroy 2026"),
       col = c("#006747", "#EFBF04"),
       lty = 1, lwd = 2.5,
       bty = "n", 
       cex = 0.85)

The radar shots provide a visual summary of McIlroy’s SG profile relative to the field mean across 4 key categories each year. In both years, McIlroy’s shape enclosed the field mean, confirming he outperformed the average player in the Masters in every dimension. But the shape of his advantage differs between each year.

In 2025, the chart is asymmetric with a dramatic stretch towards Approach which reflects exceptional iron play. The putting axis barely extends beyond the field mean, once again confirming what the t-tests showed: he is an average putter.

In 2026, the shape was more balanced. The polygon extends evenly across all four axes with putting more and putting contributing meaningfully to the overall shape. The area is slightly smaller than 2025 meaning McIlroy’s advantage shrunk but the radar still shows a more well-rounded performance.

Pearson Correlation Analysis

sg_corr_vars <- c("sg_putt", "sg_arg", "sg_app", "sg_ott", "sg_t2g", "sg_bs", "sg_total")

sg_corr_labels <- c(
  sg_putt = "Putting",
  sg_arg = "Around Green",
  sg_app = "Approach",
  sg_ott = "Off the Tee",
  sg_t2g = "Tee-to-Green",
  sg_bs = "Ball Striking",
  sg_total = "Total")

compute_correlations <- function(df, year) {
  map_dfr(sg_corr_vars, function(var) {
    complete_rows <- df |>
      select(position, all_of(var)) |>
      filter(!is.na(position), !is.na(.data[[var]]))
    ct <- cor.test(complete_rows[[var]], complete_rows$position,
                   method = "pearson")
    tibble(year = year,
           variable = var,
           label = sg_corr_labels[var],
           correlation = round(ct$estimate, 3),
           p_value = round(ct$p.value,  4),
           significant = ifelse(ct$p.value < 0.05, "Yes *", "No"))})
}

corr_2025 <- compute_correlations(Masters2025, "2025")
corr_2026 <- compute_correlations(Masters2026, "2026")

corr_combined <- bind_rows(corr_2025, corr_2026) |>
  arrange(variable, year)

print(corr_combined, n = Inf)
# A tibble: 14 × 6
   year  variable label         correlation p_value significant
   <chr> <chr>    <chr>               <dbl>   <dbl> <chr>      
 1 2025  sg_app   Approach           -0.474  0.0003 Yes *      
 2 2026  sg_app   Approach           -0.649  0      Yes *      
 3 2025  sg_arg   Around Green       -0.423  0.0016 Yes *      
 4 2026  sg_arg   Around Green       -0.427  0.0013 Yes *      
 5 2025  sg_bs    Ball Striking      -0.642  0      Yes *      
 6 2026  sg_bs    Ball Striking      -0.766  0      Yes *      
 7 2025  sg_ott   Off the Tee        -0.417  0.0019 Yes *      
 8 2026  sg_ott   Off the Tee        -0.491  0.0002 Yes *      
 9 2025  sg_putt  Putting            -0.371  0.0062 Yes *      
10 2026  sg_putt  Putting            -0.268  0.0504 No         
11 2025  sg_t2g   Tee-to-Green       -0.764  0      Yes *      
12 2026  sg_t2g   Tee-to-Green       -0.848  0      Yes *      
13 2025  sg_total Total              -0.98   0      Yes *      
14 2026  sg_total Total              -0.977  0      Yes *      
corr_plot_df <- corr_combined |>
  mutate(label = factor(label, levels = sg_corr_labels[sg_corr_vars]),
         sig_label = ifelse(significant == "Yes *", "*", ""))

ggplot(corr_plot_df, aes(x = label,
                         y = correlation,
                         fill = year)) +
  geom_col(position = "dodge", alpha = 0.9, width = 0.65) +
  geom_text(aes(label = sig_label,
                y = correlation + ifelse(correlation >= 0, 0.03, -0.03)),
            position = position_dodge(width = 0.65),
            size = 5, vjust = 0) +
  geom_hline(yintercept = 0,
             linetype = "dashed",
             color = "gray40") +
  scale_fill_manual(values = c("2025" = "#006747", "2026" = "#EFBF04"),
                    name = "Year") +
  labs(title = "Pearson Correlation: SG Components vs. Finishing Position",
       subtitle = "Negative = higher SG predicts a better finish  |  * = p < .05",
       x = NULL,
       y = "Correlation with Finishing Position") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 30, hjust = 1))

The Pearson correlation analysis correlates each SG category against finishing position for both years. The stronger the negative value, the more that skill drove the finishing order. SG:Total will be the best predictor of finishing position because where you rank in overall strokes gained is essentially where you will finish on the leaderboard.

SG:Tee to Green is the next strongest predictor for both years. It strengthened in 2026, suggesting non-putting ball control was a more decisive factor in separating the field. Ball striking follows a similar pattern, strengthening from -0.642 to -0.766.

SG:Approach saw the largest single-category shift between years. Players who hit greens well in 2026 were more likely to finish high. Putting was not a significant predictor of finishing position in 2026 like it was in 2025. That means putting barely influenced where players finished, making ball-striking the true separator.

Percentile Ranks

compute_percentiles <- function(masters_df, mcilroy_row, year) {
  map_dfr(sg_corr_vars, function(var) {
    field_vals  <- masters_df[[var]]
    mcilroy_val <- mcilroy_row[[var]]
    pct <- round(mean(field_vals <= mcilroy_val, na.rm = TRUE) * 100, 1)
    tibble(year = year,
           variable = var,
           label = sg_corr_labels[var],
           mcilroy_sg = round(mcilroy_val, 3),
           percentile = pct)})
}

pct_2025 <- compute_percentiles(Masters2025, mcilroy_2025, "2025")
pct_2026 <- compute_percentiles(Masters2026, mcilroy_2026, "2026")

pct_combined <- bind_rows(pct_2025, pct_2026) |>
  arrange(variable, year)

print(pct_combined, n = Inf)
# A tibble: 14 × 5
   year  variable label         mcilroy_sg percentile
   <chr> <chr>    <chr>              <dbl>      <dbl>
 1 2025  sg_app   Approach            2.31      100  
 2 2026  sg_app   Approach            0.66       75.8
 3 2025  sg_arg   Around Green        0.77       86.3
 4 2026  sg_arg   Around Green        1.39       96.7
 5 2025  sg_bs    Ball Striking       2.97      100  
 6 2026  sg_bs    Ball Striking       1.58       90.1
 7 2025  sg_ott   Off the Tee         0.67       87.4
 8 2026  sg_ott   Off the Tee         0.91       89  
 9 2025  sg_putt  Putting            -0.31       41.1
10 2026  sg_putt  Putting             0.59       79.1
11 2025  sg_t2g   Tee-to-Green        3.74      100  
12 2026  sg_t2g   Tee-to-Green        2.97       97.8
13 2025  sg_total Total               3.43      100  
14 2026  sg_total Total               3.55      100  
pct_plot_df <- pct_combined |>
  mutate(label = factor(label, levels = sg_corr_labels[sg_corr_vars]))

ggplot(pct_plot_df, aes(x = percentile,
                        y = label,
                        color = year)) +
  geom_vline(xintercept = 50, linetype = "dashed", color = "#E31B23") +
  geom_segment(
    aes(x = 50,
        xend = percentile,
        y = label,
        yend = label),
    position = position_dodge(width = 0.6),
    linewidth = 0.8) +
  geom_point(size = 5,
             position = position_dodge(width = 0.6)) +
  geom_text(aes(label = paste0(percentile, "%")),
            position = position_dodge(width = 0.6),
            hjust = -0.35, size = 3, fontface = "bold") +
  scale_color_manual(values = c("2025" = "#006747", "2026" = "#EFBF04"),
                     name = "Year") +
  scale_x_continuous(limits = c(0, 110), breaks = c(0, 25, 50, 75, 100),
                     labels = c("0%", "25%", "50%", "75%", "100%")) +
  labs(title = "McIlroy's Percentile Rank vs. Field by SG Category",
       subtitle = "Dashed line = field median (50th percentile)",
       x = "Percentile Rank",
       y = NULL) +
  theme_minimal()

The percentile table and lollipop chart answer the question of where specifically McIlroy stood in the field and how much better he was. In 2025, he was in the 41st percentile of putting, below the field median. More than half the players in the tournament were better at putting. He won a Major while ranking in the bottom half of the field in putting, a testament to how his ball-striking carried him.

The 2026 lollipop chart shows all points clustered around the 75-100th percentile range, a profile with no real weakness. Approach is the sharpest reversal. He fell from the 100th percentile to 75.8%. Well above average but no longer the dominant or best player on the field. Given how approach was the strongest predictor of finishing position in 2026, this drop is notable.

Same Bag, New Round

One Sample T-Test

sg_test_vars <- c("sg_putt", "sg_arg", "sg_app", "sg_ott", "sg_t2g", "sg_bs", "sg_total")

run_ttest <- function(masters_df, mcilroy_row, year) {
  purrr::map_dfr(sg_test_vars, function(var) {
    field_vals <- masters_df[[var]][!is.na(masters_df[[var]])]
    mcilroy_val <- mcilroy_row[[var]]
    field_mean <- mean(field_vals, na.rm = TRUE)
    field_sd <- sd(field_vals,   na.rm = TRUE)
    n <- length(field_vals)
    t_result <- t.test(field_vals, mu = mcilroy_val)
    tibble(year = year,
      variable = var,
      mcilroy_val = round(mcilroy_val, 3),
      field_mean = round(field_mean,  3),
      field_sd = round(field_sd,    3),
      diff = round(mcilroy_val - field_mean, 3),
      t_stat = round(t_result$statistic, 3),
      p_value = round(t_result$p.value,   4),
      significant = ifelse(t_result$p.value < 0.05, "Yes *", "No"))
    })
}

ttest_2025 <- run_ttest(Masters2025, mcilroy_2025, "2025")
ttest_2026 <- run_ttest(Masters2026, mcilroy_2026, "2026")

ttest_combined <- bind_rows(ttest_2025, ttest_2026) |>
  arrange(variable, year)

print(ttest_combined, n = Inf)
# A tibble: 14 × 9
   year  variable mcilroy_val field_mean field_sd   diff t_stat p_value
   <chr> <chr>          <dbl>      <dbl>    <dbl>  <dbl>  <dbl>   <dbl>
 1 2025  sg_app          2.31     -0.169    1.22   2.48  -19.9    0    
 2 2026  sg_app          0.66     -0.198    1.24   0.858  -6.60   0    
 3 2025  sg_arg          0.77     -0.106    0.859  0.876  -9.94   0    
 4 2026  sg_arg          1.39     -0.101    0.874  1.49  -16.3    0    
 5 2025  sg_bs           2.97     -0.211    1.49   3.18  -20.8    0    
 6 2026  sg_bs           1.58     -0.326    1.90   1.91   -9.56   0    
 7 2025  sg_ott          0.67     -0.042    0.837  0.712  -8.29   0    
 8 2026  sg_ott          0.91     -0.129    1.04   1.04   -9.52   0    
 9 2025  sg_putt        -0.31     -0.151    1.07  -0.159   1.46   0.149
10 2026  sg_putt         0.59     -0.165    1.12   0.755  -6.43   0    
11 2025  sg_t2g          3.74     -0.318    1.76   4.06  -22.5    0    
12 2026  sg_t2g          2.97     -0.427    2.11   3.40  -15.4    0    
13 2025  sg_total        3.43     -0.468    1.99   3.90  -19.1    0    
14 2026  sg_total        3.55     -0.594    2.47   4.14  -16.0    0    
# ℹ 1 more variable: significant <chr>
sg_nice_labels <- c(
  sg_putt = "Putting",
  sg_arg = "Around Green",
  sg_app = "Approach",
  sg_ott = "Off the Tee",
  sg_t2g = "Tee-to-Green",
  sg_bs = "Ball Striking",
  sg_total = "Total")

ttest_plot_df <- ttest_combined |>
  mutate(variable_label = sg_nice_labels[variable],
         variable_label = factor(variable_label,
                                 levels = sg_nice_labels[sg_test_vars]))

ggplot(ttest_plot_df, aes(x = diff,
                          y = variable_label,
                          color = year)) +
  geom_vline(xintercept = 0,
             linetype = "dashed",
             color = "gray50") +
  geom_point(aes(shape = significant),
             size = 4,
             position = position_dodge(width = 0.5)) +
  geom_text(data = ttest_plot_df |> 
              filter(significant == "Yes *"),
            aes(label = "*"),
            size = 6,
            vjust = 0.3,
            position = position_dodge(width = 0.5),
            show.legend = FALSE) +
  scale_color_manual(values = c("2025" = "#006747", "2026" = "#EFBF04"), 
                     name = "Year") +
  scale_shape_manual(values = c("Yes *" = 18, "No" = 16), 
                     name = "Significant (p < .05)") +
  labs(title = "McIlroy vs. Field: Strokes Gained Difference",
       subtitle = "Diamond = statistically significant at p < .05",
       x = "McIlroy SG − Field Mean",
       y = NULL) +
  theme_minimal() +
  theme(legend.position = "right")

The dot plot visualizes our tibble with the t-test results, making key findings readable. Every dot on the right of the dashed line means McIlroy outperformed the field mean. Nearly all the points are statistically significant with one exception: putting in 2025. This further confirms his performance was statistically indistinguishable from the average player.

Round by Round Scores

round_levels <- c("r1_score", "r2_score", "r3_score", "r4_score")
round_labels <- c("Round 1", "Round 2", "Round 3", "Round 4")

mcilroy_rounds_2025 <- mcilroy_2025 |>
  pivot_longer(cols = all_of(round_levels),
               names_to = "round",
               values_to = "score") |>
  mutate(round = factor(round,
                        levels = round_levels,
                        labels = round_labels))

mcilroy_rounds_2026 <- mcilroy_2026 |>
  pivot_longer(cols = all_of(round_levels),
               names_to = "round",
               values_to = "score") |>
  mutate(round = factor(round,
                        levels = round_levels,
                        labels = round_labels))

ggplot() +
  geom_line(data = mcilroy_rounds_2025,
            aes(x = round,
                y = score,
                color = "2025",
                group = 1),
            linewidth = 1.3) +
  geom_point(data = mcilroy_rounds_2025,
             aes(x = round,
                 y = score,
                 color = "2025"),
             size = 4) +
  geom_line(data = mcilroy_rounds_2026,
            aes(x = round,
                y = score, color = "2026",
                group = 1),
            linewidth = 1.3) +
  geom_point(data = mcilroy_rounds_2026,
             aes(x = round,
                 y = score,
                 color = "2026"),
             size = 4) +
  geom_hline(yintercept = 0,
             linetype = "dashed",
             color = "gray40") +
  scale_color_manual(values = c("2025" = "#006747", "2026" = "#87B87F"), name = "Year") +
  labs(title = "McIlroy's Round-by-Round Scores: 2025 vs. 2026",
       x = NULL,
       y = "Score (relative to par)") +
  theme_minimal()

The two scoring trajectories reveal starkly different paths with the same outcome: a win. In 2025, McIlroy opened with an even par but exploded in Round 2 with -6 and held steady in Round 3, creating a front-loaded performance that built his lead. His 2026 performance was inverted, coming out firing with Round 1 at -5, dipping to his best round yet in Round 2 with -7. He struggled back toward par in Rounds 3 and 4 but closed with a score just under par to off the field creating a win by one stroke difference.

SG:All Categories

all_sg_vars   <- c("sg_putt", "sg_arg", "sg_app", "sg_ott", "sg_t2g", "sg_bs", "sg_total")
all_sg_labels <- c("Putting", "Around Green", "Approach", "Off Tee",
                   "Tee-to-Green", "Ball Striking", "Total")

mcilroy_all_2025 <- as.numeric(mcilroy_2025[, all_sg_vars])
mcilroy_all_2026 <- as.numeric(mcilroy_2026[, all_sg_vars])

radar_all <- as.data.frame(rbind(
  rep(5, 7),
  rep(-2, 7),
  mcilroy_all_2025,
  mcilroy_all_2026))

colnames(radar_all) <- all_sg_labels
rownames(radar_all) <- c("Max", "Min", "2025", "2026")
op <- par(mar = c(2, 2, 3, 2))

radarchart(radar_all[c(1, 2, 3), ],
           axistype = 1,
           pcol = "#006747",
           pfcol = adjustcolor("#006747", alpha.f = 0.3),
           plwd = 2.5,
           cglcol = "gray80",
           cglty = 1,
           axislabcol = "gray40",
           vlcex = 0.9,
           title = "McIlroy: All Strokes Gained Categories — 2025 Masters")

legend("topright",
       legend = "McIlroy 2025",
       col = "#006747",
       lty = 1, lwd = 2.5,
       bty = "n", cex = 0.85)

op <- par(mar = c(2, 2, 3, 2))

radarchart(radar_all[c(1, 2, 4), ],
           axistype = 1,
           pcol = "#EFBF04",
           pfcol = adjustcolor("#EFBF04", alpha.f = 0.3),
           plwd = 2.5,
           cglcol = "gray80",
           cglty = 1,
           axislabcol = "gray40",
           vlcex = 0.9,
           title = "McIlroy: All Strokes Gained Categories — 2026 Masters")

legend("topright",
       legend = "McIlroy 2026",
       col = "#EFBF04",
       lty = 1, lwd = 2.5,
       bt = "n", cex = 0.85)

op <- par(mar = c(2, 2, 3, 2))

radarchart(radar_all[c(1, 2, 3, 4), ],
           axistype = 1,
           pcol = c("#006747", "#EFBF04"),
           pfcol = c(adjustcolor("#006747", alpha.f = 0.2),
                     adjustcolor("#EFBF04", alpha.f = 0.2)),
           plwd = 2.5,
           cglcol = "gray80",
           cglty = 1,
           axislabcol = "gray40",
           vlcex = 0.9,
           title = "McIlroy: Strokes Gained 2025 vs. 2026 — All Categories")

legend("topright",
       legend = c("McIlroy 2025", "McIlroy 2026"),
       col = c("#006747", "#EFBF04"),
       lty = 1, lwd = 2.5,
       bty = "n", cex = 0.85)

McIlroy’s performance across all seven categories are remarkably similar when placed on top of each other. The key differences are in two areas, the approach and putting. In his approach, the 2025 polygon extends extends further and reflects McIlroy’s 100th percentile iron play. Putting is the only space where there is a noticeable gap between the two shapes. This reflects his improvement in putting among the two years. Tee-to-Green and Total were identical between the years but Around Green and Off the Tee show improvement in 2026. This all reinforces how his ball-striking foundation remained constant across both championships while putting was what shifted meaningfully.

Walking Up 18

TipGolf Terminology

Walking Up 18 indicates the end of the round of golf, and a conclusion to the game.

top10_2025 <- Masters2025 |>
  filter(position <= 10) |>
  mutate(year = "2025",
         is_mcilroy = str_detect(player_name, "McIlroy"))

top10_2026 <- Masters2026 |>
  filter(position <= 10) |>
  mutate(year = "2026",
         is_mcilroy = str_detect(player_name, "McIlroy"))

top10_both <- bind_rows(top10_2025, top10_2026)
r4_summary <- top10_both |>
  group_by(year) |>
  summarise(top10_r4_mean = round(mean(r4_score, na.rm = TRUE), 2),
            top10_r4_sg_mean = round(mean(r4_sg_total, na.rm = TRUE), 2),
            mcilroy_r4_score = r4_score[is_mcilroy],
            mcilroy_r4_sg = r4_sg_total[is_mcilroy],
            mcilroy_vs_top10_score = round(mcilroy_r4_score - top10_r4_mean, 2),
            mcilroy_vs_top10_sg = round(mcilroy_r4_sg - top10_r4_sg_mean, 2))

print(r4_summary)
# A tibble: 2 × 7
  year  top10_r4_mean top10_r4_sg_mean mcilroy_r4_score mcilroy_r4_sg
  <chr>         <dbl>            <dbl>            <dbl>         <dbl>
1 2025          -0.91             0.99                1         -0.93
2 2026          -2.8              2.89               -1          1.09
# ℹ 2 more variables: mcilroy_vs_top10_score <dbl>, mcilroy_vs_top10_sg <dbl>
r4_sg_vars <- c("r4_sg_ott", "r4_sg_app", "r4_sg_arg", "r4_sg_putt")
r4_sg_labels <- c("Off the Tee", "Approach", "Around Green", "Putting")

top10_r4_long <- top10_both |>
  select(player_name, year, is_mcilroy, all_of(r4_sg_vars)) |>
  pivot_longer(cols = all_of(r4_sg_vars),
               names_to  = "component",
               values_to = "sg") |>
  mutate(component = factor(component,
                            levels = r4_sg_vars,
                            labels = r4_sg_labels))

mcilroy_r4_long <- top10_r4_long |> filter(is_mcilroy)
field_r4_long   <- top10_r4_long |> filter(!is_mcilroy)

ggplot() +
  geom_jitter(data = field_r4_long,
              aes(x = sg,
                  y = component,
                  color = year),
              width = 0, height = 0.15,
              alpha = 0.5, size = 3) +
  geom_point(
    data = field_r4_long |>
      group_by(year, component) |>
      summarise(mean_sg = mean(sg, na.rm = TRUE), .groups = "drop"),
    aes(x = mean_sg,
        y = component,
        color = year),
    shape = 124, size = 8) +
  geom_point(data = mcilroy_r4_long,
             aes(x = sg,
                 y = component,
                 color = year),
             shape = 18, size = 7) +
  geom_text(data = mcilroy_r4_long,
            aes(x = sg,
                y = component,
                label = paste0("McIlroy '", substr(year, 3, 4))),
            vjust = -1, size = 3, fontface = "bold") +
  geom_vline(xintercept = 0, linetype = "dashed", color = "#E31B23") +
  scale_color_manual(values = c("2025" = "#006747", "2026" = "#EFBF04"),
                     name = "Year") +
  facet_wrap(~ year) +
  labs(title = "Round 4 SG Components: McIlroy vs. Top 10 Finishers",
       subtitle = "Diamond = McIlroy  |  Bar = Top 10 mean  |  Dots = individual top 10 players",
       x = "Strokes Gained (Round 4)",
       y = NULL) +
  theme_minimal() +
  theme(legend.position = "none")

The Round 4 summary table reveals our most important finding of the project. In 2025, the top 10 averaged -0.91 on Sunday with everyone finishing strong on the final stretch. McIlroy scored +1 and finished 1.91 stokes worse than his contenders on the last day. He won because he lead from Rounds 1 and 2 so he was able to absorb a difficult Sunday.

2025 2026
R4 SG Total -0.93 +1.09
Top 10 R4 Mean -0.91 -2.80
Top 10 R4 SG -1.92 -1.80

In 2026, the story was entirely different. The top 10 field had averaged -2.8 on Sunday giving us a collectively strong finish. McIlroy matched them at -1 and finished 1.8 strokes better than the field and ranked competitively within. His 2026 Sunday was far more composed relative to his peers.

Returning to the Approach

The Approach

Does McIlroy’s back-to-back Masters success reflect absolute improvement or relative dominance?

With the conclusion of Round 4 and these findings, we can directly answer our central question. McIlroy’s back-to-back success reflects on both absolute improvement and relative dominance but in different dimensions.

His ball-striking dominance represents genuine, stable elite-level ability that was present in both years. That is relative dominance: was was simply better than the field as Augusta, twice.

2025 2026
Total Score -11 -12
SG Total 3.43 3.55

The absolute improvement is concentrated in putting. Historically his worst skill in golf, he moved from the 41st percentile and statistical insignificant in 2025 to the 79th percentile and significance in 2026. Combined with the Round 4 data showing he closed more effectively relative to the field in 2026, the evidence suggests a player who was dominant in a more complete version of his game the second time around.

McIlroy evolved from a player carried by one or two dominant skills in 2025 to one whose second Green Jacket was earned through consistent excellence across the entire game.

Getty Images

Source Data

https://datagolf.com/past-results/majors/14