A Look at the Recent (and Forthcoming) Performance of the Milwaukee Bucks basketball club (as of March 23rd, 2024)
I love data and basketball, specifically the Milwaukee Bucks, as I am originally from Whitefish Bay, a suburb of Milwaukee, Wisconsin. Thought it might be fun to combine these elements together for a personal project. Below we'll dig into a couple different datasets to examine both recent performance of the Bucks, and also look ahead to the playoffs and see if we can sniff out how Milwaukee might perform.
Thank you to Stathead for the underlying data, which I combined into single CSVs for use in this project. Additional shoutout to basketball-reference.com; countless searches over the years has nurtured this hoops appreciation and interest. Let's get it!
Recent Form
One of our datasets is a table of all player game logs from this season for the Bucks players. This includes dates and outcomes of games, whether a player started or not, and the requisite counting/shooting stats.
Taking a page from soccer's playbook, let's look at the Bucks recent form (last 5 specifically) and individual player/overall performance.
/*SELECT SUM(CASE WHEN Recent_Form='W' THEN 1 ELSE 0 END) AS Ws,
SUM(CASE WHEN Recent_Form='L' THEN 1 ELSE 0 END) AS Ls
FROM*/
--Can make the below a subquery as part of a wrapper query to count Ws and Ls in the last 5 games.
(SELECT LEFT(Date,10) AS Date,
(CASE WHEN Result LIKE 'W%'
THEN 'W'
WHEN Result LIKE 'L%'
THEN 'L'
END) AS Recent_Form,
(CASE WHEN HomeAway='@' THEN CONCAT(Opp,', Away Game')
WHEN HomeAway IS NULL THEN CONCAT(Opp,', Home Game') END) AS Opponent
FROM 'bucks_player_gamelogs.csv'
GROUP BY Date, Result,Team,Opp,HomeAway
ORDER BY Date DESC
--Changing the limit below will change how many games back we're looking
LIMIT 5)
--Above subquery pulls last five games with date and column for W or L depending on a win or loss.
;
The above dataframe contains two queries but let's look at the subquery first. We grab the date, set a W or L depending on whether the game was a win or loss, and note the other team played. Note that since we do not know whether these games are home or away games, we have to account for the possibility of Milwaukee being either or, and in each case appropriately select the other team. The result clearly articulates recent performance; three wins against Brooklyn, Phoenix, and Philly, and two losses against Boston and Sacramento.
If we want to articulate that home/away information, we can concatenate that to the opponent information by checking the 'HomeAway' column for an @. I went ahead and did that.
We can make this entire query a subquery too, and count the Ws and Ls of the last five. Extending the limit x will give us last x games. The more games we look at, only then does it become useful to use the outer query to count wins and losses. It's not really needed for only five games.
Let's move on to examine individual player performance in these last five games.
Bucks Players in the Last 5 Games
--We'll use the above query to refer to only the last five games, and look at how each player has performed in those last five games.
SELECT Player,
COUNT(Date) AS GamesPlayed,
ROUND(AVG(MP),1) AS MPG,
ROUND(AVG(FGPER),2) AS FGPerc,
ROUND(AVG(PER3P),2) AS ThreePointPerc,
ROUND(AVG(FTPER),2) AS FTPerc,
ROUND(AVG(TSPER),2) AS TS,
ROUND(AVG(PTS),1) AS PPG,
ROUND(AVG(TRB),1) AS RPG,
ROUND(AVG(AST),1) AS APG
FROM 'bucks_player_gamelogs.csv'
WHERE Date IN (SELECT DISTINCT Date FROM last_5)
GROUP BY Player
HAVING MPG>10
ORDER BY MPG DESC
;
Let's start by using the previous query to make sure we're only looking at gamelogs for the last five games, as a subquery in the WHERE clause. Then it's simply an exercise in averaging and rounding each of the stats we're interested in looking at. I pulled basic shooting percentages in addition to true shooting and basic counting stats in points, rebounds, and assists. I also pulled games played and minutes per game to get a feel for involvement in these last five, choosing to remove end-of-bench players with an arbitrary 10 MPG cap. Their minutes are too limited and their involvement only at the end of games when the result is a foregone conclusion necessitates their removal for a cleaner dataset.
Now that we have our dataset...let's dig into it!
Sorting first by true shooting (a stat that measures shooting ability arguably the most accurately, as it takes into account free throws in addition to field goals), Khris Middleton stands alone. It's worth noting he's only played two of the last five, as he's coming back from injury (having not played since early February), but in those two games, he's been sensational, averaging 22 points per game, 4.5 rebounds per game, and 6.5 assists per game (!) on 74% (!!) true shooting, including shooting nearly 60% from 3 (!!!)
Khris is a pivotal player for the Bucks (insert average of games started since he joined the team in 2013, throw in playoff numbers for 2021 Finals Run on top of that!) and these last two games have been extremely promising for him. If he can continue to stay healthy and act as the Bucks third scoring option behind Damian Lillard and Giannis Antetokounmpo, playing at this level, Milwaukee is going to be a force in the postseason. As is the case across all sports though, the best ability is availability.
Elsewhere in this table...Brook Lopez continuing to shoot the ball extremely well, which is always a plus as the Bucks rely on Brook to be their defensive lynchpin. Additionally, AJ Green shoots the leather off the ball from 3 and he'll continue to get minutes as long as that persists. Both players are perfect from the free throw line as well which is integral, especially for a bench player for whom minutes and opportunities are limited.
Pat Connaughton is slumping a bit and while that's not a huge deal since he's seventh in minutes per game, but both his field goal and three point percentage are lacking. You could argue that the scoring load doesn't weigh as heavily on the bench as it has in previous years (this could be worth it's own analysis), but that opens the door for the argument that when our starters sit, we'll need that bench scoring all the more. Regardless, it would be nice to see Pat shoot the three a little better before the regular season ends.
Looking Ahead...
Let's now switch gears and look ahead, to the playoffs. With 12 games left in the regular season, the Bucks hold the 2nd seed in the Eastern Conference, and are ahead of the Cleveland Cavaliers by 2 games. I would hope (and expect) the Bucks to hold the 2 seed barring a late-season collapse, so let's perform our analysis under this assumption.
Given the recent adoption of the play-in tournament at the end of the season, where the Bucks would have in past years simply played the 7th seed (currently the Miami Heat), they will play the winner of the firts play-in game between (at this point) the Miami Heat and the Philadelphia Sixers. Again, let's assume that the current standings hold and that the Bucks will play either one of these two teams.
Let's create a query to see how the Bucks have performed against over .500 teams, before zeroing in on the Heat and Sixers specifically.
SELECT We_Beat AS Team_2023_24_Reg_Season,
SUM(Ws) AS Ws_by_MIL,
SUM(Ls) AS Ls_by_MIL
FROM
((SELECT
DISTINCT(CASE WHEN Result LIKE 'W%' AND Team='MIL'
THEN Opp
END) AS We_Beat,
COUNT(CASE WHEN Result LIKE 'W%' AND Team='MIL'
THEN Opp
END) AS Ws,
COUNT(CASE WHEN Result LIKE 'L%' AND Team='MIL'
THEN Opp
END) AS Ls
FROM 'current_season_team_game_results.csv'
WHERE Team='MIL' AND Opp IN
(SELECT DISTINCT(Team)
FROM 'current_season_team_records_totals.csv'
WHERE WLPER>.500) AND We_Beat IS NOT NULL
GROUP BY We_Beat
ORDER BY We_Beat ASC)
UNION
(SELECT
DISTINCT(CASE WHEN Result LIKE 'L%' AND Team='MIL'
THEN Opp END)AS We_Lost_To,
COUNT(CASE WHEN Result LIKE 'W%' AND Team='MIL'
THEN Opp END) AS Ws,
COUNT(CASE WHEN Result LIKE 'L%' AND Team='MIL'
THEN Opp END) AS Ls
FROM 'current_season_team_game_results.csv'
WHERE Team='MIL' AND Opp IN
(SELECT DISTINCT(Team)
FROM 'current_season_team_records_totals.csv'
WHERE WLPER>.500) AND We_Lost_To IS NOT NULL
GROUP BY We_Lost_To
ORDER BY We_Lost_To ASC))
GROUP BY Team_2023_24_Reg_Season
ORDER BY Ws_by_MIL DESC
LIMIT 25
;
It's long but hopefully it's not too messy. Looking at two different tables (a table of current season W/L records and a table of all games and results from this season), we union together two separate queries each calculating the Bucks' wins and losses and against teams over .500 win percentage.
This shows us that the Bucks are a combined 5-1 against Philly and the Heat this season. Sweet! This certainly bodes well, at a high level though. Let's examine this further.