【世界2位】Lookerハッカソン Hack@Home2021 参加報告

この記事は Looker Advent Calendar 2021 の14日目の記事です。

https://qiita.com/advent-calendar/2021/looker

ほぼ同一内容の発表を2021-12-09開催の[Looker JPユーザー会 第７回](https://community.looker.com/%E3%83%8B%E3%83%A5%E3%83%BC%E3%82%B9%E3%81%A8%E5%91%8A%E7%9F%A5-100/looker-jp%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E4%BC%9A-%E7%AC%AC%EF%BC%97%E5%9B%9E-%E9%96%8B%E5%82%AC%E3%81%AE%E3%81%94%E6%A1%88%E5%86%85-2021-12-9-thu-28457)で行いました。

[ミクシィグループ Advent Calendar 2021](https://qiita.com/advent-calendar/2021/mixi) の13日目も担当しておりますが執筆が遅れております。今週中には投稿するのでそちらもよろしくお願いします。


# 概要
Lookerが主催するハッカソン [Hack＠Home 2021](https://community.looker.com/groups/hackathome-2021-1026)に参加しました。そこで[Custom Visualization](https://docs.looker.com/data-modeling/marketplace/adding-viz-marketplace)を用いて、動く棒グラフである「bar chart race」を実装したところ、全体2位にあたる `Nearly Best Hack Winner`の賞を頂きました。主催者からは静的な画像よりもたくさんの情報量を持つ動画をLookerに導入したことを評価していただけました。



# Lookerハッカソン Hack@Home2021
Hack@Home2021とは、Lookerの年次イベント [JOIN@Home](https://looker.com/events/join/) 内で開かれたハッカソンです。Lookerの高度な機能、Extension Framework, CustomViz, Looker API, Embedなどを活用して何かを作る会です。

複数人で構成されたチームで参加することが可能ですが、私は一人で参加しました。なお、日本人も一人です。

基本的に進行は英語ですが、**今回は部分的に日本語でのサポートがありました！！！**

## スケジュール
日本時間の2021年11月10日～12日にかけて行われました。進行は基本的に太平洋標準時(UTC-7)ですが、オンラインですので世界各国から参加する人がいます。そのため、複数のタイムゾーンに配慮した構成になっていました。

以下時刻は日本標準時です。

|時刻|内容|
|:--|:--|
|11月10日 1:00 PM - 1:30 PM|開会（アジア太平洋）|
|11月10日1:30 PM - 3:00 PM|サポート時間（英語・日本語）|
|11月10日 6:00 PM - 6:30 PM|開会（欧州・中東）|
|11月10日 6:30 PM - 8:30 PM|サポート時間（英語・日本語）|
|11月10日 - 11日 10:00 PM - 0:00 深夜|サポート時間（英語のみ）|
|11月11日 3:00 AM - 3:30 AM|開会（アメリカ大陸）|
|11月11日 3:30 AM - 6:00 AM|サポート時間（英語のみ）|
|11月11日 7:30 AM - 9:00 AM|サポート時間（英語・日本語）|
|11月11日 10:00 AM - 0:00 正午|サポート時間（英語・日本語）|
|11月11日 0:00 正午 - 1:00 PM|夜懇親会|
|11月11日 1:00 PM - 1:30 PM|ラウンドテーブル（アジア太平洋）|
|11月11日 1:30 PM - 3:00 PM|サポート時間（英語・日本語）|
|11月11日 6:00 PM - 6:30 PM|ラウンドテーブル（欧州・中東）|
|11月11日 6:30 PM - 8:00 PM|サポート時間（英語・日本語）|
|11月11日 - 12日 10:00 PM - 0:00 深夜|サポート時間（英語のみ）|
|11月12日 3:00 AM - 3:30 AM|ラウンドテーブル（アメリカ大陸）|
|11月12日 3:30 AM - 5:00 AM|サポート時間（英語のみ）|
|11月12日 6:00 AM - 8:30 AM|提出締め切り|
|11月12日 7:00 AM - 7:00 AM|ファイナリスト発表|
|11月12日 8:30 AM - 9:30 AM|優秀賞発表・デモ|
|11月12日 9:30 AM - 10:30 AM|懇親会|


## オンラインでのハッカソン

オンラインでイベント運営をする時、ツールの選定に悩まされることも多いでしょう。参考までに、このハッカソンでの運用をメモしておきます。

このコンテンツがJOIN@HOMEの一部であるということもあり、メインイベントの開会、ラウンドテーブル、優秀賞発表などの配信は、JOIN@HOMEのインフラ上で行われました。質問はSlackでのチャット、懇親会はGoogle Meetでした。


# 使用した技術
参加記録の前に、使用したライブラリや機能のうち、特筆すべきものについて説明します。

## Custom Visualization

Lookerには、棒グラフや折れ線グラフなどの基本的なグラフが実装されています。Custom Visualization（CustomViz）はJavaScriptで任意のグラフをLookerに追加することのできる機能です。CustomVizで作られたグラフは、他のLookerのグラフと同様に、LookやDashboardの一部として活用することができます。

Lookerには他にもExtensionFrameworkという、グラフの枠にとどまらず自由に実装ができるものがあります。ExtensionFrameworkはたしかに自由度が高いのですが、LookerのLookやDashboardというエコシステムに乗っかることができる点で、CustomVizも魅力的な選択肢です。

ところで、CustomVizはiframeを使っているのですが、[Visualization Components](https://developers.looker.com/components/visualization-components/)というiframeに頼らない、Reactベースの似たような機能が21.20でリリースされたようです。

### 実装例

使い方はシンプルです。下図の`updateAsync`関数に着目すると、クエリの実行結果のデータ(`data`)とグラフを描画するべきDIVタグ(`element`)と設定値(`config`)が関数の引数として与えられています。このデータを*こねこね*して作ったDOMを`element`に挿入するだけです。

公式のサンプルコード
https://github.com/looker-open-source/custom_visualizations_v2/blob/master/docs/api_reference.md

```javascript
looker.plugins.visualizations.add({
	create: function(element, config){
		element.innerHTML = "<h1>Ready to render!</h1>";
	},
	updateAsync: function(data, element, config, queryResponse, details, doneRendering){
		var html = "";
		for(var row of data) {
			var cell = row[queryResponse.fields.dimensions[0].name];
			html += LookerCharts.Utils.htmlForCell(cell);
		}
		element.innerHTML = html;
		doneRendering()
	}
});
```

## bar chart race

bar chart race(バーチャートレース)は文字通り、時間進行とともに競争をする棒グラフです。以下は有名なグラフで、Webブラウザのシェア争いを表した動画です。


<iframe width="560" height="315" src="https://www.youtube.com/embed/es9DNe0l0Qo" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

Youtubeにアップロードされている動画を見ると、[Flourish](https://flourish.studio/)で作られたものが多そうです。

## anichart.js
https://github.com/Jannchie/anichart.js

Bilibili動画で投稿されているbar chart raceの動画の多くで使われているライブラリ([Historical-ranking-data-visualization-based-on-d3.js](https://github.com/Jannchie/Historical-ranking-data-visualization-based-on-d3.js))……と同じ作者の後継のライブラリです。これは動く棒グラフ・円グラフ・折れ線グラフが作れる、TypeScriptライブラリです。Canvasに出力しており、FFmpegでの出力にも対応しています。

# 参加記録
## 動機
このハッカソンに参加することになった以前、Custom Visualizationという機能を知った時から、bar chart raceを作りたいと思っていました。しかし、業務に忙殺されているうちにきっかけがなかなか掴めず、ハッカソンはとても良い機会になりました。

Bar chart raceと私の出会いは入社直前の2019年1月のことで、Bilibili動画をダラダラ見ていたときに出会った[この動画]( https://www.bilibili.com/video/av27779055 )でした。これはBilibili動画に投稿されたVOCALOID動画の総再生数を、VOCALOIDごとに競っているものです。

（Qiitaに動画を埋め込めるサイトは[決まっている](https://qiita.com/Qiita/items/c686397e4a0f4f11683d#%E3%82%B3%E3%83%B3%E3%83%86%E3%83%B3%E3%83%84%E3%81%AE%E5%9F%8B%E3%82%81%E8%BE%BC%E3%81%BF)そうです。Bilibiliはもちろん非対応でした。残念）

当時は呼び名を知らず、似たようなものをどう検索して良いものか悩んでいたのを覚えています。この頃のBilibiliに投稿されているbar chart raceの動画はほとんど、 JannchieさんのWebアプリ、[Historical-ranking-data-visualization-based-on-d3.js](https://github.com/Jannchie/Historical-ranking-data-visualization-based-on-d3.js)で作られており、これは2018年6月に作成されています。[Bilibiliのデータ可視化タグ]( https://www.bilibili.com/v/channel/605361 )はこういった動画が多く、ひたすら見てられるので楽しいです。中国語がほとんどですが漢字なのできっと読めるのでおすすめです。

入社して４ヶ月くらいの慣れてきた頃に、業務データを用いてbar chart raceを社内向けに制作しました。その評判が良かったことも強い動機につながっています。

## 1日目
日本語に対応しているのは技術サポートのみで、基本的には英語で進行します。日本人の参加者が少ない（一人）ので仕方ありません。

ハッカソンの運営をするためのWebアプリがあるのですが、これもまたLookerのExtensionFrameworkで作成されていたことに衝撃を受けました。これはLookerAPIを使わない、たとえば単純な計算アプリなどでも、LookerをWebアプリを載せるプラットフォームとしても活用できると感じました。

13時から始まった開会式が終わりハッカソンが始まったは良いのですが、それだけやることを決めていながら、私は環境構築すら予習をしていませんので、いきなり路頭に迷ってしまいました。幸いハッカソン参加者用の「Resources」と呼ばれる大量のチュートリアル動画やドキュメントが用意されていましたので、そこをとっかかりに調査を始めていました。しかし、一向に環境構築ができません。19時ころにようやくサポートに問い合わせたところ、簡単そうな方法を教えてもらえました。すったもんだの末に満足の行く環境構築が完了したのは21時のことでした。終わった後に見返してみるとなんでこんな簡単なことがわからなかったのかと思うのですが、焦っていたり、ドキュメントがうまく探せなかったりしました。結局、環境構築を8時間近くやっていたことになります。

環境構築が終わり、Looker上で私が挿入したコードから`console.log("Hello World")` が出力できました。早速私がbar chart raceを知るきっかけとなったライブラリを入れるために調査をしたところ、新しいライブラリがリリースされていました。新しいライブラリ、[anichart.js](https://github.com/Jannchie/anichart.js) は従来のライブラリから進化しており、多機能でモダンな構造になっていました。しかし、基本的な使い方の説明はあれど、開発が優先で細かいドキュメントはなく、サンプルコードを読んで感じ取ってくれという強いOSSでした。

anichart.jsとCustomVizを気合と`console.log`で把握しながら、23時にはLookerのデータをanichart.jsに荒っぽいながらも流し込んで表示するところまで進みました。


## 2日目

基本的な繋ぎこみはできたので調整を進めていきました。不要なDOMを泥臭く消したりCSSの調整をして、一応提出できる見た目にして午前中を終えました。

### （正午だけど米国時間では夜なので）夜懇親会
なぜか参加者が私しかおらず、直前のサポート時間に質問を投げていたこともあり、スタッフに囲まれてサポートを受けました。おそらく世界で一番スタッフの多いLookerサポート体制だったと思いますが、私の英語力（と国語力とコミュ力）の不足によりフル活用とはいきませんでした。大学院時代は毎日英語を喋る機会がありましたが、最近は全然だめです。終了15分前、サポートが一段落して雑談をしている頃に、Google Meetの字幕機能に気づきました。

### 画像の表示に対応
さて、業務の可視化に将来的につなげていくために、グラフにゲームキャラクターの画像が表示されるのは必須です。業務でも[Looker 21.0 アップデート](https://community.looker.com/%E3%83%8B%E3%83%A5%E3%83%BC%E3%82%B9%E3%81%A8%E5%91%8A%E7%9F%A5-100/looker-21-0-%E3%83%AA%E3%83%AA%E3%83%BC%E3%82%B9%E3%83%8E%E3%83%BC%E3%83%88-20933 ) で追加された「Base64エンコード埋め込み画像をクエリ結果で表示する機能」は大喜びで活用しております。[以前のライブラリ](https://github.com/Jannchie/Historical-ranking-data-visualization-based-on-d3.js)でもアイコンを表示する機能はあったので、画像自体はすんなり表示させることができましたが、ラベルと画像の両方を渡す必要があるためデータの取扱が複雑になってきます。この問題は次の方法で解決します。

### 大量データと画像に対応したデータ形式

日付とラベルでグラフを描画するとき、ラベルをピボットして次のようなデータを与えるのが自然です。
![基本的な使い方の画像](https://raw.githubusercontent.com/m77so/looker-vis-barchartrace/main/docs/img/simple_table_sample.png)

しかし、これは次のような問題があります。

- カラム数の制限によりラベルの数が200に制限される
  - 業務で適用するゲームのキャラクター数は数千
- 何百列もデータを頑張って持ってきても、表示されるのはせいぜい上位20個くらい
- そもそもそんなに大量のデータをやり取りすると重くなる


次の画像・SQLのようなデータ構造は、その問題を解決します。まず、その日付の中の順位でピボットします。そしてmeasureとして、値、ラベル、画像のURLを持ちます。
![発展的なデータの持ち方の画像](https://raw.githubusercontent.com/m77so/looker-vis-barchartrace/main/docs/img/advanced_table_sample.png)

```sql
SELECT
  date, -- dimension
  ROW_NUMBER() OVER(PARTITION BY date ORDER BY value DESC) as rn, -- dimension(pivoted)
  province_state, -- measure
  value,  --measure
  icon_url --measure
```

最終的にこの2つのデータ構造はそれぞれに利点がありましたので、両方に対応した実装にしました。これにより、既存の小さなデータからさっと可視化をしたいときは前者の簡単なピボットを、ダッシュボードを作り込みたいときは後者の複雑なピボット用のテーブルを用意するという使い分けをすることができます。

## ユーザ設定の追加
アニメーションの速度を変更できるようにしました。ユーザが調整できる設定はもう少し充実させたかったのですが、体力切れで断念しました。


## 提出資料作成

ハッカソンの成果物として、以下のものを英語で作成し提出する必要がありました。

- ソースコードをまとめたもの
- どのようなものを作成したのかまとめたもの
- どのようなものか分かるデモ動画

英語に自信がなかったため、サポートの方に英語として意味が通るか、事前チェックをしていただきました。

以下は実際のデモ動画です。

本当は音声でこれが何であるかの補足をする必要があったのですが、勘違いして無言のスクリーンキャプチャをアップロードしました。また、「できるだけ短くまとめて」と書いてある言葉を真に受けてめちゃくちゃ短くなっていますが、他の参加者は数分の動画を投稿していました。

<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/8wbtMO8aV80" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

# 評価

全体2位にあたる 'Nearly Best Hack Winner'を頂きました。主催チームからは静的な画像よりもたくさんの情報量を持つ動画をLookerに導入したことを評価していただけました。

デジタル賞状
![Nearly Best Hack Winner.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45240/a5865378-2d58-b92f-0ce0-dfd4cec767f5.png)

# 今後
## マーケットプレイスへの展望
Lookerマーケットプレイスに入るには、[Lookerが定める機能要件](https://docs.looker.com/data-modeling/marketplace/adding-viz-marketplace#checking_the_functionality_of_the_visualization)を満たす必要があります。抜粋すると、

- ドリルダウンができますか
- Lookerらしい色使いができますか
- レスポンシブデザインを満たしていますか
- etc...

Canvasベースのライブラリなので、正直ドリルダウンが厳しいんじゃないかなと思っています（試していないですが）。

というわけで、すぐにマーケットプレイスの申請をするのは無理そうなのですが、野良ライブラリとして導入することは可能です。

## 追加したい機能
- BASE64画像への対応
  - 業務環境では画像をファイルではなくBase64で管理しています。転送データ量が膨大になりそうなので困っています。
- 使用するメジャーを設定で指定できるようにする。
  - 選択したメジャーをJS側で取得して、それを設定画面に反映させる方法がわからず断念しました。リファレンスを読み直したらできそうだったのでそのうちやります。
- SVGベースで再実装
  -  やってみたい（言うだけならただ）  




# Appendix
- [LookML構成例](https://github.com/m77so/looker-hackathome-2021-barchartrace)
- [成果物（とても散らかっています）](https://github.com/m77so/looker-vis-barchartrace) 
- [提出したまとめの写し](https://github.com/m77so/looker-vis-barchartrace/wiki/Hackathon-Submition)