Manny Machado, Juan Soto, and Eugenio Suarez are three all-star caliber baseball players. The trio consistently set baseball discourse ablaze. But there’s one question in baseball circles that has been debated for years: “Who is better?”
Difference between .300 and .275 hitter is one hit every two weeks. If you see both 15 games a year, there’s a 40% chance that the .275 hitter will have more hits than the .300 hitter. - Moneyball, Michael Lewis
Sure we can all watch them play, but baseball is a game of numbers. We cannot rely on our eyes, heuristics, or biases. In following the MLB trade deadline on Twitter, I saw a tweet from Jeff Passan that described Soto, Suarez, and Machado in three different aspects.
Great question, John. For most of baseball history, the walk was sneered at – a lesser form of getting on base compared to the exceptional skill it takes to swing. The analytical revolution in the game changed that. If the objective of the game is to outscore a team, then the…
To summarize, Passan describes three metrics: batting average (BA), on-base percentage (OBP), and slugging (SLG). Passan shares the Moneyball principle in that modern statistics have turn to other statistics to evaluate a hitter; old baseball viewed walks as a “lesser” hit. Each of these metrics tells a different story about the players. BA is the simplest, describing how often a player gets a hit. OBP is the percentage of times a player reaches base, which includes hits, walks, and hit by pitches. Slugging is the total number of bases a player gets per at-bat, which includes singles, doubles, triples, and home runs - its like batting average but weighted by the type of hit.
Following the definitions, Passan shares slashlines for Machado, Soto, and Suarez. A slashline is a shorthand way to describe a player’s hitting statistics, typically in the format BA/OBP/SLG.
Machado: .301/.360/.505
Soto: .247/.380/.480
Suarez: .245/.316/.565
Machado is described as the best “all-around hitter” of the three, with the highest batting average. Soto is praised for his exceptional on-base skills. He is also in the 100th percentile for walk rate. Suarez is noted for his power-hitting ability.
Code
import plotly.graph_objects as goimport pandas as pdimage_urls = ["https://img.mlbstatic.com/mlb-photos/image/upload/d_people:generic:headshot:67:current.png/w_426,q_auto:best/v1/people/592518/headshot/67/current", # Machado"https://img.mlbstatic.com/mlb-photos/image/upload/d_people:generic:headshot:67:current.png/w_426,q_auto:best/v1/people/665742/headshot/67/current", # Soto"https://img.mlbstatic.com/mlb-photos/image/upload/d_people:generic:headshot:67:current.png/w_426,q_auto:best/v1/people/553993/headshot/67/current"# Suárez]# Data for the three playersdata = {'Player': ['Manny Machado', 'Juan Soto', 'Eugenio Suárez'],'BA': [0.301, 0.247, 0.245],'OBP': [0.360, 0.380, 0.316],'SLG': [0.505, 0.480, 0.565]}df = pd.DataFrame(data)# Create the figurefig = go.Figure()# Add a bar trace for each statisticfig.add_trace(go.Bar( x=df['Player'], y=df['BA'], name='Batting Avg (BA)', marker_color='rgba(54, 162, 235, 0.7)', hovertemplate='%{y:.3f}<extra></extra>'))fig.add_trace(go.Bar( x=df['Player'], y=df['OBP'], name='On-Base % (OBP)', marker_color='rgba(75, 192, 192, 0.7)', hovertemplate='%{y:.3f}<extra></extra>'))fig.add_trace(go.Bar( x=df['Player'], y=df['SLG'], name='Slugging % (SLG)', marker_color='rgba(255, 99, 132, 0.7)', hovertemplate='%{y:.3f}<extra></extra>'))# --- Add Headshot Images ---for i, player inenumerate(df['Player']): fig.add_layout_image(dict( source=image_urls[i], xref="x", yref="paper", x=player, # Use player name to anchor on x-axis y=-0.30, # Position below the x-axis sizex=0.25, # Size of the image sizey=0.25, xanchor="center", yanchor="bottom" ) )# Update the layout for a clean, grouped bar chartfig.update_layout( barmode='group', title={'text': '<b>Player "Slash Line" Comparison</b>','y':0.9,'x':0.5,'xanchor': 'center','yanchor': 'top' }, xaxis_title='Player', yaxis_title='Rate', yaxis_range=[0, 0.6], legend_title_text='Statistic:', legend=dict( orientation="h", yanchor="bottom", xanchor="left", y=1.02, x=0.2, ), template='plotly_white', font=dict( family="Inter, sans-serif", size=12 ), margin=dict(b=120) # Increase bottom margin to make space for images)# Show the figurefig.show()
What is wOBA?
What I found most interesting is that all three players have the same wOBA (weighted on-base average) of .370 (or .369 for Soto, to be precise). If they have the same overall grade, how are they so different? And what can a deeper dive into the numbers tell us about who might be the “best” of the three?
wOBA is a more advanced metric that combines a player’s ability to get on base and hit for power into a single number. It assigns different weights to different types of hits and events, providing a more comprehensive measure of a player’s offensive contribution. Weights for wOBA are derived from the run expectancy of each event, making it a more accurate reflection of a player’s value than traditional stats like BA, OBP, or SLG.
You can think of wOBA as a GPA-like metric for hitters. A single is a C, a double is a B, a home run is an A, a walk is a B-, and a strikeout is an F. wOBA averages all those grades together. The grades are scaled by how much each event contributes to scoring runs.
Below is the formula for wOBA in 2025 per FanGraphs:
Season
wOBA
wOBAScale
wBB
wHBP
w1B
w2B
w3B
wHR
runSB
runCS
R/PA
R/W
cFIP
2025
.314
1.239
.692
.723
.884
1.256
1.591
2.048
.200
-.406
.117
9.710
3.102
Looking at doubles (w2B) we see that they are worth 1.256 runs, while home runs (wHR) are worth 2.048 runs. Walks (wBB) are worth .692 runs. This means that a player who hits a lot of doubles and home runs will have a higher wOBA than a player who hits a lot of singles, even if they have the same batting average.
Going Deeper: xwOBA (The “For the Nerds” Section)
xwOBA (expected weighted on-base average) is a metric that estimates what a player’s wOBA should be based on the quality of their contact, strikeouts, and walks. It uses Statcast data to analyze the exit velocity and launch angle of batted balls, as well as the player’s plate discipline. According to MLB, xwOBA is more “indicative” of a player’s true talent level than wOBA, which can be influenced by luck.
For example, Andrew Vaughn has a wOBA of .289 but an xwOBA of .325. This suggests that Vaughn has been unlucky and should be hitting better than he has been. I actually think the Brewers made a brilliant move trading for Vaughn, given his expected statistics and age (27).
Put it simply, if
wOBA > xwOBA, the player has been lucky. Bloop hits, seeing-eye singles, or defensive misplays are contributing to their success. They might be due for some regression.
wOBA < xwOBA, the player has been unlucky. They are hitting the ball hard, but right at defenders. They are “barreling up” the ball without the results to show for it. They could be due for a hot streak.
Note
Checkout my previous post on lucky (and unlucky) hitters!
Analyzing Our Three Players with xwOBA
Here are the current xwOBA values for our three players:
Machado: .394 (94th percentile)
Soto: .438 (100th percentile)
Suarez: .346 (66th percentile)
Machado and Soto are both outperforming their wOBA, suggesting that they are hitting the ball with authority and their numbers should be better than they are. Suarez is underperforming his wOBA, suggesting that the quality of contact might not sustain this exact level of production.
Code
import plotly.graph_objects as goimport pandas as pd# --- Data Setup ---data = {'Player': ['Manny Machado', 'Juan Soto', 'Eugenio Suárez'],'wOBA': [0.370, 0.369, 0.370],'xwOBA': [0.394, 0.438, 0.346]}df = pd.DataFrame(data)# --- Player Headshot URLs ---# Using official MLB headshot images for better qualityimage_urls = ["https://img.mlbstatic.com/mlb-photos/image/upload/d_people:generic:headshot:67:current.png/w_426,q_auto:best/v1/people/592518/headshot/67/current", # Machado"https://img.mlbstatic.com/mlb-photos/image/upload/d_people:generic:headshot:67:current.png/w_426,q_auto:best/v1/people/665742/headshot/67/current", # Soto"https://img.mlbstatic.com/mlb-photos/image/upload/d_people:generic:headshot:67:current.png/w_426,q_auto:best/v1/people/553993/headshot/67/current"# Suárez]# --- Chart Creation ---fig = go.Figure()# Add bar tracesfig.add_trace(go.Bar( x=df['Player'], y=df['wOBA'], name='wOBA (Actual)', marker_color='rgba(54, 162, 235, 0.7)', hovertemplate='%{y:.3f}<extra></extra>'))fig.add_trace(go.Bar( x=df['Player'], y=df['xwOBA'], name='xwOBA (Expected)', marker_color='rgba(255, 159, 64, 0.7)', hovertemplate='%{y:.3f}<extra></extra>'))# --- Add Headshot Images ---for i, player inenumerate(df['Player']): fig.add_layout_image(dict( source=image_urls[i], xref="x", yref="paper", x=player, # Use player name to anchor on x-axis y=-0.28, # Position below the x-axis sizex=0.27, # Size of the image sizey=0.27, xanchor="center", yanchor="bottom" ) )# --- Update Layout ---fig.update_layout( barmode='group', title={'text': '<b>wOBA vs. xwOBA Comparison</b>','y':0.99, 'x':0.5, 'xanchor': 'center', 'yanchor': 'top' }, xaxis_title=None, yaxis_title='Value', yaxis_range=[0.0, 0.45],# legend=dict(# orientation="h", yanchor="bottom", y=1.02, xanchor="center", x=0.5# ), template='plotly_white', font=dict(family="Inter, sans-serif", size=12), margin=dict(b=120) # Increase bottom margin to make space for images)fig.show()
Conclusion: Who do you want at the plate?
From Passan’s tweet, we saw three different types of hitters. While they excely in different metrics, they all share the same wOBA. We then investigated xwOBA, which gives us a deeper understanding of their performance. Based on xwOBA, Soto is the best hitter of the three, followed by Machado, and then Suarez.
So in the end, the number was a mirage. A tidy .370 wOBA that made three very different men look like statistical clones. But baseball, like any market, is filled with inefficiencies for those who know where to look. And looking at xwOBA was like looking at the audited financials instead of the press release. It revealed that one of these hitters was getting lucky, another was earning his keep, and the third, Juan Soto, was the undervalued asset — despite already being a superstar. The scoreboard showed a tie, but the underlying physics of their batted balls told you who was really winning the game.
Peace ✌️
Source Code
---title: "Same wOBA, Different Story"author: "Oliver Chang"email: oliverc1622@gmail.comdate: "2025-08-03"categories: [Padres, Mariners, Mets]toc: trueformat: html: html-math-method: katex code-tools: trueimage: composite.pngtitle-block-banner: default---<iframe src="https://streamable.com/mlbfilmroom/00u91h9ad3Lty1uCX356/genosotochado?partnerId=web_multimedia-search_video-share" width="560" height="315"></iframe>## Introduction: The Beauty of Baseball's NumbersManny Machado, Juan Soto, and Eugenio Suarez are three all-star caliber baseball players. The trio consistently set baseball discourse ablaze. But there's one question in baseball circles that has been debated for years: "Who is better?"> Difference between .300 and .275 hitter is one hit every two weeks. If you see both 15 games a year, there’s a 40% chance that the .275 hitter will have more hits than the .300 hitter. - [Moneyball](https://en.wikipedia.org/wiki/Moneyball:_The_Art_of_Winning_an_Unfair_Game), Michael LewisSure we can all watch them play, but baseball is a game of numbers. We cannot rely on our eyes, heuristics, or biases. In following the MLB trade deadline on Twitter, I saw a tweet from [Jeff Passan](https://x.com/JeffPassan/status/1951747434549289092) that described Soto, Suarez, and Machado in three different aspects.<blockquote class="twitter-tweet" data-conversation="none" data-theme="dark"><p lang="en" dir="ltr">Great question, John. For most of baseball history, the walk was sneered at -- a lesser form of getting on base compared to the exceptional skill it takes to swing. The analytical revolution in the game changed that. If the objective of the game is to outscore a team, then the…</p>— Jeff Passan (@JeffPassan) <a href="https://twitter.com/JeffPassan/status/1951747434549289092?ref_src=twsrc%5Etfw">August 2, 2025</a></blockquote><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>To summarize, Passan describes three metrics: batting average ([BA](https://www.mlb.com/glossary/standard-stats/batting-average)), on-base percentage ([OBP](https://www.mlb.com/glossary/standard-stats/on-base-percentage)), and slugging ([SLG](https://www.mlb.com/glossary/standard-stats/slugging-percentage)). Passan shares the Moneyball principle in that modern statistics have turn to other statistics to evaluate a hitter; old baseball viewed walks as a "lesser" hit. Each of these metrics tells a different story about the players. BA is the simplest, describing how often a player gets a hit. OBP is the percentage of times a player reaches base, which includes hits, walks, and hit by pitches. Slugging is the total number of bases a player gets per at-bat, which includes singles, doubles, triples, and home runs - its like batting average but weighted by the type of hit.Following the definitions, Passan shares slashlines for Machado, Soto, and Suarez. A slashline is a shorthand way to describe a player's hitting statistics, typically in the format `BA/OBP/SLG`. - Machado: .301/.360/.505- Soto: .247/.380/.480- Suarez: .245/.316/.565Machado is described as the best "all-around hitter" of the three, with the highest batting average. Soto is praised for his exceptional on-base skills. He is also in the 100th percentile for walk rate. Suarez is noted for his power-hitting ability.```{python}# | code-fold: true# | warning: falseimport plotly.graph_objects as goimport pandas as pdimage_urls = ["https://img.mlbstatic.com/mlb-photos/image/upload/d_people:generic:headshot:67:current.png/w_426,q_auto:best/v1/people/592518/headshot/67/current", # Machado"https://img.mlbstatic.com/mlb-photos/image/upload/d_people:generic:headshot:67:current.png/w_426,q_auto:best/v1/people/665742/headshot/67/current", # Soto"https://img.mlbstatic.com/mlb-photos/image/upload/d_people:generic:headshot:67:current.png/w_426,q_auto:best/v1/people/553993/headshot/67/current"# Suárez]# Data for the three playersdata = {'Player': ['Manny Machado', 'Juan Soto', 'Eugenio Suárez'],'BA': [0.301, 0.247, 0.245],'OBP': [0.360, 0.380, 0.316],'SLG': [0.505, 0.480, 0.565]}df = pd.DataFrame(data)# Create the figurefig = go.Figure()# Add a bar trace for each statisticfig.add_trace(go.Bar( x=df['Player'], y=df['BA'], name='Batting Avg (BA)', marker_color='rgba(54, 162, 235, 0.7)', hovertemplate='%{y:.3f}<extra></extra>'))fig.add_trace(go.Bar( x=df['Player'], y=df['OBP'], name='On-Base % (OBP)', marker_color='rgba(75, 192, 192, 0.7)', hovertemplate='%{y:.3f}<extra></extra>'))fig.add_trace(go.Bar( x=df['Player'], y=df['SLG'], name='Slugging % (SLG)', marker_color='rgba(255, 99, 132, 0.7)', hovertemplate='%{y:.3f}<extra></extra>'))# --- Add Headshot Images ---for i, player inenumerate(df['Player']): fig.add_layout_image(dict( source=image_urls[i], xref="x", yref="paper", x=player, # Use player name to anchor on x-axis y=-0.30, # Position below the x-axis sizex=0.25, # Size of the image sizey=0.25, xanchor="center", yanchor="bottom" ) )# Update the layout for a clean, grouped bar chartfig.update_layout( barmode='group', title={'text': '<b>Player "Slash Line" Comparison</b>','y':0.9,'x':0.5,'xanchor': 'center','yanchor': 'top' }, xaxis_title='Player', yaxis_title='Rate', yaxis_range=[0, 0.6], legend_title_text='Statistic:', legend=dict( orientation="h", yanchor="bottom", xanchor="left", y=1.02, x=0.2, ), template='plotly_white', font=dict( family="Inter, sans-serif", size=12 ), margin=dict(b=120) # Increase bottom margin to make space for images)# Show the figurefig.show()```## What is wOBA?What I found most interesting is that all three players have the same wOBA (weighted on-base average) of .370 (or .369 for Soto, to be precise). If they have the same overall grade, how are they so different? And what can a deeper dive into the numbers tell us about who might be the "best" of the three?[wOBA](https://www.mlb.com/glossary/advanced-stats/weighted-on-base-average) is a more advanced metric that combines a player's ability to get on base and hit for power into a single number. It assigns different weights to different types of hits and events, providing a more comprehensive measure of a player's offensive contribution. Weights for wOBA are derived from the run expectancy of each event, making it a more accurate reflection of a player's value than traditional stats like BA, OBP, or SLG. You can think of wOBA as a GPA-like metric for hitters. A single is a C, a double is a B, a home run is an A, a walk is a B-, and a strikeout is an F. wOBA averages all those grades together. The grades are scaled by how much each event contributes to scoring runs.Below is the formula for wOBA in 2025 per [FanGraphs](https://www.fangraphs.com/tools/guts?type=cn):<div style="height:100px;overflow:auto;">| Season | wOBA | wOBAScale | wBB | wHBP | w1B | w2B | w3B | wHR | runSB | runCS | R/PA | R/W | cFIP ||--------|------|-----------|------|------|------|-------|-------|-------|-------|--------|-------|-------|-------|| 2025 | .314 | 1.239 | .692 | .723 | .884 | 1.256 | 1.591 | 2.048 | .200 | -.406 | .117 | 9.710 | 3.102 |</div>Looking at doubles (w2B) we see that they are worth 1.256 runs, while home runs (wHR) are worth 2.048 runs. Walks (wBB) are worth .692 runs. This means that a player who hits a lot of doubles and home runs will have a higher wOBA than a player who hits a lot of singles, even if they have the same batting average.## Going Deeper: xwOBA (The "For the Nerds" Section)[xwOBA](https://www.mlb.com/glossary/statcast/expected-woba) (expected weighted on-base average) is a metric that estimates what a player's wOBA should be based on the quality of their contact, strikeouts, and walks. It uses Statcast data to analyze the exit velocity and launch angle of batted balls, as well as the player's plate discipline. According to MLB, xwOBA is more "indicative" of a player's true talent level than wOBA, which can be influenced by luck. For example, [Andrew Vaughn](https://baseballsavant.mlb.com/savant-player/andrew-vaughn-683734?stats=statcast-r-hitting-mlb) has a wOBA of .289 but an xwOBA of .325. This suggests that Vaughn has been unlucky and should be hitting better than he has been. I actually think the Brewers made a brilliant move trading for Vaughn, given his expected statistics and age (27). Put it simply, if - wOBA > xwOBA, the player has been lucky. Bloop hits, seeing-eye singles, or defensive misplays are contributing to their success. They might be due for some regression.- wOBA < xwOBA, the player has been unlucky. They are hitting the ball hard, but right at defenders. They are "barreling up" the ball without the results to show for it. They could be due for a hot streak.::: {.callout-note}Checkout my [previous post](https://runningonnumbers.com/posts/lucky-hitters/) on lucky (and unlucky) hitters!:::## Analyzing Our Three Players with xwOBAHere are the current xwOBA values for our three players:- Machado: .394 (94th percentile)- Soto: .438 (100th percentile)- Suarez: .346 (66th percentile)Machado and Soto are both outperforming their wOBA, suggesting that they are hitting the ball with authority and their numbers should be better than they are. Suarez is underperforming his wOBA, suggesting that the quality of contact might not sustain this exact level of production.```{python}# | code-fold: true# | warning: falseimport plotly.graph_objects as goimport pandas as pd# --- Data Setup ---data = {'Player': ['Manny Machado', 'Juan Soto', 'Eugenio Suárez'],'wOBA': [0.370, 0.369, 0.370],'xwOBA': [0.394, 0.438, 0.346]}df = pd.DataFrame(data)# --- Player Headshot URLs ---# Using official MLB headshot images for better qualityimage_urls = ["https://img.mlbstatic.com/mlb-photos/image/upload/d_people:generic:headshot:67:current.png/w_426,q_auto:best/v1/people/592518/headshot/67/current", # Machado"https://img.mlbstatic.com/mlb-photos/image/upload/d_people:generic:headshot:67:current.png/w_426,q_auto:best/v1/people/665742/headshot/67/current", # Soto"https://img.mlbstatic.com/mlb-photos/image/upload/d_people:generic:headshot:67:current.png/w_426,q_auto:best/v1/people/553993/headshot/67/current"# Suárez]# --- Chart Creation ---fig = go.Figure()# Add bar tracesfig.add_trace(go.Bar( x=df['Player'], y=df['wOBA'], name='wOBA (Actual)', marker_color='rgba(54, 162, 235, 0.7)', hovertemplate='%{y:.3f}<extra></extra>'))fig.add_trace(go.Bar( x=df['Player'], y=df['xwOBA'], name='xwOBA (Expected)', marker_color='rgba(255, 159, 64, 0.7)', hovertemplate='%{y:.3f}<extra></extra>'))# --- Add Headshot Images ---for i, player inenumerate(df['Player']): fig.add_layout_image(dict( source=image_urls[i], xref="x", yref="paper", x=player, # Use player name to anchor on x-axis y=-0.28, # Position below the x-axis sizex=0.27, # Size of the image sizey=0.27, xanchor="center", yanchor="bottom" ) )# --- Update Layout ---fig.update_layout( barmode='group', title={'text': '<b>wOBA vs. xwOBA Comparison</b>','y':0.99, 'x':0.5, 'xanchor': 'center', 'yanchor': 'top' }, xaxis_title=None, yaxis_title='Value', yaxis_range=[0.0, 0.45],# legend=dict(# orientation="h", yanchor="bottom", y=1.02, xanchor="center", x=0.5# ), template='plotly_white', font=dict(family="Inter, sans-serif", size=12), margin=dict(b=120) # Increase bottom margin to make space for images)fig.show()```## Conclusion: Who do you want at the plate?From Passan's tweet, we saw three different types of hitters. While they excely in different metrics, they all share the same wOBA. We then investigated xwOBA, which gives us a deeper understanding of their performance. Based on xwOBA, Soto is the best hitter of the three, followed by Machado, and then Suarez. So in the end, the number was a mirage. A tidy .370 wOBA that made three very different men look like statistical clones. But baseball, like any market, is filled with inefficiencies for those who know where to look. And looking at xwOBA was like looking at the audited financials instead of the press release. It revealed that one of these hitters was getting lucky, another was earning his keep, and the third, Juan Soto, was the undervalued asset — despite already being a superstar. The scoreboard showed a tie, but the underlying physics of their batted balls told you who was really winning the game.Peace ✌️<iframe src="https://streamable.com/m/juan-soto-homers-16-on-a-fly-ball-to-right-center-field-zkp5lh?partnerId=web_video-playback-page_video-share" width="560" height="315"></iframe>