← 記事一覧へ

年俸データは扱いを間違えやすいデータです. 欠けを数えてから使う, 時代をまたぐときは名目額で並べない の2つを 確かめてから, ランキングを作ります.

library(data.table)
library(dplyr)
library(ggplot2)
library(knitr)
library(Lahman)

sal <- as.data.table(Salaries)
head(sal, 5) |> kable()
yearID teamID lgID playerID salary
2004 SFN NL aardsda01 300000
2007 CHA AL aardsda01 387500
2008 BOS AL aardsda01 403250
2009 SEA AL aardsda01 419000
2010 SEA AL aardsda01 2750000

26,428行, 1985年から2016年, 5,149人分です. 単位はドルです.

1. 球団は揃っているか

MLBの球団数は1985-92年が26, 1993-97年が28, 1998年以降が30です.

expected_teams <- function(y) fifelse(y <= 1992, 26L, fifelse(y <= 1997, 28L, 30L))

by_year <- sal[, .(球団数 = uniqueN(teamID), 人数 = .N), by = yearID][order(yearID)]
by_year[, 本来 := expected_teams(yearID)]

by_year[球団数 != 本来] |> kable()
yearID 球団数 人数 本来

球団は毎年すべて揃っています. ここで安心してはいけません. 球団が揃っていても, 中の選手が揃っているとは 限りません.

2. 選手は揃っているか

支配下選手は40人, 出場登録は26人です. 1球団あたりの人数を見ます.

per_team <- sal[, .(人数 = .N), by = .(yearID, teamID)]

per_team |>
  ggplot(aes(x = factor(yearID), y = 人数)) +
  geom_boxplot(outlier.size = 0.7) +
  geom_hline(yintercept = 20, linetype = "dashed", colour = "firebrick") +
  xlab("年") +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, size = 7)) +
  ggtitle("1球団あたりの登録人数 (赤破線は20人)")

MIN_ROSTER <- 20
thin <- per_team[人数 < MIN_ROSTER][order(人数)]

thin |> head(10) |> kable()
yearID teamID 人数
1987 TEX 4
1987 MIN 9
1987 SEA 9
1987 BOS 15
1991 MON 17
1985 SEA 18
1985 PHI 19
1985 PIT 19
1985 MIN 19
1985 ML4 19

10件の球団年が20人未満です. 最少は1987年のTEXで4人しかありません. 25人でプレーする競技で4人分しか年俸が載っていないので, この球団年の総額はまったく使えません.

該当するのは1985年から1991年に集中しています.

sal[, 薄い := paste(yearID, teamID) %in% thin[, paste(yearID, teamID)]]
sal[, .(球団年 = uniqueN(paste(yearID, teamID)),
        `20人未満` = uniqueN(paste(yearID, teamID)[薄い])), by = .(年代 = (yearID %/% 10) * 10)] |>
  arrange(年代) |> kable()
年代 球団年 20人未満
1980 130 9
1990 278 1
2000 300 0
2010 210 0

以下, 球団総額を出すときは20人未満の球団年を除きます. 個人の年俸そのものは欠けていても値は正しいので, 個人ランキングには全部使います.

3. 名目額は時代で比べられない

trend <- sal[, .(平均 = mean(salary), 中央値 = as.numeric(median(salary)),
                 最高 = as.numeric(max(salary))), by = yearID][order(yearID)]

trend |>
  tidyr::pivot_longer(-yearID, names_to = "統計量", values_to = "ドル") |>
  ggplot(aes(x = yearID, y = ドル, colour = 統計量)) +
  geom_line(linewidth = 0.8) +
  scale_y_log10(labels = scales::dollar) +
  xlab("年") + ggtitle("年俸の推移 (対数軸)")

平均年俸は1985年の $476,299から 2016年の$4,396,410へ, 9.2倍になりました.

平均と中央値の開き方にも注目してください. 中央値はあまり動いていません. 上の方だけが伸びています.

sal[, 年平均比 := salary / mean(salary), by = yearID]

4. 個人ランキング

金額そのままの上位です.

name_of <- as.data.table(People)[, .(playerID, 選手 = paste(nameFirst, nameLast))]

top_nominal <-
  sal[order(-salary)][1:10] |>
  merge(name_of, by = "playerID", sort = FALSE) |>
  _[order(-salary), .(選手, 年 = yearID, 球団 = teamID,
                      年俸 = scales::dollar(salary), 年平均比 = round(年平均比, 1))]
kable(top_nominal)
選手 球団 年俸 年平均比
Clayton Kershaw 2016 LAN $33,000,000 7.5
Alex Rodriguez 2009 NYA $33,000,000 10.1
Alex Rodriguez 2010 NYA $33,000,000 10.1
Clayton Kershaw 2015 LAN $32,571,000 7.6
Alex Rodriguez 2011 NYA $32,000,000 9.6
Zack Greinke 2016 ARI $31,799,030 7.2
David Price 2016 BOS $30,000,000 6.8
Alex Rodriguez 2012 NYA $30,000,000 8.7
Alex Rodriguez 2013 NYA $29,000,000 7.8
Miguel Cabrera 2016 DET $28,000,000 6.4

その年の平均に対する倍率で並べ直します.

top_relative <-
  sal[order(-年平均比)][1:10] |>
  merge(name_of, by = "playerID", sort = FALSE) |>
  _[order(-年平均比), .(選手, 年 = yearID, 球団 = teamID,
                        年俸 = scales::dollar(salary), 年平均比 = round(年平均比, 1))]
kable(top_relative)
選手 球団 年俸 年平均比
Gary Sheffield 1998 FLO $14,936,667 11.7
Alex Rodriguez 2009 NYA $33,000,000 10.1
Alex Rodriguez 2010 NYA $33,000,000 10.1
Alex Rodriguez 2005 NYA $26,000,000 9.9
Alex Rodriguez 2001 TEX $22,000,000 9.6
Alex Rodriguez 2011 NYA $32,000,000 9.6
Cecil Fielder 1995 DET $9,237,500 9.6
Alex Rodriguez 2002 TEX $22,000,000 9.2
Manny Ramirez 2004 BOS $22,500,000 9.0
Cecil Fielder 1996 DET $9,237,500 9.0

上位10人のうち共通なのは3人だけです. 名目額で並べると新しい年ばかりになります.

5. 球団総額

payroll <- sal[薄い == FALSE, .(総額 = sum(salary), 人数 = .N), by = .(yearID, teamID)]

payroll |>
  ggplot(aes(x = yearID, y = 総額 / 1e6, group = teamID)) +
  geom_line(alpha = 0.35) +
  scale_y_continuous(labels = scales::comma) +
  xlab("年") + ylab("年俸総額 (百万ドル)") +
  ggtitle("球団年俸総額 (20人未満の球団年を除く)")

その年の平均総額に対する倍率で見ると, 球団間の開きが分かります.

payroll[, 比 := 総額 / mean(総額), by = yearID]

payroll |>
  ggplot(aes(x = factor(yearID), y = 比)) +
  geom_boxplot(outlier.size = 0.7) +
  geom_hline(yintercept = 1, linetype = "dashed") +
  xlab("年") + ylab("その年の平均総額に対する倍率") +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, size = 7)) +
  ggtitle("球団間の年俸格差")

payroll |>
  merge(as.data.table(Teams)[, .(yearID, teamID, name)], by = c("yearID", "teamID"),
        all.x = TRUE) |>
  _[order(-比)][1:10, .(球団 = name, 年 = yearID,
                        総額 = scales::dollar(総額), 人数,
                        平均の何倍 = round(比, 2))] |>
  kable()
球団 総額 人数 平均の何倍
New York Yankees 2005 $208,306,817 26 2.86
New York Yankees 2004 $184,193,950 29 2.67
New York Yankees 2006 $194,663,079 28 2.52
New York Yankees 2008 $207,896,789 30 2.32
New York Yankees 2013 $231,978,886 31 2.29
New York Yankees 2007 $189,259,045 28 2.29
New York Yankees 2010 $206,333,389 25 2.27
New York Yankees 2009 $201,449,189 26 2.27
Los Angeles Dodgers 2013 $223,362,196 32 2.21
New York Yankees 2011 $202,275,028 29 2.18

6. 選手をどう識別するか

年俸データで名前を選手の識別子に使うと壊れます.

dup <- name_of[, .N, by = 選手][N > 1][order(-N)]

tibble::tibble(`同じ名前の playerID が複数ある名前` = nrow(dup),
               `のべ人数` = sum(dup$N)) |> kable()
同じ名前の playerID が複数ある名前 のべ人数
750 1706
dup |> head(5) |> kable()
選手 N
NA Smith 16
NA Jones 13
NA Johnson 12
NA Thompson 7
NA Brown 6

750件の名前が複数人で共有されています. LahmanにはplayerIDがあるので, ここでは問題になりません. 名前しか無いデータでは, これが黙って混ざります.

実際に名前で束ねると, 生涯年俸がどれくらい膨らむか見てみます.

career_id <- sal[, .(生涯年俸 = sum(salary)), by = playerID] |>
  merge(name_of, by = "playerID")
career_nm <- career_id[, .(生涯年俸 = sum(生涯年俸), 人数 = .N), by = 選手]

merge(career_nm[人数 > 1], career_id[, .(選手, 単独 = 生涯年俸)], by = "選手") |>
  _[order(-生涯年俸)][, .(選手, まとめた額 = scales::dollar(生涯年俸),
                          束ねた人数 = 人数)] |>
  unique(by = "選手") |> head(5) |> kable()
選手 まとめた額 束ねた人数
Ken Griffey $156,308,682 2
Pedro Martinez $146,785,585 2
Kevin Brown $131,160,502 3
Jose Bautista $88,183,000 2
Chris Carpenter $86,574,956 2

まとめ

  • 球団が揃っていても選手は揃っていません. 1985年代の 10球団年は20人未満で, 総額としては使えません.
  • 名目額は9.2倍に伸びています. 時代をまたぐときは その年の平均で割ります. 上位10人のうち名目と平均比で共通なのは 3人だけでした.
  • 選手の識別に名前を使ってはいけません. 750件の 名前が複数人で共有されています.

注意

  • Lahmanの年俸データは2016年で止まっています.
  • 出来高, 契約金, 保険料などは含まれません. 公表ベースの数字です.
  • インフレ調整はしていません. 「その年の平均に対する倍率」は リーグ内での相対的な位置を見るものです.

この記事の来歴

もとはNPBの年俸データ (1980-2014年, スクレイピングで集めたもの) を 扱っていました. 取得元だった monespo.com はドメインが失効して 別のサイトに変わっており, 同じデータを取り直す方法がありません. 手元のファイルが無いと再現できない分析はリポジトリに置かない方針にしたので, 今も取得できるLahmanの年俸データに載せ替えました.

当時のNPB版で見つけた問題 (スクレイプ失敗で1球団まるごと欠けていた, 同姓同名が混ざっていた, 名目額で並べていた) は, データを変えても 同じ形で出てきます. 確かめる順番は変わりません.