Eu encontrei um conjunto de dados de salários de jogadores da NBA da temporada 1984-1985 até a temporada 2017-2018 aqui, e pensei que seria um conjunto de dados divertido para praticar minhas habilidades de tidyverse. Todo o código desta postagem pode ser encontrado aqui.
Primeiro, vamos importar o tidyverse
pacote, defina o tema de plotagem e leia os arquivos de dados.
library(tidyverse) theme_set(theme_bw()) # read in data players % select(id, name) salaries % inner_join(players, by = c("player_id" = "id"))
Para todos os gráficos e textos abaixo, “ano” se refere ao ano em que a temporada começou. Por exemplo, o ano de 2017 se refere à temporada 2017-2018.
Verificações de sanidade
Cada linha do salaries
dataframe corresponde a um jogador em uma temporada. Vamos fazer um gráfico do número de observações por ano:
# no. of records by season salaries %>% group_by(season_start) %>% count() %>% ggplot(aes(season_start, n)) + geom_col() + labs(x = "Year", y = "# of observations", title = "# of observations by year")
Parece que o número de jogadores está aumentando lentamente com o tempo, o que pode fazer sentido, já que o número de times da NBA está aumentando com o tempo (embora lentamente). Alguns dos primeiros anos parecem estar sem dados, e o ano de 2013 parece um pouco baixo. No restante deste post, examinaremos apenas os salários de 2000 em diante. (Se tivéssemos mais tempo, verificaríamos se os dados da temporada 2013-2014 estavam completos.)
# we only look at salaries from 2000 onwards # drop and rename some columns salaries % filter(season_start >= 2000) %>% select(player_id, name, salary, year = season_start, team)
A seguir, vamos verificar se o número de equipes representadas no conjunto de dados a cada ano está correto:
# count no. of teams by year salaries %>% group_by(year) %>% summarize(n_teams = n_distinct(team)) %>% ggplot(aes(year, n_teams)) + geom_line() + geom_point() + ylim(c(0, 30)) + labs(x = "Year", y = "# of teams", title = "# of teams by year")
Isso é correto: de acordo com este artigo da Wikipedia, havia 29 equipes nos anos anteriores a 2004 e 30 equipes de 2004 em diante.
Como nossa verificação final de sanidade, vamos olhar para o número de jogadores em cada equipe por ano:
# count no. of players by year # 2013 looks fishy, but we will ignore for now salaries %>% group_by(year, team) %>% count() %>% ggplot(aes(year, n, fill = team)) + geom_col(col = "black", size = 0.2) + labs(x = "Year", y = "# of players", title = "# of players by year") + theme(legend.position = "none")
Cada pequeno retângulo no gráfico acima representa uma equipe. Não há discrepâncias óbvias, exceto para 2013, que devemos realmente examinar em algum momento no futuro.
Tendências salariais da equipe
Vejamos o salário total pago a cada ano:
# total salary by year salaries %>% group_by(year) %>% summarize(tot_salary = sum(salary)) %>% ggplot(aes(year, tot_salary)) + geom_point() + geom_line() + expand_limits(y = 0) + labs(x = "Year", y = "Total salary", title = "Total salary of all players by year")
Os salários estão aumentando, como esperado, mas a taxa de aumento é incomum? No próximo gráfico, adicionamos uma linha de referência correspondente à inflação de 4% para cada ano.
# compare with constant inflation tot_2000 % filter(year == 2000) %>% summarize(tot_salary = sum(salary)) %>% pull() inflation_df % group_by(year) %>% summarize(tot_salary = sum(salary)) %>% ggplot(aes(year, tot_salary)) + geom_point() + geom_line() + geom_line(aes(year, inflation_amt), data = inflation_df, col = "red", linetype = 2) + annotate("text", x = 2008, y = 2.6e9, label = c("4% increase/yr"), color="red") + expand_limits(y = 0) + labs(x = "Year", y = "Total salary", title = "Total salary of all players by year")
Parece que o início de 2010 foi de anos “magros”, enquanto os salários dispararam depois disso.
Como são as tendências salariais por equipe? Neste próximo gráfico, cada linha preta representa uma equipe. A linha azul é uma versão suavizada com média de todas as equipes.
# total salary by year by team salaries %>% group_by(year, team) %>% summarize(tot_salary = sum(salary)) %>% ggplot(aes(year, tot_salary)) + geom_line(aes(group = team), size = 0.1) + geom_smooth(size = 2, se = FALSE) + expand_limits(y = 0) + labs(x = "Year", y = "Total salary", title = "# of players by year", subtitle = "One line per team") + theme(legend.position = "none")
A propagação da linha preta nos diz que há uma grande variação por equipe. A linha azul suavizada reflete a tendência geral de salários que vimos anteriormente.
A seguir, vamos comparar as equipes entre si: existem algumas equipes que sempre gastam mais do que outras? Para o próximo gráfico, classificamos as equipes por salário dentro de cada ano, com as fileiras menores pagando mais salário. Em seguida, fazemos um mapa de calor, com as equipes classificadas por sua classificação média ao longo dos anos.
# team ranking comparison by total salary by year salaries %>% group_by(year, team) %>% summarize(tot_salary = sum(salary)) %>% arrange(year, desc(tot_salary)) %>% mutate(rank = row_number()) %>% group_by(team) %>% mutate(overall_rank = mean(rank)) %>% ggplot(aes(year, fct_reorder(team, overall_rank, .desc = TRUE))) + geom_tile(aes(fill = rank)) + scale_fill_distiller(palette = "RdYlBu", direction = 1) + labs(x = "Year", y = NULL, title = "Teams ranked by total salary by year") + theme(legend.position = "bottom")
(Se tivéssemos mais tempo, deveríamos fundir as linhas que representam o mesmo time, mesmo que o time tenha mudado de nome, por exemplo, Brooklyn Nets e New Jersey Nets.) As equipes no topo do mapa de calor tendem a gastar mais do que as equipes próximas ao inferior. Parece haver alguma correlação positiva entre o salário e a qualidade da equipe (por domínio de conhecimento), mas também há aberrações claras (por exemplo, a equipe logo acima).
Tendências salariais do jogador
Para satisfazer a curiosidade de todos, aqui está a tabela do jogador mais bem pago de cada ano a partir de 2000. Eu acho que todos os jogadores aqui estão ou no Hall da Fama (ou serão uma escolha certa).
# top paid player in each year salaries %>% group_by(year) %>% top_n(salary, n = 1) %>% arrange(year) # # A tibble: 18 x 5 # # Groups: year [18] # player_id name salary year team ## 1 garneke01 Kevin Garnett 19610000 2000 Minnesota Timberwolves # 2 garneke01 Kevin Garnett 22400000 2001 Minnesota Timberwolves # 3 garneke01 Kevin Garnett 25200000 2002 Minnesota Timberwolves # 4 garneke01 Kevin Garnett 28000000 2003 Minnesota Timberwolves # 5 onealsh01 Shaquille O'Neal 27696430 2004 Miami Heat # 6 onealsh01 Shaquille O'Neal 20000000 2005 Miami Heat # 7 garneke01 Kevin Garnett 21000000 2006 Minnesota Timberwolves # 8 garneke01 Kevin Garnett 23750000 2007 Boston Celtics # 9 garneke01 Kevin Garnett 24751934 2008 Boston Celtics # 10 mcgratr01 Tracy McGrady 23239562 2009 New York Knicks # 11 bryanko01 Kobe Bryant 24806250 2010 Los Angeles Lakers # 12 bryanko01 Kobe Bryant 25244493 2011 Los Angeles Lakers # 13 bryanko01 Kobe Bryant 27849149 2012 Los Angeles Lakers # 14 bryanko01 Kobe Bryant 30453805 2013 Los Angeles Lakers # 15 bryanko01 Kobe Bryant 23500000 2014 Los Angeles Lakers # 16 bryanko01 Kobe Bryant 25000000 2015 Los Angeles Lakers # 17 jamesle01 LeBron James 30963450 2016 Cleveland Cavaliers # 18 curryst01 Stephen Curry 34682550 2017 Golden State Warriors
Se você plotar o salário máximo por ano, verá que geralmente ele está aumentando, mas há uma grande variação.
salaries %>% group_by(year) %>% top_n(salary, n = 1) %>% ggplot(aes(year, salary)) + geom_line() + geom_point() + expand_limits(y = 0) + labs(x = "Year", y = "Top salary", title = "Top salary by year")
No restante desta postagem, queremos responder à pergunta: os salários dos jogadores se tornaram mais desiguais com o tempo? Vejamos a distribuição dos salários dos jogadores por alguns anos selecionados:
# histogram of player salaries for 4 years salaries %>% filter(year %in% c(2000, 2005, 2010, 2015)) %>% ggplot(aes(log10(salary))) + geom_histogram() + facet_wrap(~ year) + labs(x = "log10(Salary)", y = "Count", title = "Histogram of player salaries for select years")
É difícil dizer a diferença entre esses 4 histogramas. Outra maneira de abordar isso é traçar a curva de Lorenz para cada ano. A curva mostra a proporção do salário recebido pelos piores x% dos jogadores. Se todos os jogadores tivessem exatamente o mesmo salário, a curva seria a
linha. Quanto mais desiguais forem os salários, mais próxima a curva estará do canto inferior direito do gráfico.
Aqui está a curva de Lorenz para os 4 anos selecionados:
# Lorenz curve for 4 years salaries %>% filter(year %in% c(2000, 2005, 2010, 2015)) %>% arrange(year, salary) %>% group_by(year) %>% mutate(cum_salary = cumsum(salary), tot_salary = sum(salary), cum_n = row_number(), tot_n = n()) %>% mutate(cum_salary_prop = cum_salary / tot_salary * 100, cum_n_prop = cum_n / tot_n * 100) %>% ggplot(aes(cum_n_prop, cum_salary_prop, col = factor(year))) + geom_line() + geom_abline(slope = 1, intercept = 0, linetype = 2) + labs(x = "Bottom x%", y = "% of total salary", title = "% of total salary made by bottom x% of players") + coord_equal() + theme(legend.title = element_blank())
As curvas estão quase todas em cima umas das outras, mas se você olhar de perto, verá que as curvas estão se movendo ligeiramente para o canto inferior direito.
Vamos plotar o coeficiente de Gini (ou índice de Gini) para cada um dos anos. O índice de Gini está intimamente relacionado à curva de Lorenz (consulte o link anterior para obter detalhes). A igualdade completa corresponde a um índice de Gini de 0, enquanto a desigualdade completa (uma pessoa com todo o dinheiro) corresponde a um índice de Gini de 1. Abaixo, calculamos o índice de Gini para cada ano. Eu faço isso de uma maneira não inversa: ficaria feliz em saber como se pode fazer de uma maneira mais ordenada.
# Gini index for each year GetGini % arrange(year, salary) %>% group_by(year) %>% mutate(cum_salary = cumsum(salary), tot_salary = sum(salary), cum_n = row_number(), tot_n = n()) %>% mutate(cum_salary_prop = cum_salary / tot_salary * 100, cum_n_prop = cum_n / tot_n * 100) gini_vec
There does seem to be a slight increase in Gini index over time, but not too noticeable. For reference, based on the latest World Bank’s estimates for the Gini index by country, the lowest Gini index was 24.2 (Slovenia in 2017) and the highest was 63.0 (South Africa in 2014). (The latest Gini index estimate for the USA was 41.4 in 2016. As one might expect, NBA player salaries are very unequal!
Here is the linear regression result of Gini index on year. The slope is statistically significant at level 0.05.
# OLS of gini index on year summary(lm(gini ~ year, data = gini_df)) # Call: # lm(formula = gini ~ year, data = gini_df) # # Residuals: # Min 1Q Median 3Q Max # -0.057448 -0.010524 0.000716 0.013028 0.032470 # # Coefficients: # Estimate Std. Error t value Pr(>|t|) # (Intercept) -4.7569138 1.9592525 -2.428 0.0274 * # year 0.0026375 0.0009755 2.704 0.0156 * # --- # Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1 # # Residual standard error: 0.02147 on 16 degrees of freedom # Multiple R-squared: 0.3136, Adjusted R-squared: 0.2707 # F-statistic: 7.311 on 1 and 16 DF, p-value: 0.01565Relacionados