ExcelからElixir入門⑤：Webにグラフ表示

【本コラムは、2分で読めて、10分くらいでお試しいただけます】
[piacere](https://twitter.com/piacere_ex)です、ご覧いただいてありがとございます :bow:

[前回](https://qiita.com/piacerex/items/4c212615a4eb699dd109)は、Web上に外部APIデータを表示しました

今回は、Web上にテーブルや文字だけで無く、グラフを表示します

**■「ExcelからElixir入門」シリーズの目次**
[**①データ並替え／絞り込み**](https://qiita.com/piacere_ex/items/6714e1440e3f25fb46a1)
|> [**②データ列抽出、Web表示**](https://qiita.com/piacere_ex/items/b7787580fce5f148242f)
|> [<b>③WebにDBデータ表示</b>](https://qiita.com/piacere_ex/items/a7558adc6856e3577dc6)
|> [<b>④Webに外部APIデータ表示</b>](https://qiita.com/piacere_ex/items/4c212615a4eb699dd109)
|> [<b>⑤Webにグラフ表示</b>](https://qiita.com/piacere_ex/items/290b76b76d5ff8e019bf)
|> [**⑥SPAからPhoenix製APIを呼び出す（表示編）【LiveView版】**](https://qiita.com/piacerex/items/64e77b85bfb4c6832662)
|> [**⑦SPAからPhoenix製APIを呼び出す（更新編）【LiveView版】**](https://qiita.com/piacerex/items/78ee0d2ad8cca0927980)
|> [<b>⑧Gigalixirに本番リリース</b>](https://qiita.com/piacerex/items/1a9cbcc740ca3707eaec)
|> [**⑨ElixirサーバサイドのみでReactと同じSPA／リアルタイムUIが作れる「LiveView」**](https://qiita.com/piacerex/items/3f8ee18c9443d63955bf)
|> [**⑩ElixirサーバサイドSPAをスマホで見るためにGigalixirリリース**](https://qiita.com/piacerex/items/583174d72e6fc0cd289e)
|> [**⑪Gigalixir上のLiveViewアプリに独自ドメイン名を付与して正式なアプリ公開**](https://qiita.com/piacerex/items/b61348c282395167fd8c)
|> [**⑫Elixir／PhoenixのCRUD Webアプリをリリース**](https://qiita.com/piacerex/items/511f24888c7350639d0b)

:stars::stars::stars::stars::stars:<b>　お知らせ　</b>:stars::stars::stars::stars::stars:
<strike>[<b>「fukuoka.ex#11：DB／データサイエンスにコネクトするElixir」</b>](https://fukuokaex.connpass.com/event/87241)を6/22（金）19時に開催します</strike>
<font color="red">開催終了しています</font>

特別ゲストは、Erlangで世界的活動を続ける「力武 健次さん」と、北九州の飯塚市で「e-ZUKA Tech Night」を6年間主催し続けるハウインターナショナルの「谷口 耕平さん」のお二人と、実に豪華なイベントです:wink:

私は、「1家に1台、パーソナルなデータ分析AIを全員が持つ2020年を作る」というタイトルで、Elixirによる、ブラウザUI上からサクっとデータ分析プラットフォームを披露するLTをお届けします:stuck_out_tongue_winking_eye:
[![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/608acb8f-bb81-0634-a5e1-aa37ac6cd2c9.png)](https://fukuokaex.connpass.com/event/87241)

# グラフ化する数値の追加

前回の外部APIデータ表示で、Qiitaから取得したコラムのID／タイトル／作成日時を表示しましたが、ここに、コラムが何回「いいね」されたかを示す「likes_count」を列追加します

`columns`に、`"likes_count"` を追加するだけです（非常にカンタンですね）

```diff_elixir:lib/basic_web/templates/page/index.html.heex
<%
result = Req.get!("https://qiita.com/api/v2/items?query=elixir") |> Map.get(:body)
datas = result
+ columns = ["id", "title", "created_at", "likes_count"]
%>
<table>
<tr>
  <%= for column <- columns do %>
  <th><%= column %></th>
  <% end %>
</tr>
<%= for n <- datas do %>
<tr>
  <%= for column <- columns do %>
  <td><%= n[column] %></td>
  <% end %>
</tr>
<% end %>
</table>
```

「いいね」数が追加されました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/7ad23c7f-1978-0ea8-128e-56ba697b4d9a.png)

では、この数値をグラフ化してみます

# 「Chart.js」によるグラフ表示

[「Chart.js」](https://www.chartjs.org/)というグラフ描画JavaScriptライブラリを使って「いいね」数をグラフ化します
[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/ddfc3549-a314-328e-cd1a-4181c641c2fd.png)](https://www.chartjs.org/)

なお、冒頭の外部APIデータ取得と、後半のテーブル表示は、一切変えておらず、その間の`<canvas ～></canvas>`と`<script>～</script>`にグラフ描画部分を追加したのみです

```diff_elixir:lib/basic_web/templates/page/index.html.heex
<%
result = Req.get!("https://qiita.com/api/v2/items?query=elixir") |> Map.get(:body)
datas = result
columns = ["id", "title", "created_at", "likes_count"]
%>
+ <canvas id="likes"></canvas>
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
+ <script>
+ new Chart( document.getElementById("likes"), {
+   type: 'bar',
+   data: {
+     labels: [<%= for n <- datas do %>'<%= n["title"] %>', <% end %>],
+     datasets: [{
+       label: 'Qiitaいいね数',
+       data: [<%= for n <- datas do %><%= n["likes_count"] %>, <% end %>]
+     }]
+   },
+   options: {
+     indexAxis: 'y',
+   }
+ })
+ </script>
+ 
<table>
<tr>
  <%= for column <- columns do %>
  <th><%= column %></th>
  <% end %>
</tr>
<%= for n <- datas do %>
<tr>
  <%= for column <- columns do %>
  <td><%= n[column] %></td>
  <% end %>
</tr>
<% end %>
</table>
```

このようなグラフ表示になります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/7eea0eb9-eac7-d38f-dc35-1f0c2258b117.png)

グラフにデータを流し込むところは、`labels` に `title`、`data` に `likes_count` が並べられます

```elixir:lib/basic_web/templates/page/index.html.heex
…
new Chart( document.getElementById("likes"), {
  type: 'bar',
  data: {
    labels: [<%= for n <- datas do %>'<%= n["title"] %>', <% end %>],
    datasets: [{
      label: 'Qiitaいいね数',
      data: [<%= for n <- datas do %><%= n["likes_count"] %>, <% end %>]
…
```

この部分がJavaScript中（≒HTML）に展開されると、以下のような感じになり、グラフのデータとしてChart.jsに解釈されます

```elixir:lib/basic_web/templates/page/index.html.heex
…
new Chart( document.getElementById("likes"), {
  type: 'bar',
  data: {
    labels: ['BSD系OSにElixirを…', 'Elixir初学者が悩む…', '「なぜ私はElixirに賭けたか」…', …],
    datasets: [{
      label: 'Qiitaいいね数',
      data: [7, 4, 34, …]
…
```

# グラフ内の文字列を短縮する

グラフはキレイに出たのですが、左側のタイトル文字列が長過ぎるものが、末尾のみ残っていて気持ち悪いため、先頭30文字でカットし、`…` を付与してみましょう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/0327347d-563a-fcf6-b560-4e3dc8d0a4f1.png)

コードはこんな感じで、グラフ生成中に文字列加工を挟みます

```diff_elixir:lib/basic_web/templates/page/index.html.heex
<%
result = Req.get!("https://qiita.com/api/v2/items?query=elixir") |> Map.get(:body)
datas = result
columns = ["id", "title", "created_at", "likes_count"]
%>
<canvas id="likes"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
new Chart( document.getElementById("likes"), {
  type: 'bar',
  data: {
+   labels: [<%= for n <- datas do %>
+     '<%= String.slice(n["title"], 0..30) %><%= if String.length(n["title"]) > 30, do: "…" %>', 
+   <% end %>],
    datasets: [{
      label: 'Qiitaいいね数',
      data: [<%= for n <- datas do %><%= n["likes_count"] %>, <% end %>]
    }]
  },
  options: {
    indexAxis: 'y',
  }
})
</script>

<table>
<tr>
  <%= for column <- columns do %>
  <th><%= column %></th>
  <% end %>
</tr>
<%= for n <- datas do %>
<tr>
  <%= for column <- columns do %>
  <td><%= n[column] %></td>
  <% end %>
</tr>
<% end %>
</table>
```

# グラフの色をカラフルにする

グラフの色が、水色だけというのもつまらないので、カラフルにしてみましょう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/f62a7d86-9f0b-8f16-f76a-82be960d8a7c.png)

`backgroundColor` 属性を追加することで設定できるので、RGBを各範囲でランダムに設定させます

```diff_elixir:lib/basic_web/templates/page/index.html.heex
<%
result = Req.get!("https://qiita.com/api/v2/items?query=elixir") |> Map.get(:body)
datas = result
columns = ["id", "title", "created_at", "likes_count"]
%>
<canvas id="likes"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
new Chart( document.getElementById("likes"), {
  type: 'bar',
  data: {
  labels: [<%= for n <- datas do %>
      '<%= String.slice(n["title"], 0..30) %><%= if String.length(n["title"]) > 30, do: "…" %>', 
    <% end %>],
    datasets: [{
      label: 'Qiitaいいね数',
      data: [<%= for n <- datas do %><%= n["likes_count"] %>, <% end %>], 
+     backgroundColor: [<%= for n <- datas do %>
+       'rgba(<%= Enum.random(150..230) %>, <%= Enum.random(80..170) %>, <%= Enum.random(190..255) %>, 0.6)', 
+     <% end %>], 
    }]
  },
  options: {
    indexAxis: 'y',
  }
})
</script>

<table>
<tr>
  <%= for column <- columns do %>
  <th><%= column %></th>
  <% end %>
</tr>
<%= for n <- datas do %>
<tr>
  <%= for column <- columns do %>
  <td><%= n[column] %></td>
  <% end %>
</tr>
<% end %>
</table>
```

リロードすると、カラーリングが変化します

ただし、Qiita APIは何十回が呼び出すと、`Rate Limit` がかかって呼出不可になるので、リロードし過ぎないように注意してください（なお、1時間立つと解除されます）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/a5728844-7610-63c6-9140-711b024cac0e.png)

何回も遊びたい方は、下記でQiita API結果JSONをファイル保存してください

```
iex> Req.get!("https://qiita.com/api/v2/items?query=elixir") |> Map.get(:body) |> Jason.encode! |> then(& File.write("qiita.txt", &1))
```

下記で保存したQiita API結果JSONから表示できるので、`Rate Limit` を気にせず何回も遊べます

```diff_elixir:lib/basic_web/templates/page/index.html.heex
<%
+ result = File.read!("qiita.txt") |> Jason.decode!
datas = result
columns = ["id", "title", "created_at", "likes_count"]
%>
<canvas id="likes"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
new Chart( document.getElementById("likes"), {
  type: 'bar',
  data: {
  labels: [<%= for n <- datas do %>
      '<%= String.slice(n["title"], 0..30) %><%= if String.length(n["title"]) > 30, do: "…" %>', 
    <% end %>],
    datasets: [{
      label: 'Qiitaいいね数',
      data: [<%= for n <- datas do %><%= n["likes_count"] %>, <% end %>], 
      backgroundColor: [<%= for n <- datas do %>
        'rgba(<%= Enum.random(150..230) %>, <%= Enum.random(80..170) %>, <%= Enum.random(190..255) %>, 0.6)', 
      <% end %>], 
    }]
  },
  options: {
    indexAxis: 'y',
  }
})
</script>

<table>
<tr>
  <%= for column <- columns do %>
  <th><%= column %></th>
  <% end %>
</tr>
<%= for n <- datas do %>
<tr>
  <%= for column <- columns do %>
  <td><%= n[column] %></td>
  <% end %>
</tr>
<% end %>
</table>
```

# 【参考】本コラムの検証環境

本コラムは、以下環境で検証しています（恐らくUbuntu実機やMacでも動きます）

- Windows 10
  - 実機＊Elixir 1.14.2 (Erlang/OTP 25)
    - Phoenix 1.6.15
  - WSL2／Ubuntu 20.04＋Elixir 1.14.2 (Erlang/OTP 25)　※[**最新版のインストール手順はコチラ**](https://qiita.com/piacerex/items/01e89435af0f7a454ad2)
    - Phoenix 1.6.15
  - Docker／Debian 11.6＋Elixir 1.14.2 (Erlang/OTP 25)
    - Phoenix 1.6.15

- Windows11
  - WSL2／Ubuntu 22.04＋Docker Compose＋Elixir 1.13.4 (Erlang/OTP 25)
    - Phoenix 1.6.15

# 終わり

ElixirでのWebグラフ表示が、とてもカンタンに実現することがお分かりいただけたでしょうか？

今回は、外部APIデータのグラフ表示でしたが、DBも外部APIと全く変わらない扱い方ができるので、DBデータのグラフ表示も、やはりカンタンです

そして、データさえ揃っていれば、この連載でお伝えした内容に多少の手を加えるだけで、以下のようなデータサイエンスプロダクトが、開発できてしまうのです
![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/ec754ad5-3a0e-27d2-e24d-66743f96aacb.png)

Elixirで、WebとDB／API／グラフ表示を行うことが、いかにお手軽でパワフルかということを感じていただけたら幸いです

[次回は、更にフロント側の表現力を上げていく、<b>「LiveViewとPhoenix APIの連携」</b>](https://qiita.com/piacerex/items/64e77b85bfb4c6832662)を行います

:coffee: :coffee: :coffee:　<b>ここで一息、CofeeBreak</b>　:coffee: :coffee: :coffee: 
オブジェクト指向から関数型言語に移った方は、この連載をやっていくうちに、「なんで、こんなにカンタンなはずの関数型言語の習得に今まで難航してたんだろう？」と感じたのでは無いでしょうか？

[その疑問にお答えするコラムも番外編として書いてみました](https://qiita.com/piacere_ex/items/8fad067885aed73bf789)
:coffee: :coffee: :coffee:　<b>ここで一息、CofeeBreak</b>　:coffee: :coffee: :coffee: