分布を見る

イントロ

研究者向けにプロ野球のデータを使ってRとggplot2の使い方を紹介してます。もともとラボメンバーの興味を得るために野球データを使っていますが、打率、左打ち、チームなどの専門用語を適宜生物学的専門用語に脳内変換して眺めてください。今回は与えられたデータの分布を見ます。

目的

  • ggplot2でヒストグラムかきます
  • ggplot2で箱ひげ図かきます
  • ggplot2で散布図かきます
  • 散布図で点が重なるときの対策を講じます

プロ野球データ(打者)のデータをロードする

こちらのエントリで作成したプロ野球データの打者データを使っていきます。
打者のデータbatting.csvはこちらからダウンロードできます。

library(tidyverse)

# load data into memory
df <- read_csv(file.path("data", "batting.csv"))
## Parsed with column specification:
## cols(
##   .default = col_double(),
##   Name = col_character(),
##   CS = col_logical(),
##   GDP = col_logical(),
##   IBB = col_logical(),
##   League = col_character(),
##   Team = col_character(),
##   PlayerID = col_character(),
##   Bats = col_character()
## )
## See spec(...) for full column specifications.
## Warning: 23681 parsing failures.
##  row col           expected actual               file
## 1014  CS 1/0/T/F/TRUE/FALSE     4  'data/batting.csv'
## 1016  CS 1/0/T/F/TRUE/FALSE     11 'data/batting.csv'
## 1017  CS 1/0/T/F/TRUE/FALSE     3  'data/batting.csv'
## 1018  CS 1/0/T/F/TRUE/FALSE     3  'data/batting.csv'
## 1019  CS 1/0/T/F/TRUE/FALSE     2  'data/batting.csv'
## .... ... .................. ...... ..................
## See problems(...) for more details.

おそらく似たような警告が出ているのではないでしょうか。これは、read_csvがファイルから読み出すときに、各カラムごとに自動的に「型」を判断するのですが、デフォルトだと最初の1000行を使うから生じた警告です。なので、そのあたりを考慮すれば警告なしにデータを読み込めます。

# this column should be an intger, but it's not.
is(df$CS)
## [1] "logical" "vector"
# read_csv guesses the column types from the first guess_max rows.
# The default guess_max is 1000, which is not enough to reach to
# the meaningful row in CS column starting from row 1014.

# Let's get the line number of batting.csv
R.utils::countLines(file.path("data","batting.csv"))
## [1] 31080
## attr(,"lastLineHasNewline")
## [1] TRUE
# and give a larger number for guessing.
df <- read_csv(file.path("data", "batting.csv"), guess_max = 30000)
## Parsed with column specification:
## cols(
##   .default = col_double(),
##   Name = col_character(),
##   League = col_character(),
##   Team = col_character(),
##   PlayerID = col_character(),
##   Bats = col_character()
## )
## See spec(...) for full column specifications.
df
## # A tibble: 31,079 x 31
##       Rk Name    Age     G    PA    AB     R     H  `2B`  `3B`    HR   RBI    SB
##    <dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
##  1     1 Yosh…    17     5     3     2     1     2     0     0     0     1     0
##  2     2 Chuj…    18     7    27    24     2     5     1     0     0     3     1
##  3     3 Junz…    21    14    66    49     3    10     1     0     0     4     2
##  4     4 Kats…    29     5    14    13     3     4     0     0     0     3     1
##  5     5 Eiji…    19     9    34    28     4     7     0     0     0     3     0
##  6     6 Take…    26    13    59    51     5    11     0     0     0     6     1
##  7     7 Kazu…    19    14    69    60     7    13     4     0     0     5     3
##  8     8 Hisa…    17     7    15    10     1     2     1     0     0     1     0
##  9     9 Nori…    25    14    67    59     6    15     0     2     0     7     2
## 10    10 Yuki…    25    12    33    29     2     6     0     0     0     4     0
## # … with 31,069 more rows, and 18 more variables: CS <dbl>, BB <dbl>,
## #   SO <dbl>, BA <dbl>, OBP <dbl>, SLG <dbl>, OPS <dbl>, TB <dbl>, GDP <dbl>,
## #   HBP <dbl>, SH <dbl>, SF <dbl>, IBB <dbl>, League <chr>, Team <chr>,
## #   Year <dbl>, PlayerID <chr>, Bats <chr>

はい。今度は警告なしに読めましたね。

年齢分布

それでは、さっそく打者の年齢の分布を見ていきましょう。

# basic stuff
summary(df$Age)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##   16.00   23.00   26.00   26.82   30.00   48.00       1
# histogram
ggplot(df) + 
geom_histogram(aes(x = Age), binwidth = 1)
## Warning: Removed 1 rows containing non-finite values (stat_bin).

80年分のデータですが、それっぽい分布だと思います。

次はチームごとの年齢分布を見てみましょう。

# boxplots over teams
ggplot(df) + 
geom_boxplot(aes(x = Age, y = Team)) +
scale_y_discrete(limits = rev(unique(sort(df$Team))))
## Warning: Removed 1 rows containing non-finite values (stat_boxplot).

いろんなことが見えてきますね。チームによっては40代後半の選手がぱらぱらと在籍してますね。ぱっとみると南海あたりがいちばん年齢分布が若そうですが、、、並べ替えてみましょう。

# boxplots over teams in median-age-order
team_order<-df%>%
  group_by(Team)%>%
  summarize(median = median(Age, na.rm = TRUE))%>%
  arrange(median, Team)%>%
  select(Team)%>%
  unlist()

ggplot(df) + 
geom_boxplot(aes(x = Age, y = Team)) +
scale_y_discrete(limits = rev(team_order))
## Warning: Removed 1 rows containing non-finite values (stat_boxplot).

一番若いのは大東京軍、黒鷲軍、そして南海ですね。大東京軍は実は大洋ホエールズの一部だったんですね。親近感わきます。

今度は個別の選手を見てみましょう。

df%>%
  arrange(desc(Age))%>%
  select(Name, Age, Team, Year)%>%
  print(n = 20)
## # A tibble: 31,079 x 4
##    Name                   Age Team                 Year
##    <chr>                <dbl> <chr>               <dbl>
##  1 Shinji Hamasaki*        48 Hankyu Braves        1950
##  2 Masahiro Yamamoto*      48 Chunichi Dragons     2014
##  3 Yoshio Yuasa            47 Mainichi Orions      1950
##  4 Masahiro Yamamoto*      47 Chunichi Dragons     2013
##  5 Shinji Hamasaki*        46 Hankyu Braves        1948
##  6 Kimiyasu Kudo*          46 Yokohama Bay Stars   2009
##  7 Masahiro Yamamoto*      46 Chunichi Dragons     2012
##  8 Shinji Hamasaki*        45 Hankyu Braves        1947
##  9 Tadashi Wakabayashi?    45 Mainichi Orions      1953
## 10 Yoshiyuki Iwamoto       45 Toei Flyers          1957
## 11 Katsuya Nomura          45 Seibu Lions          1980
## 12 Toru Nimura             45 Chiba Lotte Marines  1997
## 13 Kimiyasu Kudo*          45 Yokohama Bay Stars   2008
## 14 Fumio Ono               44 Tokyu Flyers         1949
## 15 Yoshiyuki Iwamoto       44 Toei Flyers          1956
## 16 Katsuya Nomura          44 Seibu Lions          1979
## 17 Hiromitsu Kadota*       44 Fukuoka Daiei Hawks  1992
## 18 Toru Nimura             44 Chiba Lotte Marines  1996
## 19 Hiromitsu Ochiai        44 Nippon Ham Fighters  1998
## 20 Kimiyasu Kudo*          44 Yokohama Bay Stars   2007
## # … with 31,059 more rows

レジェンドばかりです・・・。ヤママサもすごいが、浜崎真二選手もすごい!45歳で選手兼総監督としてプロ野球に入団したらしいです・・・。今の自分よりも年上。

次は年代別に見ていきましょう。

# boxplots over years
ggplot(df) + 
geom_boxplot(aes(x = Age, y = factor(Year))) +
scale_y_discrete(limits = rev(unique(sort(factor(df$Year)))))
## Warning: Removed 1 rows containing non-finite values (stat_boxplot).

もう少し視覚に訴えるために、箱ひげ図ではなく、散布図にしてみましょう。

# scatter plot, Age x Year, #1
ggplot(df) + 
geom_point(aes(x = Age, y = Year), size = 1) + 
scale_y_reverse(expand=c(0,0), 
  breaks = function(x){scales::breaks_width(10)(c(min(x), max(x)))})
## Warning: Removed 1 rows containing missing values (geom_point).

なんだか点が少ないですね。3万人以上のデータがあるはずなんですが、こんなに少なく見えるのは、今回のような整数のデータの散布図だと、1つの座標に何個もデータがかぶることがあるからです。点が1個しかなくても、100個あっても、同じように見えてしまいます。そこで、geom_jitterを使うと見やすくなります。

# jitter plot, Age x Year
ggplot(df) + 
geom_jitter(aes(x = Age, y = Year), 
  width = 0.5, height = 0.3, size = 1, alpha = 0.3, shape = 16) + 
scale_y_reverse(expand=c(0,0), 
  breaks = function(x){scales::breaks_width(10)(c(min(x), max(x)))})
## Warning: Removed 1 rows containing missing values (geom_point).

戦時中の隙間がその後数年も影を引いているのがよくわかります。。。

点の重なりを解消する他の方法としては、密度に変換する方法もあります。

# density_2d #1, Age x Year
ggplot(df) + 
stat_density_2d(aes(x = Age, y = Year, fill = after_stat(density)),
  geom = "raster", contour = FALSE) + 
scale_x_continuous(expand = c(0,0)) +
scale_y_reverse(expand=c(0,0), 
  breaks = function(x){scales::breaks_width(10)(c(min(x), max(x)))}) +
scale_fill_distiller("Density", palette = "Spectral")
## Warning: Removed 1 rows containing non-finite values (stat_density2d).

あるいは、こんなこともできます。

# density_2d #2, Age x Year
ggplot(df) + 
stat_density_2d(aes(x = Age, y = Year, size = after_stat(density)), 
  geom = "point", n=30, contour = FALSE) +
scale_x_continuous(expand=c(0,0)) +
scale_y_reverse(expand=c(0,0), 
  breaks = function(x){scales::breaks_width(10)(c(min(x), max(x)))})
## Warning: Removed 1 rows containing non-finite values (stat_density2d).

打席の位置(左・右・スイッチ)

スイッチヒッターはいつの時代も憧れだと思います。実際に昔からスイッチの選手はどのくらいいたのでしょうか?さっそくみてみましょう。なお、同じ選手が年ごとに登録されているので、1選手を1カウントとするためにBatsとPlayerIDでuniqueを通しています。同じ選手が年によって異なる登録をしているとダブりますが。

# extract the batting stance info
batting_stances <- c("right", "left", "both", "unknown")
df2 <- df%>%
  select(Bats, PlayerID)%>%
  unique()%>%
  mutate(Bats = factor(Bats, batting_stances))

# count players who regsitered multiple batting-stances
df2%>%
  group_by(PlayerID)%>%
  summarize(n=n())%>%
  filter(n>1)
## # A tibble: 0 x 2
## # … with 2 variables: PlayerID <chr>, n <int>

ダブってませんね。では、とりあえず数を見てみましょう。

# basic stuff
ggplot(df2) +
geom_bar(aes(x = Bats))

全体的に右が多いのは納得ですね。それではさっそくチーム別にみてみましょう。

# distribution over teams, #1
df2 <- df%>%
  mutate(Bats = factor(Bats, batting_stances))

ggplot(df2) +
geom_bar(aes(y = Team, fill = Bats)) +
scale_fill_brewer(palette = "Set1") +
scale_x_continuous(expand=c(0,0)) +
scale_y_discrete(limits = rev(unique(sort(df$Team))))

# distribution over teams, #2
ggplot(df2) +
geom_histogram(aes(x = Team, fill = Bats), stat="count", position="fill") + 
scale_fill_brewer(palette = "Set1") +
scale_y_discrete("Ratio", expand=c(0,0)) +
scale_x_discrete(limits = rev(unique(sort(df$Team)))) +
coord_flip()
## Warning: Ignoring unknown parameters: binwidth, bins, pad

我らがホエールズの誇るスイッチヒッターを列挙しておきます。

df2%>%
  filter(str_detect(Team, "(Taiyo|Bay)"), Bats=="both")%>%
  select(Name, Team)%>%
  unique()%>%
  print(n=30)
## # A tibble: 29 x 2
##    Name                Team                 
##    <chr>               <chr>                
##  1 Chikara Morinaka#   Taiyo Whales         
##  2 Kaname Yashiki#     Yokohama Taiyo Whales
##  3 Yutaka Takagi#      Yokohama Taiyo Whales
##  4 Mark Budaska#       Yokohama Taiyo Whales
##  5 Hirokazu Kato#      Yokohama Taiyo Whales
##  6 Leon Lee#           Yokohama Taiyo Whales
##  7 Koichi Muraoka#     Yokohama Taiyo Whales
##  8 Masahiro Takahashi# Yokohama Taiyo Whales
##  9 Jerry White#        Yokohama Taiyo Whales
## 10 Takashi Okawa#      Yokohama Taiyo Whales
## 11 R.J. Reynolds#      Yokohama Taiyo Whales
## 12 Takashi Okawa#      Yokohama Bay Stars   
## 13 Yutaka Takagi#      Yokohama Bay Stars   
## 14 Masahiro Takahashi# Yokohama Bay Stars   
## 15 Kaname Yashiki#     Yokohama Bay Stars   
## 16 Tomo Ohka#          Yokohama Bay Stars   
## 17 Yuichi Furukawa#    Yokohama Bay Stars   
## 18 Kiyoshi Arai#       Yokohama Bay Stars   
## 19 Tatsuhiko Kinjo#    Yokohama Bay Stars   
## 20 Daisuke Kanda#      Yokohama Bay Stars   
## 21 Kazunori Tanaka#    Yokohama Bay Stars   
## 22 Cedrick Bowers#     Yokohama Bay Stars   
## 23 Marc Kroon#         Yokohama Bay Stars   
## 24 Daisuke Hayakawa#   Yokohama Bay Stars   
## 25 Kensuke Uchimura#   Yokohama Bay Stars   
## 26 Kazuki Mishima#     Yokohama Bay Stars   
## 27 Masashi Nishimori#  Yokohama Bay Stars   
## 28 Hiroki Minei#       Yokohama Bay Stars   
## 29 Elian Herrera#      Yokohama Bay Stars

スーパーカートリオがきちんと入ってますね。そして、三嶋一輝選手がスイッチであることや、このデータからは全くわかりませんが、彼が周船寺小学校出身であることを知りました。テンション上がります。

次は年代ごとの分布を見ていきましょう。

# distribution over time, #1
ggplot(df2) +
geom_bar(aes(y = Year, fill = Bats)) +
scale_fill_brewer(palette = "Set1") +
scale_x_continuous(expand = c(0,0)) +
scale_y_reverse(expand = c(0,0), 
  breaks = function(x){scales::breaks_width(10)(c(min(x), max(x)))})

# distribution over time, #2
ggplot(df2) +
geom_histogram(aes(x = Year, fill = Bats), stat="count", position="fill") + 
scale_fill_brewer(palette = "Set1") +
scale_y_discrete("Ratio", expand=c(0,0)) +
scale_x_reverse(expand=c(0,0),
  breaks = function(x){scales::breaks_width(10)(c(min(x), max(x)))}) +
coord_flip()
## Warning: Ignoring unknown parameters: binwidth, bins, pad

これをみると、左バッターが徐々に増えていること、スイッチは60年代以降に増えてきたが、ピークは90年代あたりということがわかります。

sessioninfo::session_info(c("tidyverse"))
## ─ Session info ───────────────────────────────────────────────────────────────
##  setting  value                       
##  version  R version 3.6.3 (2020-02-29)
##  os       macOS Mojave 10.14.6        
##  system   x86_64, darwin15.6.0        
##  ui       RStudio                     
##  language (EN)                        
##  collate  ja_JP.UTF-8                 
##  ctype    ja_JP.UTF-8                 
##  tz       Asia/Tokyo                  
##  date     2020-05-18                  
## 
## ─ Packages ───────────────────────────────────────────────────────────────────
##  package      * version  date       lib source        
##  askpass        1.1      2019-01-13 [1] CRAN (R 3.6.0)
##  assertthat     0.2.1    2019-03-21 [1] CRAN (R 3.6.0)
##  backports      1.1.6    2020-04-05 [1] CRAN (R 3.6.2)
##  base64enc      0.1-3    2015-07-28 [1] CRAN (R 3.6.0)
##  BH             1.72.0-3 2020-01-08 [1] CRAN (R 3.6.0)
##  broom          0.5.6    2020-04-20 [1] CRAN (R 3.6.2)
##  callr          3.4.3    2020-03-28 [1] CRAN (R 3.6.2)
##  cellranger     1.1.0    2016-07-27 [1] CRAN (R 3.6.0)
##  cli            2.0.2    2020-02-28 [1] CRAN (R 3.6.0)
##  clipr          0.7.0    2019-07-23 [1] CRAN (R 3.6.0)
##  colorspace     1.4-1    2019-03-18 [1] CRAN (R 3.6.0)
##  crayon         1.3.4    2017-09-16 [1] CRAN (R 3.6.0)
##  curl           4.3      2019-12-02 [1] CRAN (R 3.6.0)
##  DBI            1.1.0    2019-12-15 [1] CRAN (R 3.6.0)
##  dbplyr         1.4.3    2020-04-19 [1] CRAN (R 3.6.2)
##  desc           1.2.0    2018-05-01 [1] CRAN (R 3.6.0)
##  digest         0.6.25   2020-02-23 [1] CRAN (R 3.6.0)
##  dplyr        * 0.8.5    2020-03-07 [1] CRAN (R 3.6.0)
##  ellipsis       0.3.0    2019-09-20 [1] CRAN (R 3.6.0)
##  evaluate       0.14     2019-05-28 [1] CRAN (R 3.6.0)
##  fansi          0.4.1    2020-01-08 [1] CRAN (R 3.6.0)
##  farver         2.0.3    2020-01-16 [1] CRAN (R 3.6.0)
##  forcats      * 0.5.0    2020-03-01 [1] CRAN (R 3.6.0)
##  fs             1.4.1    2020-04-04 [1] CRAN (R 3.6.3)
##  generics       0.0.2    2018-11-29 [1] CRAN (R 3.6.0)
##  ggplot2      * 3.3.0    2020-03-05 [1] CRAN (R 3.6.0)
##  glue           1.4.0    2020-04-03 [1] CRAN (R 3.6.1)
##  gtable         0.3.0    2019-03-25 [1] CRAN (R 3.6.0)
##  haven          2.2.0    2019-11-08 [1] CRAN (R 3.6.0)
##  highr          0.8      2019-03-20 [1] CRAN (R 3.6.0)
##  hms            0.5.3    2020-01-08 [1] CRAN (R 3.6.0)
##  htmltools      0.4.0    2019-10-04 [1] CRAN (R 3.6.0)
##  httr           1.4.1    2019-08-05 [1] CRAN (R 3.6.0)
##  isoband        0.2.1    2020-04-12 [1] CRAN (R 3.6.2)
##  jsonlite       1.6.1    2020-02-02 [1] CRAN (R 3.6.0)
##  knitr        * 1.28     2020-02-06 [1] CRAN (R 3.6.0)
##  labeling       0.3      2014-08-23 [1] CRAN (R 3.6.0)
##  lattice        0.20-41  2020-04-02 [1] CRAN (R 3.6.3)
##  lifecycle      0.2.0    2020-03-06 [1] CRAN (R 3.6.0)
##  lubridate      1.7.8    2020-04-06 [1] CRAN (R 3.6.2)
##  magrittr       1.5      2014-11-22 [1] CRAN (R 3.6.0)
##  markdown     * 1.1      2019-08-07 [1] CRAN (R 3.6.0)
##  MASS           7.3-51.6 2020-04-26 [1] CRAN (R 3.6.2)
##  Matrix         1.2-18   2019-11-27 [1] CRAN (R 3.6.3)
##  mgcv           1.8-31   2019-11-09 [1] CRAN (R 3.6.3)
##  mime           0.9      2020-02-04 [1] CRAN (R 3.6.0)
##  modelr         0.1.7    2020-04-30 [1] CRAN (R 3.6.2)
##  munsell        0.5.0    2018-06-12 [1] CRAN (R 3.6.0)
##  nlme           3.1-147  2020-04-13 [1] CRAN (R 3.6.2)
##  openssl        1.4.1    2019-07-18 [1] CRAN (R 3.6.0)
##  pillar         1.4.4    2020-05-05 [1] CRAN (R 3.6.3)
##  pkgbuild       1.0.8    2020-05-07 [1] CRAN (R 3.6.3)
##  pkgconfig      2.0.3    2019-09-22 [1] CRAN (R 3.6.0)
##  pkgload        1.0.2    2018-10-29 [1] CRAN (R 3.6.0)
##  plogr          0.2.0    2018-03-25 [1] CRAN (R 3.6.0)
##  plyr           1.8.6    2020-03-03 [1] CRAN (R 3.6.0)
##  praise         1.0.0    2015-08-11 [1] CRAN (R 3.6.0)
##  prettyunits    1.1.1    2020-01-24 [1] CRAN (R 3.6.0)
##  processx       3.4.2    2020-02-09 [1] CRAN (R 3.6.0)
##  progress       1.2.2    2019-05-16 [1] CRAN (R 3.6.0)
##  ps             1.3.2    2020-02-13 [1] CRAN (R 3.6.0)
##  purrr        * 0.3.4    2020-04-17 [1] CRAN (R 3.6.2)
##  R6             2.4.1    2019-11-12 [1] CRAN (R 3.6.0)
##  RColorBrewer   1.1-2    2014-12-07 [1] CRAN (R 3.6.0)
##  Rcpp           1.0.4.6  2020-04-09 [1] CRAN (R 3.6.3)
##  readr        * 1.3.1    2018-12-21 [1] CRAN (R 3.6.0)
##  readxl         1.3.1    2019-03-13 [1] CRAN (R 3.6.0)
##  rematch        1.0.1    2016-04-21 [1] CRAN (R 3.6.0)
##  reprex         0.3.0    2019-05-16 [1] CRAN (R 3.6.0)
##  reshape2       1.4.4    2020-04-09 [1] CRAN (R 3.6.2)
##  rlang          0.4.6    2020-05-02 [1] CRAN (R 3.6.2)
##  rmarkdown    * 2.1      2020-01-20 [1] CRAN (R 3.6.0)
##  rprojroot      1.3-2    2018-01-03 [1] CRAN (R 3.6.0)
##  rstudioapi     0.11     2020-02-07 [1] CRAN (R 3.6.0)
##  rvest          0.3.5    2019-11-08 [1] CRAN (R 3.6.0)
##  scales         1.1.0    2019-11-18 [1] CRAN (R 3.6.0)
##  selectr        0.4-2    2019-11-20 [1] CRAN (R 3.6.1)
##  stringi        1.4.6    2020-02-17 [1] CRAN (R 3.6.0)
##  stringr      * 1.4.0    2019-02-10 [1] CRAN (R 3.6.0)
##  sys            3.3      2019-08-21 [1] CRAN (R 3.6.0)
##  testthat       2.3.2    2020-03-02 [1] CRAN (R 3.6.0)
##  tibble       * 3.0.1    2020-04-20 [1] CRAN (R 3.6.2)
##  tidyr        * 1.0.3    2020-05-07 [1] CRAN (R 3.6.3)
##  tidyselect     1.0.0    2020-01-27 [1] CRAN (R 3.6.0)
##  tidyverse    * 1.3.0    2019-11-21 [1] CRAN (R 3.6.0)
##  tinytex        0.22     2020-04-17 [1] CRAN (R 3.6.2)
##  utf8           1.1.4    2018-05-24 [1] CRAN (R 3.6.0)
##  vctrs          0.2.4    2020-03-10 [1] CRAN (R 3.6.0)
##  viridisLite    0.3.0    2018-02-01 [1] CRAN (R 3.6.0)
##  whisker        0.4      2019-08-28 [1] CRAN (R 3.6.0)
##  withr          2.2.0    2020-04-20 [1] CRAN (R 3.6.2)
##  xfun           0.13     2020-04-13 [1] CRAN (R 3.6.2)
##  xml2           1.3.2    2020-04-23 [1] CRAN (R 3.6.2)
##  yaml           2.2.1    2020-02-01 [1] CRAN (R 3.6.0)
## 
## [1] /Library/Frameworks/R.framework/Versions/3.6/Resources/library

genshiro

シェアする