Andre Ethier versus Brad Lidge

Author

Derek Sollberger

Published

July 16, 2025

library("ggtext")
library("Lahman")
library("retrosheet")
library("tidyverse")

Setting the Scene

I am finally reading Analyzing Baseball Data with R, and thankfully there is a book club over at the DSLC Slack channel (Data Science Learning Community). I want to apply our new skills to a particular half-inning that is cherished in my memories. In particular, I want to see if I can find a particular at-bat by Andre Ethier where he faced Brad Lidge in the bottom of the ninth

Brad Lidge

Andre Ethier

After volunteering to present the chapter on Quarto for our book club, I have decided to further explore the retrosheet package for R users.

Data Sets

Lahman

The Lahman data set can help us find overall information through its People, Batting, Pitching, Fielding, and Teams tables (and this data set is more extensive than I remember). First, let us find certain players in the People table:

Lahman::People |>
  filter(nameLast %in% c("Ethier", "Lidge", "Werth"))
   playerID birthYear birthMonth birthDay   birthCity birthCountry birthState
1 ethiean01      1982          4       10     Phoenix          USA         AZ
2 lidgebr01      1976         12       23  Sacramento          USA         CA
3 werthde01      1952         12       29     Lincoln          USA         IL
4 werthja01      1979          5       20 Springfield          USA         IL
  deathYear deathMonth deathDay deathCountry deathState deathCity nameFirst
1        NA         NA       NA         <NA>       <NA>      <NA>     Andre
2        NA         NA       NA         <NA>       <NA>      <NA>      Brad
3        NA         NA       NA         <NA>       <NA>      <NA>    Dennis
4        NA         NA       NA         <NA>       <NA>      <NA>    Jayson
  nameLast            nameGiven weight height bats throws      debut   bbrefID
1   Ethier        Andre Everett    210     74    L      L 2006-05-02 ethiean01
2    Lidge       Bradley Thomas    210     77    R      R 2002-04-26 lidgebr01
3    Werth          Dennis Dean    200     73    R      R 1979-09-17 werthde01
4    Werth Jayson Richard Gowan    235     77    R      R 2002-09-01 werthja01
   finalGame  retroID deathDate  birthDate
1 2017-10-01 ethia001      <NA> 1982-04-10
2 2012-06-16 lidgb001      <NA> 1976-12-23
3 1982-09-27 wertd101      <NA> 1952-12-29
4 2017-10-01 wertj001      <NA> 1979-05-20

We will need these player IDs (the latter one in the case of “Werth”). I will now try to look up Ethier, Lidge, and Werth in the Batting, Pitching, and Fielding tables respectively.

Lahman::Batting |>
  filter(playerID == "ethiean01")
    playerID yearID stint teamID lgID   G  AB  R   H X2B X3B HR RBI SB CS BB
1  ethiean01   2006     1    LAN   NL 126 396 50 122  20   7 11  55  5  5 34
2  ethiean01   2007     1    LAN   NL 153 447 50 127  32   2 13  64  0  4 46
3  ethiean01   2008     1    LAN   NL 141 525 90 160  38   5 20  77  6  3 59
4  ethiean01   2009     1    LAN   NL 160 596 92 162  42   3 31 106  6  4 72
5  ethiean01   2010     1    LAN   NL 139 517 71 151  33   1 23  82  2  1 59
6  ethiean01   2011     1    LAN   NL 135 487 67 142  30   0 11  62  0  1 58
7  ethiean01   2012     1    LAN   NL 149 556 79 158  36   1 20  89  2  2 50
8  ethiean01   2013     1    LAN   NL 142 482 54 131  33   2 12  52  4  3 61
9  ethiean01   2014     1    LAN   NL 130 341 29  85  17   6  4  42  2  2 31
10 ethiean01   2015     1    LAN   NL 142 395 54 116  20   7 14  53  2  3 43
11 ethiean01   2016     1    LAN   NL  16  24  2   5   1   0  1   2  0  0  2
12 ethiean01   2017     1    LAN   NL  22  34  3   8   1   0  2   3  0  0  4
    SO IBB HBP SH SF GIDP
1   77   2   5  0  6   11
2   68  12   4  0  8   10
3   88   0   4  1  7    6
4  116  10  13  0  4   19
5  102  11   3  0  6   11
6  103   9   3  0  3    8
7  124   6   9  0  3   13
8   95  11   7  0  3    9
9   74   3   6  1  1    5
10  75   2   4  0  3   11
11   6   1   0  0  0    1
12  10   1   0  0  0    0
Lahman::Pitching |>
  filter(playerID == "lidgebr01")
    playerID yearID stint teamID lgID W L  G GS CG SHO SV IPouts  H ER HR BB
1  lidgebr01   2002     1    HOU   NL 1 0  6  1  0   0  0     26 12  6  0  9
2  lidgebr01   2003     1    HOU   NL 6 3 78  0  0   0  1    255 60 34  6 42
3  lidgebr01   2004     1    HOU   NL 6 5 80  0  0   0 29    284 57 20  8 30
4  lidgebr01   2005     1    HOU   NL 4 4 70  0  0   0 42    212 58 18  5 23
5  lidgebr01   2006     1    HOU   NL 1 5 78  0  0   0 32    225 69 44 10 36
6  lidgebr01   2007     1    HOU   NL 5 3 66  0  0   0 19    201 54 25  9 30
7  lidgebr01   2008     1    PHI   NL 2 0 72  0  0   0 41    208 50 15  2 35
8  lidgebr01   2009     1    PHI   NL 0 8 67  0  0   0 31    176 72 47 11 34
9  lidgebr01   2010     1    PHI   NL 1 1 50  0  0   0 27    137 32 15  5 24
10 lidgebr01   2011     1    PHI   NL 0 2 25  0  0   0  1     58 16  3  0 13
11 lidgebr01   2012     1    WAS   NL 0 1 11  0  0   0  2     28 12 10  1 11
    SO BAOpp  ERA IBB WP HBP BK BFP GF  R SH SF GIDP
1   12 0.333 6.23   1  0   2  0  48  2  6  1  0    1
2   97 0.202 3.60   7  4   5  1 349  9 36  2  3    6
3  157 0.174 1.90   5  3   6  1 369 44 21  3  2    5
4  103 0.223 2.29   1  8   3  0 291 65 21  4  1    1
5  104 0.238 5.28   4 11   6  0 340 52 47  6  2    2
6   88 0.219 3.36   4  6   4  0 287 34 29  5  1    3
7   92 0.198 1.95   4  5   1  0 292 61 17  2  1    3
8   61 0.301 7.21   3  4   5  0 283 55 51  4  1    4
9   52 0.194 2.96   4  3   1  1 193 38 16  3  0    2
10  23 0.225 1.40   3  2   1  0  86  4  3  1  0    1
11  10 0.308 9.64   5  0   0  0  51  4 10  1  0    0
Lahman::Fielding |>
  filter(playerID == "werthja01")
    playerID yearID stint teamID lgID POS   G  GS InnOuts  PO  A E DP PB WP SB
1  werthja01   2002     1    TOR   AL  OF  15  13     363  33  1 0  0 NA NA NA
2  werthja01   2003     1    TOR   AL  OF  20  11     324  27  1 0  0 NA NA NA
3  werthja01   2004     1    LAN   NL  OF  79  70    1932 146  6 4  2 NA NA NA
4  werthja01   2005     1    LAN   NL  OF 101  96    2493 218  6 3  2 NA NA NA
5  werthja01   2007     1    PHI   NL  1B   1   0       3   1  0 0  0 NA NA NA
6  werthja01   2007     1    PHI   NL  OF  76  63    1727 147  9 2  2 NA NA NA
7  werthja01   2008     1    PHI   NL  OF 125 103    2898 231  9 2  2 NA NA NA
8  werthja01   2009     1    PHI   NL  OF 157 152    4138 353 11 6  4 NA NA NA
9  werthja01   2010     1    PHI   NL  OF 153 150    4026 293  8 4  2 NA NA NA
10 werthja01   2011     1    WAS   NL  OF 149 148    3973 342 11 8  4 NA NA NA
11 werthja01   2012     1    WAS   NL  OF  79  79    2076 166  4 1  1 NA NA NA
12 werthja01   2013     1    WAS   NL  OF 126 126    3216 235  7 2  1 NA NA NA
13 werthja01   2014     1    WAS   NL  OF 139 137    3662 247  8 5  1 NA NA NA
14 werthja01   2015     1    WAS   NL  OF  86  84    2162 113  3 2  0 NA NA NA
15 werthja01   2016     1    WAS   NL  OF 133 133    3474 201  5 1  1 NA NA NA
16 werthja01   2017     1    WAS   NL  OF  67  67    1655 116  1 4  0 NA NA NA
   CS ZR
1  NA NA
2  NA NA
3  NA NA
4  NA NA
5  NA NA
6  NA NA
7  NA NA
8  NA NA
9  NA NA
10 NA NA
11 NA NA
12 NA NA
13 NA NA
14 NA NA
15 NA NA
16 NA NA
Brad Lidge

“Bradley Thomas Lidge (born December 23, 1976), nicknamed”Lights Out”,[1] is an American former professional baseball pitcher. Lidge played 11 seasons in Major League Baseball (MLB), from 2002–2012. He played for the Houston Astros, Philadelphia Phillies, and Washington Nationals. As a relief pitcher Lidge saved 225 games during his career. He was a two-time All-Star, and in 2008 won the Delivery Man of the Year Award and the National League (NL) Rolaids Relief Man Award. Lidge was a host on SiriusXM’s MLB Network Radio.

“Lidge threw a four-seam fastball that consistently reached 95–97 miles per hour, as well as a hard, sharp breaking slider that ranged from 85 to 87 mph. He also had a cutter of the variation. He sealed the Phillies’ 2008 World Series championship with the final out, a strikeout of Eric Hinske in Game 5.” —Wikipedia

Andre Ethier

“Andre Everett Ethier (/ˈiːθiər/; born April 10, 1982) is an American former professional baseball outfielder. He played in Major League Baseball (MLB) for the Los Angeles Dodgers from 2006 to 2017 and is second all-time in post-season appearances as a Dodger with 51.

“Drafted in the second round (62nd pick overall) of the 2003 MLB draft by the Oakland Athletics, Ethier played in the major leagues from 2006 through 2017, all for the Dodgers. Career highlights include All-Star selections in 2010 and 2011, a Silver Slugger Award in 2009, and a Gold Glove Award in 2011. Primarily a right fielder throughout his career, Ethier also filled in at left field and center field for the Dodgers. Due to leg and back injuries, he had only 58 at bats in the 2016 and 2017 regular seasons, combined.” —Wikipedia

Retrosheet

Let us try out the retrosheet package (by Colin Douglas and Richard Scriven) to get game data from each of those four seasons where Brad Lidge was pitching for the Philadelphia Phillies. Note: we probably do not have to concern ourselves with mid-season trades in this case study.

season_2008 <- retrosheet::get_retrosheet("game", 2008)
season_2009 <- retrosheet::get_retrosheet("game", 2009)
season_2010 <- retrosheet::get_retrosheet("game", 2010)
season_2011 <- retrosheet::get_retrosheet("game", 2011)

Statcast

As recommended by the textbook authors in section C.2, we can load a list of player IDs for Statcast data (takes a few seconds to load and download)

chadwick_id <- baseballr::chadwick_player_lu()
readr::write_rds(chadwick_id, "data/chadwick_id.rds")
chadwick_id <- readr::read_rds("data/chadwick_id.rds")

statcast_ids <- chadwick_id |>
  filter(name_last %in% c("Ethier", "Lidge", "Werth")) |>
  select(key_mlbam, name_last, name_first, mlb_played_first, mlb_played_last) |>
  filter(!is.na(mlb_played_first))
readr::write_rds(statcast_ids, "data/statcast_ids.rds")

Here, I kept a subset of the data rather than keeping a 100 MB file in our GitHub repo.

statcast_ids <- readr::read_rds("data/statcast_ids.rds")
statcast_ids
# A tibble: 4 × 5
  key_mlbam name_last name_first mlb_played_first mlb_played_last
      <int> <chr>     <chr>                 <int>           <int>
1    400058 Lidge     Brad                   2002            2012
2    124099 Werth     Dennis                 1979            1982
3    444843 Ethier    Andre                  2006            2017
4    150029 Werth     Jayson                 2002            2017

Match Ups

Retrosheet

For this case study, we can use the filter command from the dplyr to subset our data where

  • visiting team: Philadelphia Phillies
  • home team: Los Angeles Dodgers
lidge_phillies_dodgers <- rbind(season_2008, season_2009, season_2010, season_2011) |>
  dplyr::filter(VisTm == "PHI" & HmTm == "LAN")
lidge_phillies_dodgers |> select(Date, VisTm, HmTm, VisRuns, HmRuns, Attendance)
         Date VisTm HmTm VisRuns HmRuns Attendance
1  2008-08-11   PHI  LAN       6      8      45547
2  2008-08-12   PHI  LAN       3      4      47586
3  2008-08-13   PHI  LAN       6      7      45786
4  2008-08-14   PHI  LAN       1      3      51060
5  2009-06-04   PHI  LAN       3      0      33839
6  2009-06-05   PHI  LAN       3      4      52538
7  2009-06-06   PHI  LAN       2      3      41412
8  2009-06-07   PHI  LAN       7      2      42288
9  2010-08-30   PHI  LAN       0      3      44896
10 2010-08-31   PHI  LAN       8      4      45164
11 2010-09-01   PHI  LAN       5      1      37080
12 2011-08-08   PHI  LAN       5      3      35380
13 2011-08-09   PHI  LAN       2      1      46547
14 2011-08-10   PHI  LAN       9      8      41807

Statcast

In addition to the calendar dates found above, we can try to get pitch-by-pitch data from the statcast_search function in the baseballr package.

lidge1 <- baseballr::statcast_search(start_date = "2008-08-11", end_date = "2008-08-11", playerid = 400058)

I forgot that Statcast data only dates back to 2015

Schedule

Why June 5, 2009?

For brevity, I looked up the game in particular with a simple search of Baseball Reference. Other dplyr filters could have been applied after more data wrangling.

schedule_2009 <- retrosheet::getRetrosheet("schedule", 2009)
schedule_2009 |> filter(Date == "20090605")
       Date GameNo Day VisTeam VisLg VisGmNo HmTeam HmLg HmGmNo TimeOfDay
1  20090605      0 Fri     MIL    NL      55    ATL   NL     55         N
2  20090605      0 Fri     TEX    AL      54    BOS   AL     55         N
3  20090605      0 Fri     CLE    AL      57    CHA   AL     56         N
4  20090605      0 Fri     CHN    NL      55    CIN   NL     55         N
5  20090605      0 Fri     ANA    AL      55    DET   AL     55         N
6  20090605      0 Fri     SFN    NL      53    FLO   NL     56         N
7  20090605      0 Fri     PIT    NL      56    HOU   NL     55         N
8  20090605      0 Fri     PHI    NL      55    LAN   NL     57         N
9  20090605      0 Fri     TBA    AL      57    NYA   AL     57         N
10 20090605      0 Fri     BAL    AL      55    OAK   AL     55         N
11 20090605      0 Fri     ARI    NL      56    SDN   NL     55         N
12 20090605      0 Fri     MIN    AL      56    SEA   AL     55         N
13 20090605      0 Fri     COL    NL      54    SLN   NL     57         N
14 20090605      0 Fri     KCA    AL      54    TOR   AL     57         N
15 20090605      0 Fri     NYN    NL      55    WAS   NL     55         N
   Postponed   Makeup
1                  NA
2                  NA
3                  NA
4                  NA
5                  NA
6                  NA
7                  NA
8                  NA
9       Rain 20090907
10                 NA
11                 NA
12                 NA
13                 NA
14                 NA
15                 NA

Standings

2008 Season

In the previous season,

Lahman::Teams |>
  filter(yearID == 2008) |>
  filter(lgID == "NL") |>
  select(yearID, teamID, W, L, DivWin, WCWin, LgWin, WSWin) |>
  arrange(desc(W)) |>
  filter(W > 81)
  yearID teamID  W  L DivWin WCWin LgWin WSWin
1   2008    CHN 97 64      Y     N     N     N
2   2008    PHI 92 70      Y     N     Y     Y
3   2008    MIL 90 72      N     Y     N     N
4   2008    NYN 89 73      N     N     N     N
5   2008    HOU 86 75      N     N     N     N
6   2008    SLN 86 76      N     N     N     N
7   2008    FLO 84 77      N     N     N     N
8   2008    LAN 84 78      Y     N     N     N
9   2008    ARI 82 80      N     N     N     N

the Dodgers won the NL West (with a relatively low amount of wins), lost to the Phillies in the NLCS, and then Phillies went on to defeat the Tampa Bay Rays in the World Series.

2009 Season

standings_2009 <- season_2009 |>
  select(Date, VisTm, HmTm, VisRuns, HmRuns) |>
  mutate(WinTm = ifelse(VisRuns > HmRuns, VisTm, HmTm)) |>
  count(WinTm, Date, name = "TeamWins") |>
  group_by(WinTm) |>
  mutate(TeamWins = cumsum(TeamWins)) |>
  ungroup()

title_text <- "<span style='color:#E81828;'>Philadelphia Phillies</span> at <span style='color:#005A9C;'>Los Angeles Dodgers</span>"

standings_2009 |>
  ggplot(aes(x = Date, y = TeamWins, group = WinTm)) +
  # geom_vline(xintercept = "2009-06-05") +
  geom_line(aes(x = Date, y = TeamWins, group = WinTm),
            color = "gray70",
            data = standings_2009 |> filter(!WinTm %in% c("LAN", "PHI") & Date < "2009-06-05")) +
  geom_line(aes(x = Date, y = TeamWins),
            color = "#005A9C",
            data = standings_2009 |> 
              filter(WinTm == "LAN" & Date < "2009-06-05"),
            linewidth = 3) +
  geom_line(aes(x = Date, y = TeamWins),
            color = "#E81828",
            data = standings_2009 |>
              filter(WinTm == "PHI" & Date < "2009-06-05"),
            linewidth = 3) +
  geom_point(aes(x = Date, y = TeamWins),
            color = "#005A9C",
            data = standings_2009 |> 
              filter(WinTm == "LAN" & Date == "2009-06-03"),
            size = 5) +
  geom_point(aes(x = Date, y = TeamWins),
            color = "#E81828",
            data = standings_2009 |>
              filter(WinTm == "PHI" & Date == "2009-06-04"),
            size = 5) +
  labs(title = title_text,
       subtitle = "June 5, 2009",
       y = "Wins") +
  theme_minimal() +
  theme(plot.title = ggtext::element_markdown())

Play by Play

Retrosheet play-by-play data

dodgers_2009 <- retrosheet::getRetrosheet("play",  2009, "LAN")
#length(dodgers_2009) # 81 home games
#class(dodgers_2009)  # list
str(dodgers_2009[[1]])
List of 8
 $ id     : chr "LAN200904130"
 $ version: chr "2"
 $ info   : chr [1:26, 1:2] "visteam" "hometeam" "site" "date" ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "category" "info"
 $ start  : chr [1:18, 1:5] "winnr001" "rente001" "lewif001" "molib001" ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:5] "retroID" "name" "team" "batPos" ...
 $ play   : chr [1:92, 1:6] "1" "1" "1" "1" ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:6] "inning" "team" "retroID" "count" ...
 $ com    : chr(0) 
 $ sub    : chr [1:17, 1:5] "valdm003" "velee001" "meddb001" "velee001" ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:5] "retroID" "name" "team" "batPos" ...
 $ data   : chr [1:8, 1:3] "er" "er" "er" "er" ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:3] "projCode" "retroID" "ER"
num_home <- length(dodgers_2009)
for(game_index in 1:num_home){
  if(dodgers_2009[[game_index]]$id == "LAN200906050"){
    print(paste0("Home game number: ", game_index))
    break
  }
}
[1] "Home game number: 28"
dodgers200906050 <- dodgers_2009[[28]]
game_df <- dodgers200906050$play |> as.data.frame()

First Inning

game_df |> filter(inning == 1)
  inning team  retroID count   pitches             play
1      1    0 rollj001    10        BX           5/F56-
2      1    0 utlec001    01        CX         S5/G56S-
3      1    0 wertj001    32   BCSBB>B            W.1-2
4      1    0 howar001    02       SFS                K
5      1    0 ibanr001    02       CFX           4/F4S-
6      1    1 pierj002    21      CBBX          S7/G56+
7      1    1 furcr001    00      11>B              SB2
8      1    1 furcr001    31 11>B.CBBX 4/P3F/DP.2X3(45)
9      1    1 hudso001    20       BBX          9/F89D+

Run-Scoring Plays

game_df |> 
  filter((str_detect(retroID, "iba") & inning == 3) |
           (str_detect(retroID, "utl") & inning == 4) |
           (str_detect(retroID, "hud") & inning == 4) |
           (str_detect(retroID, "lon") & inning == 7))
  inning team  retroID count pitches                     play
1      3    0 ibanr001    11     FBX         36(1)/FO/G4S.3-H
2      4    0 utlec001    01      FX D9/L9LM+.2-H(UR);1-H(UR)
3      4    1 hudso001    20    *BBX              63/G6MD.3-H
4      7    1 lonej001    10      BX             S9/F89S-.2-H

At this time, I simply used the parsed results at Baseball Reference

  • top 3rd: Raul Ibanez RBI groundout (PHI 1, LAD 0)
  • top 4th: Chase Utley 2-RBI double (PHI 3, LAD 0)
  • bottom 4th: Orlando Hudson RBI groundout (PHI 3, LAD 1)
  • bottom 7th: James Loney RBI single (PHI 3, LAD 2)

Bottom of the Ninth

game_df |> 
  filter(inning == 9 & team == 1)

With the save situation, Philadelphia sent their closer Brad Lidge to the mound to face the heart of the Dodger lineup.

Brad Lidge

Rafael Furcal
game_df |> 
  filter(inning == 9 & team == 1) |>
  filter(str_detect(retroID, "fur"))
  inning team  retroID count pitches play
1      9    1 furcr001    00           NP
2      9    1 furcr001    32 .CCBBBC    K

After two quick strikes, Furcal worked up to a full count. Lidge was able to get Furcal to strikeout looking.

One out

Orlando Hudson
game_df |> 
  filter(inning == 9 & team == 1) |>
  filter(str_detect(retroID, "hud"))
  inning team  retroID count pitches    play
1      9    1 hudso001    12    CBCX 13/G13-

On a 1-2 count, Hudson grounded out to the pitcher.

Two outs

Casey Blake
game_df |> 
  filter(inning == 9 & team == 1) |>
  filter(str_detect(retroID, "bla"))
  inning team  retroID count pitches    play
1      9    1 blakc001    12    CBSX S7/L78S

After a 1-2 count, Casey Blake singled to left field.

Runner on first

James Loney
game_df |> 
  filter(inning == 9 & team == 1) |>
  filter(str_detect(retroID, "lon"))
  inning team  retroID count    pitches  play
1      9    1 lonej001    32 CBS*BF*B>B W.1-2
  • called strike
  • ball
  • swinging strike (blocked by catcher)
  • ball
  • foul (blocked by catcher)
  • ball (Blake faked steal attempt)
  • ball (Blake to 2B)

Clean-up hitter James Loney worked a walk.

Runners on first and second

Russel Martin
game_df |> 
  filter(inning == 9 & team == 1) |>
  filter(str_detect(retroID, "mar"))
  inning team  retroID count pitches           play
1      9    1 martr004    12   S*BCX E5/G5+.2-3;1-2

After a 1-2 count, Martin lightly hit a squibber, but the third baseman committed an error!

Bases loaded

Andre Ethier
game_df |> 
  filter(inning == 9 & team == 1) |>
  filter(str_detect(retroID, "eth"))
  inning team  retroID count pitches                         play
1      9    1 ethia001    00       X D9/F9LD+.3-H(UR);2-H(UR);1-3

On the very next pitch, Ethier hit a long double down the right-field line for a walk-off victory!

Dodgers win!

Epilogue

title_text <- "<span style='color:#E81828;'>Philadelphia Phillies</span> at <span style='color:#005A9C;'>Los Angeles Dodgers</span>"

standings_2009 |>
  ggplot(aes(x = Date, y = TeamWins, group = WinTm)) +
  # geom_vline(xintercept = "2009-06-05") +
  geom_line(aes(x = Date, y = TeamWins, group = WinTm),
            color = "gray70",
            data = standings_2009 |> filter(!WinTm %in% c("LAN", "PHI"))) +
  geom_line(aes(x = Date, y = TeamWins),
            alpha = 0.5,
            color = "#005A9C",
            data = standings_2009 |> filter(WinTm == "LAN"),
            linewidth = 2) +
  geom_line(aes(x = Date, y = TeamWins),
            alpha = 0.5,
            color = "#E81828",
            data = standings_2009 |> filter(WinTm == "PHI"),
            linewidth = 2) +
  geom_line(aes(x = Date, y = TeamWins),
            color = "#005A9C",
            data = standings_2009 |> 
              filter(WinTm == "LAN" & Date < "2009-06-05"),
            linewidth = 3) +
  geom_line(aes(x = Date, y = TeamWins),
            color = "#E81828",
            data = standings_2009 |>
              filter(WinTm == "PHI" & Date < "2009-06-05"),
            linewidth = 3) +
  geom_point(aes(x = Date, y = TeamWins),
            color = "#005A9C",
            data = standings_2009 |> 
              filter(WinTm == "LAN" & Date == "2009-06-05"),
            size = 5) +
  geom_point(aes(x = Date, y = TeamWins),
            color = "#E81828",
            data = standings_2009 |>
              filter(WinTm == "PHI" & Date == "2009-06-04"),
            size = 5) +
  labs(title = title_text,
       subtitle = "June 5, 2009",
       y = "Wins") +
  theme_minimal() +
  theme(plot.title = ggtext::element_markdown())
Lahman::Teams |>
  filter(yearID == 2009) |>
  filter(lgID == "NL") |>
  select(yearID, teamID, W, L, DivWin, WCWin, LgWin, WSWin) |>
  arrange(desc(W)) |>
  filter(W > 81)
  yearID teamID  W  L DivWin WCWin LgWin WSWin
1   2009    LAN 95 67      Y     N     N     N
2   2009    PHI 93 69      Y     N     Y     N
3   2009    COL 92 70      N     Y     N     N
4   2009    SLN 91 71      Y     N     N     N
5   2009    SFN 88 74      N     N     N     N
6   2009    FLO 87 75      N     N     N     N
7   2009    ATL 86 76      N     N     N     N
8   2009    CHN 83 78      N     N     N     N

The Dodgers and Phillies would face off in the NLCS again in 2009—with the Phillies once again defeating the Dodgers 4 games to one. The Phillies would not repeat as champions in the World Series, however, as that season was won by the New York Yankees.

sessionInfo()
R version 4.5.1 (2025-06-13 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 10 x64 (build 19045)

Matrix products: default
  LAPACK version 3.12.1

locale:
[1] LC_COLLATE=English_United States.utf8 
[2] LC_CTYPE=English_United States.utf8   
[3] LC_MONETARY=English_United States.utf8
[4] LC_NUMERIC=C                          
[5] LC_TIME=English_United States.utf8    

time zone: America/New_York
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] lubridate_1.9.4  forcats_1.0.0    stringr_1.5.1    dplyr_1.1.4     
 [5] purrr_1.1.0      readr_2.1.5      tidyr_1.3.1      tibble_3.3.0    
 [9] ggplot2_3.5.2    tidyverse_2.0.0  retrosheet_1.1.6 Lahman_12.0-0   
[13] ggtext_0.1.2    

loaded via a namespace (and not attached):
 [1] utf8_1.2.6         generics_0.1.4     xml2_1.3.8         stringi_1.8.7     
 [5] hms_1.1.3          digest_0.6.37      magrittr_2.0.3     evaluate_1.0.4    
 [9] grid_4.5.1         timechange_0.3.0   RColorBrewer_1.1-3 fastmap_1.2.0     
[13] jsonlite_2.0.0     httr_1.4.7         rvest_1.0.4        scales_1.4.0      
[17] cli_3.6.5          rlang_1.1.6        litedown_0.7       commonmark_2.0.0  
[21] withr_3.0.2        yaml_2.3.10        tools_4.5.1        tzdb_0.5.0        
[25] curl_6.4.0         vctrs_0.6.5        R6_2.6.1           lifecycle_1.0.4   
[29] htmlwidgets_1.6.4  pkgconfig_2.0.3    pillar_1.11.0      gtable_0.3.6      
[33] glue_1.8.0         Rcpp_1.1.0         xfun_0.52          tidyselect_1.2.1  
[37] rstudioapi_0.17.1  knitr_1.50         farver_2.1.2       htmltools_0.5.8.1 
[41] rmarkdown_2.29     labeling_0.4.3     compiler_4.5.1     markdown_2.0      
[45] gridtext_0.1.5