Explorer: LiveBookでTidyverse

## 3度目の正直

今週は3つの起こったことのおかげで、データ分析の楽しさを思い出しました。

最初は[Thinking Elixirポッドキャスト](https://podcast.thinkingelixir.com/104)のゲストChris GrangerさんはElixirのNx Explorerのライブラリを説明しました。このライブラリーはRの「Tidyverse」の「dplyr」関数をElixirに持ってます。グレインジャーさんと同じく、NxとLiveViewを初めて聞いたときに、R Studioより使いやすいかExploratoryのスクリプトのみのことができるかなと思い出しました。早く使ってみたいと思いましたが、まぁ、仕事に集中して、週末にしましょう。

次は@RyoWakabayashiさんは「[Elixir でデータ分析 Explorer を使ってみた](https://qiita.com/RyoWakabayashi/items/dfb5495b9c25eff710a2)」の記事をここに書きました。いきなり、
```elixir
Mix.install([
  {:explorer, "~> 0.2.0"}
])
```
をで初期化して、いろんな例を書きました。`filter`, `select`, `transform`、`group_by`などの機能のれいを次々書いてくれました。これが最高です。やりたいことの例が全てあります。

昼時間に、昔 R Studioでやった分割を探してみました。うん、今コンピュータにないので、仕事の後にバックアップで探してみようと思いました。

でも、その午後に「[Japan Baseball Weekly Podcast](https://www.japanesebaseball.com/blogs/blog.jsp?blogid=1197)」の John E. Gibsonさんは「今年、阪神の大山悠輔選手はよく一球目の投球を打ってないですか?」と聞きました。もう、週末までに待てられない。

その聞いたの質問を2021年と2022年のデータで文献しましたが、まぁ、平均の選手より早いカウントで打ってると確認しましたが、例外のようではなかった。今度の12.18のショーにその結果を発表します。

その結果のMP3を送った後に、ギブソンさんは「一球目にホームランは?」と聞きました。うん、面白そうですね。「ちょっとお待ち下さい」と返事して、フィルターを追加して、結果で驚きました。

以下にその結果を最初から再現しましょう。

## 準備

最初はデータが必要です。データベースから安打の結果のレコードのみを取得して、何球の投球を含まれます。

LiveBookに「`:explorer, "=> 0.2.0"}`でExplorerをロードして、新しい「Elixir」分を作成します。その中に、
```elixir
import Explorer.DataFrame
alias Explorer.Series

df = from_csv!(
  "/path/to/2022-batters-pitches-to-hits.tsv",
  delimiter: "\t"
)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710912/606f7f0e-e40a-c273-9185-2f0d9e88714f.png)
若林さんは`import Explorer.DataFrame`ではなくて、`alias Explorer.DataFrame`を使用しました。うん、何度も何度も「`DataFrame.関数名`を打ちたくないので、直接関数を使ってます。例えば、`from_csv!`は`DataFrame`モジュールの関数ですが、`DataFrame`を抜くとよりわかりやすいかなと思います。

データフレームの構造が見えるとは好きですが、本当に表で表示してほしいです。`DataFrame.table()`でできるよ。
```elixir
df
|> table()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710912/977c6fff-26b2-e5a0-9c13-47ec57e83feb.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710912/27e35a22-8e52-b052-05d6-6fab6987b099.png)
右にスクロールすると「`home-run`」の結果が見えます。よっしゃー。

## フィルター

今のデータが多すぎる。実際に「投球の数が1」と「打類がホームラン」だけを利用したいです。
```elixir
df
|> filter(&Series.equal(&1["Pitches"], 1))
|> filter(&Series.equal(&1["HitType"], "home-run"))
|> table()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710912/7d58b396-2216-2931-c9bb-bb7f9d800f94.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710912/0ee2fead-2e39-4b10-e039-46b1a6a6d059.png)
この結果を見ると、以上の条件のデータのみを表示しています。

でも、グラシアル選手のセルが槙原選手より幅が広いです。これを見ると1980年代のShift_JISがわからない`sprintf`関数の時代とか1990後半のUnicode関連ライブラリーが思い出します。うん、Elixirの`String.pad_leading`と`String.pad_trailing`関数がCJK互換性が必要ですか? どうやってその幅を計算していますか? もっと時間があったらPRを書きたくなります。

## グループ化して要約する

では、引き続き、選手とチームをグループ化して、ホームランの数を計算しましょう。
```elixir
df
|> filter(&Series.equal(&1["Pitches"], 1))
|> filter(&Series.equal(&1["HitType"], "home-run"))
|> group_by(["選手", "Team"])
|> summarise("Pitches": [:count])
|> table()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710912/467e773c-927b-cc89-ac9a-b039b2cbbf49.png)
牧原選手の2つレコードが一つの集まって、2本塁を打ちました。よし。

## 名前の変更と配置（並べ替え）

次は`Pitches_count`という項目名を「本塁打」に変更して、「チーム」も入れましょう。そうして、最多の本塁打を上から並び替えましょう。
```elixir
df
|> filter(&Series.equal(&1["Pitches"], 1))
|> filter(&Series.equal(&1["HitType"], "home-run"))
|> group_by(["選手", "Team"])
|> summarise("Pitches": [:count])
|> rename("Pitches_count": "本塁打", "Team": "チーム")
|> arrange(desc: "本塁打")
|> table()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710912/cc2e13b1-abc7-14d2-6ea7-d119d58da5aa.png)
うん、あの`HAN`、`YOM`、などより`神`、`巨`、などになるはずです。チーム列名をリネームするより`mutate`しましょう。

最初は、変更したいマップを作成して、
```elixir
teams = %{
  "CHU" => "中", "HAN" => "神", "HIR" => "広", "YAK" => "ヤ", "YOK" => "De", "YOM" => "巨",
  "LOT" => "ロ", "NIP" => "日", "ORX" => "オ", "RAK" => "楽", "SEI" => "西", "SFT" => "ソ"
}
```
そうして、`mutate`は新しい「チーム」列を作成して、リネームが必要なくなります。
```elixir
df
|> filter(&Series.equal(&1["Pitches"], 1))
|> filter(&Series.equal(&1["HitType"], "home-run"))
|> mutate("チーム": &Series.transform(&1["Team"], fn team -> Map.get(teams, team) end))
|> group_by(["選手", "チーム"])
|> summarise("Pitches": [:count])
|> rename("Pitches_count": "本塁打")
|> arrange(desc: "本塁打")
|> table()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710912/de4ccdc8-63fd-0cca-4614-985a4c9f672a.png)
よっしゃー。ギブソンさんは正しかった。大山選手は一球目の本塁打がかんなり多いでしょう。巨人の岡本さんも昨日(6月24日にも一球目に打ちました。

## テーブルの全体

あっ、最後に`table()`は最初の5つレコードしか表示していません。全部を見たいので、これに変更しましょう:
```elixir
|> table(limit: :infinity)
```
すると、
```elixir
+--------------------------------------------+
| Explorer DataFrame: [rows: 75, columns: 3] |
+-------------------+-------------+----------+
|        選手         |     チーム     |   本塁打    |
|     <string>      |  <string>   |  <nil>   |
+===================+=============+==========+
| 大山 悠輔             | 神           | 6        |
+-------------------+-------------+----------+
| 岡本 和真             | 巨           | 6        |
+-------------------+-------------+----------+
| 村上 宗隆             | ヤ           | 5        |
+-------------------+-------------+----------+
| 万波 中正             | 日           | 4        |
+-------------------+-------------+----------+
| 柳田 悠岐             | ソ           | 4        |
+-------------------+-------------+----------+
| 中田 翔              | 巨           | 4        |
+-------------------+-------------+----------+
| 牧 秀悟              | De          | 4        |
+-------------------+-------------+----------+
| 山川 穂高             | 西           | 3        |
+-------------------+-------------+----------+
| 佐藤 輝明             | 神           | 3        |
+-------------------+-------------+----------+
| ポランコ グレゴリー        | 巨           | 3        |
+-------------------+-------------+----------+
| 丸 佳浩              | 巨           | 3        |
+-------------------+-------------+----------+
| ウォーカー アダム         | 巨           | 3        |
+-------------------+-------------+----------+
| 佐野 恵太             | De          | 3        |
+-------------------+-------------+----------+
| 塩見 泰隆             | ヤ           | 3        |
+-------------------+-------------+----------+
| 牧原 大成             | ソ           | 2        |
+-------------------+-------------+----------+
| 中村 奨吾             | ロ           | 2        |
+-------------------+-------------+----------+
| レアード ブランドン        | ロ           | 2        |
+-------------------+-------------+----------+
| マーティン レオネス        | ロ           | 2        |
+-------------------+-------------+----------+
| 淺間 大基             | 日           | 2        |
+-------------------+-------------+----------+
| 石井 一成             | 日           | 2        |
+-------------------+-------------+----------+
| 浅村 栄斗             | 楽           | 2        |
+-------------------+-------------+----------+
| 三森 大貴             | ソ           | 2        |
+-------------------+-------------+----------+
| ロハス・ジュニア メル       | 神           | 2        |
+-------------------+-------------+----------+
| 大城 卓三             | 巨           | 2        |
+-------------------+-------------+----------+
| 長岡 秀樹             | ヤ           | 2        |
+-------------------+-------------+----------+
| 濱田 太貴             | ヤ           | 2        |
+-------------------+-------------+----------+
| 西川 龍馬             | 広           | 2        |
+-------------------+-------------+----------+
| 鵜飼 航丞             | 中           | 2        |
+-------------------+-------------+----------+
| ビシエド ダヤン          | 中           | 2        |
+-------------------+-------------+----------+
| 石川 昂弥             | 中           | 2        |
+-------------------+-------------+----------+
| 京田 陽太             | 中           | 2        |
+-------------------+-------------+----------+
| グラシアル ユリスベル       | ソ           | 1        |
+-------------------+-------------+----------+
| 中村 剛也             | 西           | 1        |
+-------------------+-------------+----------+
| バレラ ブレイビック        | オ           | 1        |
+-------------------+-------------+----------+
| 紅林 弘太郎            | オ           | 1        |
+-------------------+-------------+----------+
| 杉本 裕太郎            | オ           | 1        |
+-------------------+-------------+----------+
| 佐藤 都志也            | ロ           | 1        |
+-------------------+-------------+----------+
| エチェバリア アデイニー      | ロ           | 1        |
+-------------------+-------------+----------+
| 近藤 健介             | 日           | 1        |
+-------------------+-------------+----------+
| 松本 剛              | 日           | 1        |
+-------------------+-------------+----------+
| 今川 優馬             | 日           | 1        |
+-------------------+-------------+----------+
| ヌニエス レナート         | 日           | 1        |
+-------------------+-------------+----------+
| 宇佐見 真吾            | 日           | 1        |
+-------------------+-------------+----------+
| 山口 航輝             | ロ           | 1        |
+-------------------+-------------+----------+
| 清宮 幸太郎            | 日           | 1        |
+-------------------+-------------+----------+
| アルカンタラ アリスメンディ    | 日           | 1        |
+-------------------+-------------+----------+
| 島内 宏明             | 楽           | 1        |
+-------------------+-------------+----------+
| 鈴木 大地             | 楽           | 1        |
+-------------------+-------------+----------+
| マルモレホス ホセ         | 楽           | 1        |
+-------------------+-------------+----------+
| 辰己 涼介             | 楽           | 1        |
+-------------------+-------------+----------+
| 中野 拓夢             | 神           | 1        |
+-------------------+-------------+----------+
| 小幡 竜平             | 神           | 1        |
+-------------------+-------------+----------+
| 糸井 嘉男             | 神           | 1        |
+-------------------+-------------+----------+
| 立岡 宗一郎            | 巨           | 1        |
+-------------------+-------------+----------+
| 倉本 寿彦             | De          | 1        |
+-------------------+-------------+----------+
| 宮﨑 敏郎             | De          | 1        |
+-------------------+-------------+----------+
| 山田 哲人             | ヤ           | 1        |
+-------------------+-------------+----------+
| サンタナ ドミンゴ         | ヤ           | 1        |
+-------------------+-------------+----------+
| 小園 海斗             | 広           | 1        |
+-------------------+-------------+----------+
| 坂倉 将吾             | 広           | 1        |
+-------------------+-------------+----------+
| 會澤 翼              | 広           | 1        |
+-------------------+-------------+----------+
| 阿部 寿樹             | 中           | 1        |
+-------------------+-------------+----------+
| 木下 拓哉             | 中           | 1        |
+-------------------+-------------+----------+
| 宇草 孔基             | 広           | 1        |
+-------------------+-------------+----------+
| マルティネス アリエル       | 中           | 1        |
+-------------------+-------------+----------+
| 大田 泰示             | De          | 1        |
+-------------------+-------------+----------+
| 山崎 晃大朗            | ヤ           | 1        |
+-------------------+-------------+----------+
| 梶原 昂希             | De          | 1        |
+-------------------+-------------+----------+
| ソト ネフタリ           | De          | 1        |
+-------------------+-------------+----------+
| マッカーシー ジョー        | オ           | 1        |
+-------------------+-------------+----------+
| 西 純矢              | 神           | 1        |
+-------------------+-------------+----------+
| 中村 悠平             | ヤ           | 1        |
+-------------------+-------------+----------+
| 岡田 貴弘             | オ           | 1        |
+-------------------+-------------+----------+
| 川島 慶三             | 楽           | 1        |
+-------------------+-------------+----------+
| 安田 悠馬             | 楽           | 1        |
+-------------------+-------------+----------+
```
すべて一球目の本塁打を打った選手が表示されます。

## まとめ

数年間ぶりデータをこんなに楽しんで分析してます。テンサーなどのMLではなくて、NxとLiveBookを初めてみたときにこんなデータで遊びの使い方でやりたい気持ちがありました。今週、グレインジャーさん、若林さん、とギブソンさんのおかげで未来のデータ分析が楽しいそうです。