The table was created on May 20 ahead of the 2024 ACC Baseball Tournament that runs from May 21-May 26. The table shows the regular season standings used to generate the conference tournament seeding, a team’s latest ranking in the RPI and overall record, and run differentials by location in league play.
There are 14 teams that participate in ACC baseball. The top 12 teams in the standings qualify for the ACC Tournament. The league uses a pool play format featuring four pools of three teams. The winner of each pool advances to a single elimination bracket to determine the champion.
Fetch the data
The first step is fetch the results of every ACC team in league play only.
Code
# this is an expensive query, so going to save the ids as a vector# below is the code to fetch using baseballr# acc_teams <- baseballr::ncaa_teams(year = 2024, division = 1) |> # dplyr::filter(conference == "ACC")# acc_team_ids <- as.numeric(acc_teams$team_id)acc_team_ids <-c(67, 147, 193, 234, 255, 367, 415, 490, 457, 513, 545, 746, 742, 749)
Next, a function is used to loop through all the teams and grab the results.
Code
# function to scrape schedule for league games onlyacc_results <-function(id) { baseballr::ncaa_schedule_info(team_id = id, year =2024) |> dplyr::filter(home_team_conference =="ACC"& away_team_conference =="ACC") |> dplyr::filter(!is.na(home_team_score))}# grab acc only gamesacc_scores <-lapply(acc_team_ids, acc_results)# fix an error in how UVA are spelled with spacesacc_results_2024 <-as.data.frame(do.call(rbind, acc_scores)) |> dplyr::distinct(contest_id, .keep_all =TRUE) |> dplyr::mutate(away_team = dplyr::if_else(away_team =="Virginia ", "Virginia", away_team))
Once we’ve generated the results, now we want to grab the specific run differentials for all the league games and the home/away records. This code is quite industrious because we pull home and away separately. It repeats itself, however, I find it easier to read.
Code
# find home run differential by team home_diffs <- acc_results_2024 |> dplyr::mutate(h_diff = home_team_score - away_team_score) |> dplyr::group_by(home_team) |> dplyr::summarize(home_diff =sum(h_diff)) |> dplyr::rename(team = home_team)# find away run differential by team away_diffs <- acc_results_2024 |> dplyr::mutate(a_diff = away_team_score - home_team_score) |> dplyr::group_by(away_team) |> dplyr::summarize(away_diff =sum(a_diff)) |> dplyr::rename(team = away_team)# combine the tables for differentials full_diffs <-merge(home_diffs, away_diffs, by ="team")|> dplyr::mutate(full_diff = (home_diff + away_diff))|> dplyr::select(team, full_diff, home_diff, away_diff)# find home conference records by teamhome_results <- acc_results_2024 |> dplyr::mutate(h_result = dplyr::if_else(home_team_score > away_team_score, "W", "L")) |> dplyr::group_by(home_team, h_result) |> dplyr::count() |> tidyr::pivot_wider( names_from = h_result,values_from = n)|> dplyr::rename(team = home_team, h_w = W, h_l = L)# find away conference records by team# some teams have not one a game on the road, so we need to replace NAs with 0saway_results <- acc_results_2024 |> dplyr::mutate(a_result = dplyr::if_else(away_team_score > home_team_score, "W", "L")) |> dplyr::group_by(away_team, a_result) |> dplyr::count() |> tidyr::pivot_wider( names_from = a_result,values_from = n) |> dplyr::rename(team = away_team, a_w = W, a_l = L) |> dplyr::mutate(across(everything(), ~replace(.x, is.na(.x), 0))) # join the results to compile them togetherfull_recs <-merge(home_results, away_results, by ="team") |> dplyr::mutate(W = (h_w + a_w), L = (h_l + a_l))|> dplyr::select(team, W, L, h_w, h_l, a_w, a_l)# add in the win percentage overall for the league records diffs_recs <-merge(full_diffs, full_recs, by ="team") |> dplyr::mutate(win_pct =round(W / (W + L), digits =3)) |> dplyr::arrange(-win_pct)
Next, we’re going to write a function to scrape the RPI table from ncaa.com. This is helpful to grab the team’s overall record, plus it adds context to how each conference team ranks nationally.
The last step is to hard code the pool play data. This is easier to do manually, and it forces me to double-check my work. Now, we’ll merge the data and generate our final table.
Code
# add pools play data from ACC.com pool_play <- tibble::tribble(~team,~pool,"North Carolina","Pool A","Wake Forest","Pool A","Pittsburgh","Pool A", "Boston College","Did not qualify","Notre Dame","Did not qualify","Clemson","Pool B","Louisville","Pool B","Miami (FL)","Pool B","NC State","Pool C","Duke","Pool C","Virginia Tech","Pool C","Virginia","Pool D","Florida St.","Pool D","Georgia Tech","Pool D") total_records <-merge(acc_rpi, diffs_recs, by ="team") |> dplyr::left_join(pool_play, by ="team") |> dplyr::mutate(overall =paste0(o_w, "-", o_l, " | ", "RPI:", rpi),acc_rec =paste0(W, "-", L),home_rec =paste0(h_w, "-", h_l),away_rec =paste0(a_w, "-", a_l), ) |> dplyr::select( team, overall, pool, acc_rec, win_pct, full_diff, home_rec, home_diff, away_rec, away_diff )
Create the static HTML table
Now, we’re ready to generate the table. We’re going to modify the theme and use several useful functions from the {gtExtras} package.
This also uses the {cbbplotR} package to add in logos of the teams.
Shows regular season standings used to determine tournament seeding and pools, plus RPI, overall record, and league records by location with run differentials.
Tournament Seeds & Pools
Overall
Home
Away
W-L
Win %
+/-
W-L
+/-
W-L
+/-
1
North Carolina
41-12 | RPI:2
Pool A
22-8
0.733
+95
14-1
+74
8-7
+21
2
Clemson
40-13 | RPI:6
Pool B
20-10
0.667
+54
11-4
+46
9-6
+8
3
North Carolina St.
33-19 | RPI:13
Pool C
18-11
0.621
-6
13-2
+20
5-9
-26
4
Virginia
41-14 | RPI:9
Pool D
18-12
0.600
+42
9-6
-2
9-6
+44
5
Florida St.
40-14 | RPI:8
Pool D
17-12
0.586
+40
11-3
+39
6-9
+1
6
Duke
36-18 | RPI:22
Pool C
16-14
0.533
+31
8-7
+8
8-7
+23
7
Louisville
32-23 | RPI:58
Pool B
16-14
0.533
-51
9-6
-17
7-8
-34
8
Wake Forest
37-19 | RPI:11
Pool A
15-15
0.500
+17
8-7
+12
7-8
+5
9
Georgia Tech
31-23 | RPI:48
Pool D
15-15
0.500
+12
9-6
+29
6-9
-17
10
Virginia Tech
32-22 | RPI:68
Pool C
14-16
0.467
-9
7-8
+8
7-8
-17
11
Miami FL
26-29 | RPI:87
Pool B
11-19
0.367
-38
7-8
+1
4-11
-39
12
Pittsburgh
26-28 | RPI:89
Pool A
10-20
0.333
-70
5-10
-54
5-10
-16
13
Notre Dame
27-25 | RPI:76
Did not qualify
9-21
0.300
-31
9-6
+43
0-15
-74
14
Boston College
22-31 | RPI:85
Did not qualify
8-22
0.267
-86
3-12
-50
5-10
-36
Top 12 teams qualify for ACC Tournament in Charlotte. Pool-play format, winner of each pool plays in a four-team, single-elimination bracket to determine champion.
Table by Chris (@dadgumboxscores) + Bless your chart | data via baseballr and ncaa.org through May 18 games