Jupyter NotebookでKotlinが利用できるようになったので触ってみた

本記事はコネヒト [Advent Calendar 2019](https://qiita.com/advent-calendar/2019/connehito) 24日目の記事になります。

## はじめに
[Jupyter Notebook](https://jupyter.org/)はプログラムコードや数式、図、説明文などを含む文書を作成し、共有することが出来る、データ解析用のWebアプリケーションです。一見するとJupyter NotebookとKotlinはあまり関連性がなさそうに見えますが、Jupyter NotebookでもKotlinが使えるようになったので、触ってみた内容を共有したいと思います。

## Jupyter NotebookでKotlinが利用できることを知った
[KotlinConf 2019](https://kotlinconf.com/)にて、Kotlin 1.4、Coroutines、Multiplatform、Spaceなど、Kotlinの最新情報からチーム開発を円滑に進めるためのツールまで様々なセッションがありました。たくさんのセッションがあった中にKotlinを用いたデータサイエンスに関連するセッションが3つありました。
<img width="917" alt="スクリーンショット 2019-12-24 7.06.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23391/26d269ec-6afa-46ef-5a26-623b51368f0b.png">
その中でもRoma BelovさんのセッションではKotlinを用いたデータサイエンスについて[発表](https://www.youtube.com/watch?v=APnyDVye4JA)されて、主にツールやライブラリなどの側面からデータサイエンス に適用されるKotlinのエコシステムの説明がありました。また、Jupyter NotebookでKotlinが利用できる点にも触れています。

## Jupyter NotebookでKotlinを利用するには？
Jupyter NotebookでKotlinを使うには、Kernelが必要になります。Kernelとは、入力されたコードをインタラクティブに処理して結果を返すプロセスのことを表します。すでに[様々なKernel](https://github.com/jupyter/jupyter/wiki/Jupyter-kernels)があり、KotlinのKernelは[kotlin-jupyter-kernel](https://github.com/Kotlin/kotlin-jupyter)になります。Jupyter Notebook自体のインストール方法については割愛させていただきますが、kotlin-jupyter-kernelは、以下のようにCondaパッケージを使ってインストールします。kotlin-jupyter-kernelをインストール後にJupyter Notebookを起動し、「New -> Notebooks -> Kotlin」とKotlinのNotebookを作成できることがわかります。

```
$ conda install kotlin-jupyter-kernel -c jetbrains
$ jupyter notebook
```

![スクリーンショット 2019-12-24 11.31.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23391/3c79c94f-8bc3-a1c1-d0b4-c9c732e93765.png)

Notebookを新規作成して、Hello Worldを試した例です。また、ローカル環境だけでなく[Binder](https://mybinder.org/v2/gh/cheptsov/kotlin-jupyter-demo/master?filepath=index.ipynb)というサービスからJupyter NotebookをWeb上で簡単に試すこともできます。

![スクリーンショット 2019-12-24 11.39.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23391/b052c762-9365-1ac3-de77-e75b9205ff81.png)

## データサイエンスのライブラリについて
ここまでJupyter NotebookでKotlinを使う部分について紹介しました。では、実際にデータ分析をするにはどうするのでしょうか。Kotlinには、以下のようにすでに多くのライブラリが存在します。下図の青い項目がKotlinのライブラリになります。

<img width="767" alt="スクリーンショット 2019-12-24 7.07.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23391/a63c0a5e-b0c8-62e3-1d4d-753978690626.png">

### lets-plotライブラリ
いくつかKotlinライブラリがあることがわかりましたが、ここからは[lets-plot](https://github.com/JetBrains/lets-plot)と[krangl](https://github.com/holgerbrandl/krangl)のデータ操作とチャートに関するライブラリに絞って紹介したいと思います。まずはlets-plotで、これはチャートを簡単に描画できるライブラリです。具体的には以下のチャートが[サポート](https://github.com/JetBrains/lets-plot/blob/6a4006d21664c9aa5f44c6d89d6cb30a06064b12/docs/ref/index.md)されており、描画サンプルは以下の通りです。

|チャート|メソッド名|
|:--|:--|
|Area chart|geom_area()|
|Bar chart|geom_bar()|
|Boxplot chart|geom_boxplot()|
|Contour chart|geom_contour(), geom_contourf()|
|Density chart|geom_density() and geom_density2d()|
|Error bar chart|geom_errorbar()|
|Historgam|geom_histogram()|
|Line chart|geom_line()|
|Scatter chart|geom_point()|
|Polygon chart|geom_polygon()|
|Rectangle chart, Tile chart|geom_rect(), geom_tile()|
|Image plot|geom_image()|

#### Boxplot chart
<img width="1030" alt="スクリーンショット 2019-12-24 12.03.33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23391/e0e8b699-0600-1fb1-2aa9-c434ccbb8611.png">

#### Historgam chart
<img width="1035" alt="スクリーンショット 2019-12-24 12.18.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23391/59454fab-ccb0-f4af-157e-4506ccbf9fb7.png">

#### Line chart
<img width="1036" alt="スクリーンショット 2019-12-24 12.31.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23391/e2c0c024-c18a-2495-1c9a-eca35cf6bcbc.png">

#### Bar chart
<img width="1035" alt="スクリーンショット 2019-12-24 12.37.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23391/d6d3d63f-5ec2-37a6-50f4-ffcd02813f1c.png">

#### Scatter chart
<img width="1038" alt="スクリーンショット 2019-12-24 12.42.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23391/bd6af568-35fc-8887-c064-702dad4a6360.png">

#### Tile chart
<img width="1038" alt="スクリーンショット 2019-12-24 12.47.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23391/24dfc97e-f14c-a4ba-821d-8ccb94586c43.png">

#### Density chart
<img width="1037" alt="スクリーンショット 2019-12-24 12.53.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23391/949e253f-c83c-09cf-cb37-2ab3cd2eb412.png">

### kranglライブラリ
次にkranglライブラリについて説明します。データを行列のデータ形式に変換、集計、フィルタリングなど、データフレームの基本的な操作が行えます。kranglにはirisDataがバンドルされているのでそれらも用いてサンプルを作ってみます。

#### irisDataの表示
<img width="1034" alt="スクリーンショット 2019-12-24 13.05.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23391/54098b32-f635-2908-9b2d-a3f4e17a4a50.png">

#### データフレームの作成と表示
<img width="1037" alt="スクリーンショット 2019-12-24 13.16.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23391/a30f4baf-2e71-abda-6f63-49a35e4a4546.png">

## Slackデータを用いたデータ分析
ここまで、Jupyter NotebookとKotlin Kernelとチャートとデータ操作ライブラリについて説明しました。では、Slackデータを用いて簡単なデータ分析をしてみましょう。Slackデータに関しては、権限があればワークスペースからデータを簡単に[エクスポート](https://slack.com/intl/ja-jp/help/articles/201658943)できます。ワークスペースのデータをエクスポートすると、メッセージの履歴やファイルへのリンクにアクセスできる .zip ファイルをダウンロードできます。 データエクスポートには以下[2種類](https://slack.com/intl/ja-jp/help/articles/220556107-How-to-read-Slack-data-exports)あり、ワークスペースのオーナーと管理者によってのみ実行可能です。

|| 公開データ | ワークスペースの全データ |
|:--|:--|:--|
| channels.json |  |  |
| dms.json |   |  |
| groups.json |   |  |
| integration_logs.json |  |  |
| mpims.json |   |  |
| users.json |  |  |
| 全チャンネルのフォルダ |  |  |

いくつかサンプルを紹介しますが、コネヒトのSlackデータを用いてデータ抽出を行いましたので、一部モザイクをかけています。

### channels.jsonから参加者の多い上位のチャンネルを抽出する
```kotlin
// krangl, lets-plotのインポート
%use krangl, lets-plot

// Json配列からList型に変換
fun readFromJsonString(s: String) =
    s.removePrefix("JsonArray(value=[")
    .removeSuffix("])")
    .split(",")
    .dropLastWhile { it.isEmpty() }
    .toList()

// データフレームの生成
val channels = DataFrame.fromJson("channels.json")
    .addColumn("channle_members") { it["members"].map<String> { value -> readFromJsonString(value) } }
    .addColumn("count") { it["channle_members"].map<List<String>> { it.count() } }
    .filter { it["is_archived"] eq false }
    .select("id", "name", "channle_members", "count")
    .sortedByDescending("count")

// 出力用にデータの整形
val data = mapOf(
    "name" to channels["name"].asStrings().toList(),
    "count" to channels["count"].asInts().toList()
)

// グラフの描画
lets_plot(data) + 
    geom_bar(stat = Stat.identity) {
        x = "name"
        y = "count"
    }
```

![plots1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23391/862233d6-8004-c2fe-4562-36e26ce104c3.png)

### users.jsonからTimeZone別にユーザを抽出する
```kotlin
// krangl, lets-plotのインポート
%use krangl, lets-plot

// データフレームの生成
val channels = DataFrame.fromJson("users.json")
    .filterByRow { (it["tz"] as? String)?.isNotEmpty() == true }
    .filterByRow { (it["deleted"] as Boolean) == false }
    .select("id", "name", "tz")
    .sortedByDescending("id")

// tz毎にグルーピング
val groupData = channels.groupBy("tz").count()

// 出力用にデータの整形
val data = mapOf(
    "name" to groupData["tz"].asStrings().toList(),
    "count" to groupData["n"].asInts().toList()
)

// グラフの描画
lets_plot(data) + 
    geom_bar(stat = Stat.identity) {
        x = "name"
        y = "count"
    }

```

![スクリーンショット 2019-12-23 18.32.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23391/5053cfea-4332-220d-9b29-b3a14051d931.png)

### integration_logs.jsonからSlackのインテグレーションタイプ別に抽出する

```kotlin
// krangl, lets-plotのインポート
%use krangl, lets-plot

// データフレームの生成
val channels = DataFrame.fromJson("integration_logs.json")
    .filterByRow { (it["service_type"] as? String)?.isNotEmpty() == true }
    .select("service_type")

// service_type毎にグルーピング
val groupData = channels.groupBy("service_type").count()

// 出力用にデータの整形
val data = mapOf(
    "name" to groupData["service_type"].asStrings().toList(),
    "count" to groupData["n"].asInts().toList()
)

// グラフの描画
lets_plot(data) + 
    geom_bar(stat = Stat.identity) {
        x = "name"
        y = "count"
    }
```

![plots2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23391/5a703318-626f-5739-dc4c-c3c36585d505.png)

## 最後に
KotlinでJupyter Notebookを触ってみました。KotlinはAndroidやサーバサイドで利用されている事例は増えていますが、データサイエンスの事例がとても少なかったので興味深かったです。すでに多くのライブラリが開発されており、何かしらコミュニティに貢献できればと考えています。

## PR
コネヒトではエンジニアを募集しています！家族向けサービスをつくりたいエンジニアの皆さん、お待ちしています。<a href="https://www.wantedly.com/projects/290082">
  <img src="https://d2v9k5u4v94ulw.cloudfront.net/small_light(dw=1956,dh=796,da=s,ds=s,cw=1956,ch=796,cc=FFFFFF,of=webp)/assets/images/3591158/original/57a7b345-04dc-492c-8166-b68717e17079?1552905339" alt="Connehito Image">
  <span>家族の課題を解決するサービスのMAUを増やすAndroidエンジニア募集！</span>
</a>