When we calculate a summary statistic in univariate statistics, we’re making a statement about what we can expect to see in other situations. If I say that the average height of a cedar tree is 75 feet, that gives an expectation for the average height we might calculate for any given sample of cedar trees. However, there’s more information that we need to communicate. It’s not just the summary measure– it’s also our level of uncertainty around that summary measure. Sure, the average height might be 75 feet, but does that mean in every sample we ever collect we’re always going to see an average of 75 feet?
We’ll be working with data from every NBA player who was active during the 2018-19 season.
Here’s the data:
require(tidyverse)
nba<-read_rds("https://github.com/rweldzius/PSC4175_SUM2025/raw/main/Data/nba_players_2018.Rds")
## Error in readRDS(con, refhook = refhook): cannot open the connection to 'https://github.com/rweldzius/PSC4175_SUM2025/raw/main/Data/nba_players_2018.Rds'
This data contains the following variables:
| Name | Definition |
|---|---|
| namePlayer | Player name |
| idPlayer | Unique player id |
| slugSeason | Season start and end |
| numberPlayerSeason | Which season for this player |
| isRookie | Rookie season, true or false |
| slugTeam | Team short name |
| idTeam | Unique team id |
| gp | Games Played |
| gs | Games Started |
| fgm | Field goals made |
| fga | Field goals attempted |
| pctFG | Percent of field goals made |
| fg3m | 3 point field goals made |
| fg3a | 3 point field goals attempted |
| pctFG3 | Percent of 3 point field goals made |
| pctFT | Free Throw percentage |
| fg2m | 2 point field goals made |
| fg2a | 2 point field goals attempted |
| pctFG2 | Percent of 2 point field goals made |
| agePlayer | Player age |
| minutes | Minutes played |
| ftm | Free throws made |
| fta | Free throws attempted |
| oreb | Offensive rebounds |
| dreb | Defensive rebounds |
| treb | Total rebounds |
| ast | Assists |
| blk | Blocks |
| tov | Turnovers |
| pf | Personal fouls |
| pts | Total points |
| urlNBAAPI | Source url |
We might be interested in a variety of questions:
To answer these questions we need to look at the following variables:
For me, I’m most curious if the Eastern or Western conferences have different styles of play. In particular, I want to know if one conference fouls more than the other.
Recall that there are two conference in the NBA, eastern and western. Let’s take a look at the variable that indicates which conference the player played in that season.
nba%>%select(idConference)%>%
glimpse()
## Error: object 'nba' not found
It looks like conference is structured as numeric, but a “1” or a “2”. Because it’s best to have binary variables structured as “has the characteristic” or “doesn’t have the characteristic” we’re going to create a variable for western conference that’s set to 1 if the player was playing in the western conference and 0 if the player was not (this is the same as playing in the eastern conference).
nba<-nba%>%
mutate(conference=ifelse(idConference==1,'West','East'))
## Error: object 'nba' not found
Now that we’ve wrangled, let’s compare personal fouls among players in the east versus west conferences
nba %>%
group_by(conference) %>%
summarise(pf_mean = mean(pf,na.rm=T))
## Error: object 'nba' not found
Players in the Eastern conference have an average of 98 personal fouls in the 2018-2019 seasons, compared to players in the Western conference who only had 96.1 (on average).
But are these differences meaningful? Another way of expressing this is “how confident are we that they are significantly different?”
Statistical significance can be expressed in many different ways, but for now think of it as if you were an all-powerful deity who could see across a thousand universes. In how many of those universes would our conclusion that Eastern conference players commit more personal fouls be true?
This is all very heady, so let’s do something more mundane that winds up simulating this idea.
We’re going to start by building up a range of uncertainty from the data we already have. We’ll do this by sampling from the data itself.
Let’s just take very small sample of players– 100 players– and
calculate personal fouls for those in the Eastern and Western
conferences. We are going to set.seed to ensure that we get
the same/similar answers every time we run the “random number”
generator.
set.seed(123)
sample_size<-100
nba%>%
sample_n(size=sample_size, replace=TRUE) %>% ## Sample size is as set above. Replacement is set to TRUE
group_by(conference)%>% ## Group by the conference
summarize(mean(pf)) ## calculate mean
## Error: object 'nba' not found
An even bigger difference! Among this random sample of 100 players, there is more than a 10-personal foul difference between the East and the West!
If we think of this random sample as a proxy for an alternate universe, in this universe our conclusion is even stronger!
But what about a different universe?
nba%>%
sample_n(size=sample_size, replace=TRUE) %>% ## Sample size is as set above. Replacement is set to TRUE
group_by(conference)%>% ## Group by the conference
summarize(mean(pf)) ## calculate mean
## Error: object 'nba' not found
Oh wait…this time the conclusion is reversed? In this simulated alternate universe, Western conference players had more personal fouls (102 versus 100). What should we therefore conclude?
These resamples on their own don’t appear to be particularly useful, but what would happen if we calculated a bunch (technical term) of them?
I can continue this process of sampling and generating values many times using a loop. The code below resamples from the data 1,000 times, each time calculating the mean personal fouls for Eastern and Western conference players in a sample of size 100. It then adds those two means to a growing list, using the bind_rows function. ## Warning: the code below will take a little while to run
bsRes<-NULL ## Create a NULL variable: will fill this in later
for (i in 1:1000){ # Repeat the steps below 1000 times
bsRes<-nba%>%
sample_n(size=sample_size, replace=TRUE) %>% ## Sample 100 players
group_by(conference)%>% ## Group by conference
summarize(mean_pf=mean(pf))%>% ## Calculate mean personal fouls for Eastern and Western players
mutate(bsInd = i) %>% ## Save the indicator for which random sample we are on
bind_rows(bsRes) ## add this result to the existing dataset
}
## Error: object 'nba' not found
Now I have a dataset that is built up from a bunch of small resamples from the data, with average personal fouls for Eastern and Western conference players in each small sample. Let’s see what these look like.
bsRes
## NULL
This is a dataset that’s just a bunch of means. We can calculate the mean of all of these means and see what it looks like:
bsRes%>%
group_by(conference)%>%
summarise(mean_of_means=mean(mean_pf))
## Error in UseMethod("group_by"): no applicable method for 'group_by' applied to an object of class "NULL"
So the average of these averages is actually pretty close to what we see in the actual data, right?
nba %>%
group_by(conference) %>%
summarise(mean_pf = mean(pf))
## Error: object 'nba' not found
Quick Exercise Repeat the above, but do it for points scored.
# INSERT CODE HERE
That’s fine, but the other thing is that the distribution of those repeated samples will tell us about what we can expect to see in other, out of sample data that’s generated by the same process.
Let’s take a look at the distribution of personal fouls by conference:
bsRes%>%
ggplot(aes(x=mean_pf,fill=conference))+
geom_density(alpha=.3)
## Error in `geom_density()`:
## ! Problem while computing aesthetics.
## ℹ Error occurred in the 1st layer.
## Caused by error:
## ! object 'mean_pf' not found
It’s pretty hard to tell if these are different, right?
Now we can make some statements about uncertainty. Based on this, we can pretend to be all-powerful voyager across universes, and conclude that Eastern conference players commit more personal fouls.
The easiest way to do this is just to create a new variable that indicates whether the Eastern conference players had more personal fouls than the Western conference players in a given random sample. But currently, our data is organized in the “long” format, right?
bsRes
## NULL
We want to convert it to the “wide” format, which means that each row is a random sample simulation, and we have one column for the Eastern conference personal fouls, and one column for the Western conference personal fouls.
Let’s create this using either spread() or
pivot_wider().
# Spread approach
bsRes %>%
spread(conference,mean_pf)
## Error in UseMethod("spread"): no applicable method for 'spread' applied to an object of class "NULL"
# Pivot-wider approach
bsRes %>%
pivot_wider(names_from = 'conference',values_from = 'mean_pf')
## Error in UseMethod("pivot_wider"): no applicable method for 'pivot_wider' applied to an object of class "NULL"
With the data organized in “wide” format, it is now trivial to calculate whether the Eastern players had more personal fouls than the Western players.
bsRes %>%
pivot_wider(names_from = 'conference',values_from = 'mean_pf') %>%
mutate(diff = East - West,
EastMore = diff > 0)
## Error in UseMethod("pivot_wider"): no applicable method for 'pivot_wider' applied to an object of class "NULL"
To express our “confidence” in the conclusion that Eastern conference
players made more personal fouls than Western conference players in the
2018-2019 season, we can simply calculate the proportion of the 1,000
simulated alternate universes in which this conclusion was true! To do
this, we just take the overall average of our new column
EastMore!
bsRes %>%
pivot_wider(names_from = 'conference',values_from = 'mean_pf') %>%
mutate(diff = East - West,
EastMore = diff > 0) %>%
summarise(conf = mean(EastMore))
## Error in UseMethod("pivot_wider"): no applicable method for 'pivot_wider' applied to an object of class "NULL"
0.531. Or, approximately 53.1%. In other words, in the data, Eastern conference players committed more personal fouls in a little more than half of the 1,000 simulated realities.
How strong is our argument do you think? Typically social scientists adhere to a norm of at least 95% confidence before we feel comfortable defending our conclusion. Otherwise, how can we be certain that it’s not just a fluke of the data?
How confident are you that Eastern conference players are better than Western conference players on any of these metrics?
# INSERT CODE HERE