関数型でデータサイエンス#4：インプットしたデータを集約する②

（この記事は[「fukuoka.ex x ザキ研 Advent Calendar 2017」](https://adventar.org/calendars/2873)の21日目です)

昨日は、@kobatako さんの[「GraphQL for Elixir#3 Middlewareと認証について考える」](https://qiita.com/kobatako/items/dcc9fc5a603d797edea9)でした

----
[fukuoka.ex](https://fukuokaex.fun/)代表の[piacere](https://twitter.com/piacere_ex)です
今回もご覧いただいて、ありがとうございます:bow:

この連載の、前回までの記事は、以下になります
　|> [<b>関数型でデータサイエンス#1：様々なデータをインプットする</b>](https://qiita.com/piacere_ex/items/f7077f0ff1b1a6c7a959)
　|> [<b>関数型でデータサイエンス#2：インプットしたデータを変換する</b>](https://qiita.com/piacere_ex/items/64c2127857599137e8a7)
　|> [<b>関数型でデータサイエンス#3：インプットしたデータを集約する①</b>](https://qiita.com/piacere_ex/items/ef434011d154dc73f0cd)

今回は、前回の前半に続き、「インプットしたデータの集約」の後半を解説します

ElixirのEnumが、平均値・中央値などの「統計」機能を標準で計算できないため、ここを対応していきます

サンプルデータ等は、[前回](https://qiita.com/piacere_ex/items/64c2127857599137e8a7)と同じものを使います
<br>
:shamrock::shamrock::shamrock:<b>　お礼：6/22のfukuoka.ex#11、おかげさまで盛り上がりました　:shamrock::shamrock::shamrock:

fukuoka.ex設立から1周年記念となる、[fukuoka.ex#11「DB／データサイエンスにコネクトするElixir」](https://fukuokaex.connpass.com/event/87241/)、過去最大規模の50名オーバーで、コンテンツもメチャクチャ濃く、とても盛り上がりました

[![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/14bd3532-2b36-0cb5-a54f-4e24b3c76d7a.png)](https://fukuokaex.connpass.com/event/87241/)

# データ集約（続き）

以下について、説明します

- 平均値
- 中央値
- パーセンタイル
- 分散値
- 標準偏差
- 最頻値

## 統計ライブラリ「Statistics」のインストール

統計ライブラリを自作しようかな、と思ったら、[「Statistics」](https://hex.pm/packages/statistics)という統計ライブラリを発見したので、こちらをインストールして、利用します

mix.exsの「def deps do」配下に追記します（:phoenix～の記載の上行に追加）

ついでに、小粒でピリリなユーティリティライブラリ[「smallex」](https://hex.pm/packages/smallex)のバージョンアップもしておきます

```elixir:mix.exs
defmodule SampleAnalytics.Mixfile do
  use Mix.Project
…
  defp deps do
    [
      {:statistics, "~> 0.5.1"}, 
      {:smallex, "~> 0.1.8"}, 
      {:phoenix, "~> 1.3.0"},
      …
    ]
  end
…
```

ライブラリを取得します（要ネット接続）

```
mix deps.get
```

Phoenixサーバーを起動することで、コンパイルします

```
iex -S mix phx.server
```

## 平均値

平均値は、データリストにStatistics.mean()を行うことで取得できます

```elixir:lib/sample_db_web/templates/page/index.html.eex
…
datas = result
  |> Enum.group_by( &( &1[ "Profession" ] ), &( &1[ "ApplicantIncome" ] ) ) 
  |> Enum.map( &( 
    %{ "Profession" => elem( &1, 0 ), "ApplicantIncome" => elem( &1, 1 ) |> 
      Enum.map( fn( n ) -> String.to_integer( n ) end ) |> Statistics.mean } ) )
…
```

## 中央値

中央値は、データリストにStatistics.median()を行うことで取得できます

```elixir:lib/sample_db_web/templates/page/index.html.eex
…
datas = result
  |> Enum.group_by( &( &1[ "Profession" ] ), &( &1[ "ApplicantIncome" ] ) ) 
  |> Enum.map( &( 
    %{ "Profession" => elem( &1, 0 ), "ApplicantIncome" => elem( &1, 1 ) |> 
      Enum.map( fn( n ) -> String.to_integer( n ) end ) |> Statistics.median } ) )
…
```

## パーセンタイル

パーセンタイルは、データリストにStatistics.percentile()を行うことで取得できます

たとえば、90%tileであれば、以下の通りです

```elixir:lib/sample_db_web/templates/page/index.html.eex
…
datas = result
  |> Enum.group_by( &( &1[ "Profession" ] ), &( &1[ "ApplicantIncome" ] ) ) 
  |> Enum.map( &( 
    %{ "Profession" => elem( &1, 0 ), "ApplicantIncome" => elem( &1, 1 ) |> 
      Enum.map( fn( n ) -> String.to_integer( n ) end ) |> Statistics.percentile( 90 ) } ) )
…
```

## 分散値

分散値は、データリストにStatistics.variance()を行うことで取得できます

```elixir:lib/sample_db_web/templates/page/index.html.eex
…
datas = result
  |> Enum.group_by( &( &1[ "Profession" ] ), &( &1[ "ApplicantIncome" ] ) ) 
  |> Enum.map( &( 
    %{ "Profession" => elem( &1, 0 ), "ApplicantIncome" => elem( &1, 1 ) |> 
      Enum.map( fn( n ) -> String.to_integer( n ) end ) |> Statistics.variance } ) )
…
```

## 標準偏差

標準偏差は、データリストにStatistics.stdev()を行うことで取得できます

```elixir:lib/sample_db_web/templates/page/index.html.eex
…
datas = result
  |> Enum.group_by( &( &1[ "Profession" ] ), &( &1[ "ApplicantIncome" ] ) ) 
  |> Enum.map( &( 
    %{ "Profession" => elem( &1, 0 ), "ApplicantIncome" => elem( &1, 1 ) |> 
      Enum.map( fn( n ) -> String.to_integer( n ) end ) |> Statistics.stdev } ) )
…
```

## 最頻値

最頻値は、データリストにStatistics.mode()を行うことで取得できます

```elixir:lib/sample_db_web/templates/page/index.html.eex
…
datas = result
  |> Enum.group_by( &( &1[ "Profession" ] ), &( &1[ "ApplicantIncome" ] ) ) 
  |> Enum.map( &( 
    %{ "Profession" => elem( &1, 0 ), "ApplicantIncome" => elem( &1, 1 ) |> 
      Enum.map( fn( n ) -> String.to_integer( n ) end ) |> Statistics.mode } ) )
…
```

# 終わり

今回は、「インプットしたデータの集約」のうち、統計を伴う集計を行いました

統計ライブラリ「Statistics」のおかげで、だいぶラクができた感じですね

「Statistics」には、上記以外にも、相関係数を取得するStatistics.correlation()や、調和平均を取得するStatistics.harmonic_mean()、尖度を取得するStatistics.kurtosis()、歪度を取得するStatistics.skew()、
Zスコアを取得するStatistics.zscore()など、便利な統計関数が揃っているので、トレーディングなどのデータサイエンス以外の分野でも活用できそうです

次回は、「インプットしたデータの変形」を行います

明日は @yukq16 さんの[「Elixirで関数の実行速度を測定する」](https://qiita.com/hisaway/items/2f075f1705b35e829c5c)です

# p.s.「いいね」よろしくお願いします

よろしければ、ページ左上の ![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/4d515047-cc48-382e-c2b1-3ad0cc50dbbf.png) や ![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/a4e3da58-70a3-4197-95a2-6a6906650d01.png) のクリックをお願いしますー:bow:
ここの数字が増えると、書き手としては「ウケている」という感覚が得られ、連載を更に進化させていくモチベーションになりますので、もっとElixirネタを見たいというあなた、私たちと一緒に盛り上げてください！:tada: