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

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

昨日は、@kobatako さんの[「GraphQL for Elixir#2 リクエストとレスポンスの値について考える」](https://qiita.com/kobatako/items/cf34dc3e97534976a50d)でした

----
[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)

今回は、「データ前処理」の続きとして、「インプットしたデータの集約」について解説します

ElixirのEnumの中では、やや使いこなしが難しい、Enum.group_by()を、SQLのGROUP BYと同じように使うサンプルとしても活用できるコラムです

サンプルデータ等は、[前回](https://qiita.com/piacere_ex/items/64c2127857599137e8a7)と同じものを使います

それでは、Phoenixサーバーを起動し、ブラウザで「```http://localhost:4000```」のWebページが見れる状態にしたら、以下を試していきましょう
<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/56ad1316-e987-3722-0c38-1e17708fca1c.png)](https://fukuokaex.connpass.com/event/87241/)

# データ集約

データ集約としては、以下がポピュラーです

- グループ化
- カウント
- 合計
- 最大値
- 最小値
- 平均値
- 中央値
- パーセンタイル
- 分散値
- 標準偏差
- 最頻値

## グループ化

同じキー値を持つデータをリストアップするには、まずEnum.group_by()で「キー値」と「データリスト」という形式でグルーピングし、キー値とデータリストの各々に列名を付け直す操作をします

```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 ) |> Lst.to_csv } ) )
…
```

## カウント

カウントは、グルーピング後のキー値とデータリストの各々に列名を付け直す際、データリストにEnum.count()することで取得できます

```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.count } ) )
```

## 合計

合計は、グルーピング後のキー値とデータリストの各々に列名を付け直す操作時、データリストをEnum.reduce()で足しこんでいけば取得できます

```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.reduce( 0, fn( n, acc ) -> acc + String.to_integer( n ) end ) } ) )
…
```

## 最大値・最小値

最大値・最小値は、グルーピング後のキー値とデータリストの各々に列名を付け直す操作時、データリストにEnum.max()もしくはEnum.max()を行うことで取得できます

```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 ) |> Enum.max } ) )
…
```

```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 ) |> Enum.min} ) )
…
```

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

これらの統計系は、ElixirのEnumが、標準で計算する機能を持っていないため、次回、自前で関数を作りつつ、対応してみます

# 終わり

今回は、「データ前処理」の続きとして、「インプットしたデータの集約」の前半について取り上げました

次回は、<b>[「インプットしたデータの集約」の後半](https://qiita.com/piacere_ex/items/7c33836f930b63b0444d)</b>を行います

明日は @yukq16 さんの「2次元リスト加算関数」です

# 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: