Arc ダイアグラムを使って天然ガスの米国からの輸出入を可視化 ‐ Yellowfin と amCharts の連携

## はじめに
[amCharts](https://www.amcharts.com/) と連携して、Arc ダイアグラムを作成します。[サンキーダイアグラム](https://qiita.com/ExtremeCarvFJ/items/01e6c0eaad6864ef4c8f)や[コードダイアグラム](https://qiita.com/ExtremeCarvFJ/items/db5105e1dc90807bbcb5)と同様に、モノや情報の流れを可視化する目的に用いられるチャートです。
![NaturalGas.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2128512/bf27d2bd-dffc-4477-a010-e91e207aedd0.gif)

## 1. 事前準備
#### 1-1. 参照データ
米国における、2024 年天然ガス輸出入データを取り扱います。[U.S. Energy Information Administration](https://www.eia.gov/) のデータをもとに、以下のテーブルを作成しました。volume の単位は百万立方フィート (1ft : 30.48cm) だそうですが、この数値を聞いても私は全くピンときません。
|importexport|transport|country|volume|
|---|---|---|---|
|import|Pipeline|Canada|3,127,280|
|import|Pipeline|Mexico|452|
|import|LNG|Trinidad and Tobago|10,444|
|import|LNG|Norway|5,037|
|import|LNG|Canada|1,000|
|export|Pipeline|Mexico|2,343,562|
|export|Pipeline|Canada|996,022|
|export|LNG|Netherlands|463,769|
|export|LNG|France|354,824|
|export|LNG|Japan|335,944|
|export|LNG|South Korea|289,232|
|export|LNG|India|256,045|
|export|LNG|Mexico|8,597|

* 2024 年の米国への輸入は、カナダ、トリニダード・トバゴ、メキシコ、ノルウェーの 4 ヶ国からのみでした
* 一方、輸出先は 46 ヶ国に上るため、輸出量上位 8 ヶ国に絞りました。輸出先上位 8 ヶ国の中には日本も含まれます
* 陸続きのカナダと米国、メキシコと米国の間には、天然ガスのパイプラインが通っており、特にカナダからは、大量の天然ガスがパイプラインで運ばれてきています
* それ以外の国との輸出入は、液化天然ガス (LNG) として運ばれていました

#### 1-2. ビューの作成まで
* [以前の記事](https://qiita.com/ExtremeCarvFJ/items/ad453a6110cec093bdb9)  を参考に、『1-2. ビューの作成』 までの作業を実施します

## 2. グラフの作成
* 準備が整ったら、グラフの作成に手順を進めます。新規でレポートを作成し、以下の手順に従ってグラフを作成します
#### 2-1. [データ] ステップ
* importexport、transport、country、volume をテーブルに配置します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2128512/536ee32b-38cd-478e-9f3b-7bcbb8b35f31.png)

* [フィルター](https://wiki.yellowfin.co.jp/space/yfcurrent/1900626/%E3%83%95%E3%82%A3%E3%83%AB%E3%82%BF%E3%83%BC)に transportation を配置します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2128512/21909d9b-2cb3-428c-bcd3-472e43a5f3cb.png)

#### 2-2. [グラフ] ステップ
* [グラフ] ステップに進み、画面右側 [グラフの選択] から [JavaScriptグラフ] を選択します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2128512/4af4ca23-a911-4271-b6a2-973fa5f52f05.png)

##### JavaScriptタブ
* 雛形を全て削除し、以下のコードに置き換えます。コードの処理の内容は後ほど説明します
```javascript:全容
generateChart = function(options) {
   var $chartDrawDiv = $(options.divSelector);
   var processedData = processData(options.dataset.data);
   doDrawing($chartDrawDiv, processedData);
},

processData = function(dataset) {
    var ds = [];
    for (i=0;i<dataset.transport.length;i++){
        if(dataset.importexport[i].raw_data == 'import'){
            ds.push({
                from: dataset.country[i].raw_data,
                to: 'US',
                value: dataset.volume[i].raw_data,

            });
        } else if(dataset.importexport[i].raw_data == 'export'){
            ds.push({
                from: 'US',
                to: dataset.country[i].raw_data,
                value: dataset.volume[i].raw_data,
            });
        }
    }
    console.log(JSON.stringify(ds));
    return ds;
},

doDrawing = function($chartDrawDiv, ds, obj) {
    require(['https://cdn.amcharts.com/lib/5/index.js',
            'https://cdn.amcharts.com/lib/5/flow.js',
            'https://cdn.amcharts.com/lib/5/themes/Animated.js'], function(){
        var $canvas = $('<div id="chartdiv"></div>');
        $chartDrawDiv.append($canvas);
        
        am5.ready(function() {

            // ルートエレメント設定
            var root = am5.Root.new("chartdiv");
            
            // テーマ設定
            root.setThemes([
              am5themes_Animated.new(root)
            ]);
            
            // シリーズ設定
            var series = root.container.children.push(am5flow.ArcDiagram.new(root, {
              sourceIdField: "from",
              targetIdField: "to",
              valueField: "value",
              orientation: "vertical"
            }));
            
            // ラベル設定
            series.nodes.labels.template.setAll({
              fontSize: "0.85em",
              paddingLeft: 20,
              paddingRight: 20,
              width: 160
            });
            
            // アニメーションの設定
            series.bullets.push(function(_root, _series, dataItem) {
              var bullet = am5.Bullet.new(root, {
                locationY: Math.random(),
                sprite: am5.Circle.new(root, {
                  radius: 5,
                  fill: dataItem.get("source").get("fill")
                })
              });
            
              bullet.animate({
                key: "locationY",
                to: 1,
                from: 0,
                duration: Math.random() * 1000 + 2000,
                loops: Infinity
              });
            
              return bullet;
            });
            
            
            // データセット受渡し
            series.data.setAll(
                ds
            );
            
            // ロード
            series.appear(1000, 100);
        });
})};
```

## コード

##### generateChart
```JavaScript:generateChart
generateChart = function(options) {
   var $chartDrawDiv = $(options.divSelector);
   var processedData = processData(options.dataset.data);
   doDrawing($chartDrawDiv, processedData);
},
```

* JavaScript グラフの処理において、最初に呼び出されるのが generateChart = function(options) です
* [データ] ステップでテーブルに追加した情報が options に格納されて、メソッドに受け渡されます
* processData でデータ処理を行うメソッドを呼び出し、doDrawingで描画するメソッドをそれぞれ呼び出しています。各メソッドで実行する具体的な処理は後述します

##### processData
* 輸出 (from U.S.) と輸入 (to U.S.) に関する、以下のデータを生成する必要があります
```JSON:ds
[
  {"from":"Canada","to":"US","value":3127280},
  {"from":"US","to":"Mexico","value":2343562},
  …
]
  ```

* そのための処理が以下です
```JavaScript:processData2023
processData = function(dataset) {
    var ds = [];
    for (i=0;i<dataset.transport.length;i++){
        if(dataset.importexport[i].raw_data == 'import'){
            ds.push({
                from: dataset.country[i].raw_data,
                to: 'US',
                value: dataset.volume[i].raw_data,

            });
        } else if(dataset.importexport[i].raw_data == 'export'){
            ds.push({
                from: 'US',
                to: dataset.country[i].raw_data,
                value: dataset.volume[i].raw_data,
            });
        }
    }
    console.log(JSON.stringify(ds));
    return ds;
},
```

* 生成されるデータの中身を確認したい場合は、`console.log(JSON.stringify(ds))` で、データをコンソールに出力しておくと便利です

##### CSS
* CSS タブに下記を記述します。後ほど、定義した id を JavaScript から呼び出します
```css:css
#chartdiv{
    height: 600px;
    width: 650px;
}
```

##### doDrawing
* チャートの描画に関わる処理を記述します
```JavaScript:doDrawing
doDrawing = function($chartDrawDiv, ds, obj) {
    require(['https://cdn.amcharts.com/lib/5/index.js','https://cdn.amcharts.com/lib/5/flow.js','https://cdn.amcharts.com/lib/5/themes/Animated.js'], function(){
        var $canvas = $('<div id="chartdiv"></div>');
        $chartDrawDiv.append($canvas);
        
        am5.ready(function() {

            // ルートエレメント設定
            var root = am5.Root.new("chartdiv");
            
            // テーマ設定
            root.setThemes([
              am5themes_Animated.new(root)
            ]);
            
            // シリーズ設定
            var series = root.container.children.push(am5flow.ArcDiagram.new(root, {
              sourceIdField: "from",
              targetIdField: "to",
              valueField: "value",
              orientation: "vertical"
            }));
            
            // ラベル設定
            series.nodes.labels.template.setAll({
              fontSize: "0.85em",
              paddingLeft: 20,
              paddingRight: 20,
              width: 160
            });
            
            // アニメーションの設定
            series.bullets.push(function(_root, _series, dataItem) {
              var bullet = am5.Bullet.new(root, {
                locationY: Math.random(),
                sprite: am5.Circle.new(root, {
                  radius: 5,
                  fill: dataItem.get("source").get("fill")
                })
              });
            
              bullet.animate({
                key: "locationY",
                to: 1,
                from: 0,
                duration: Math.random() * 1000 + 2000,
                loops: Infinity
              });
            
              return bullet;
            });
            
            // データセット受渡し
            series.data.setAll(
                ds
            );
            
            // ロード
            series.appear(1000, 100);
    });
})};
```

* `require[]` の中で、JavaScriptライブラリの場所を指定します。上記の例では CDN のアドレスを指定していますが、JS ライブラリを[ダウンロード](https://www.amcharts.com/download/)して利用することも可能です
* `var root = am5.Root.new("chartdiv")` で、CSS で定義した id を指定します
* それ以降の処理では、[テーマ](https://www.amcharts.com/docs/v5/concepts/themes/)、[シリーズ](https://www.amcharts.com/docs/v5/charts/flow-charts/arc-diagram/)、[ラベル]( https://www.amcharts.com/docs/v5/charts/flow-charts/arc-diagram/#Labels)、[アニメーション](https://www.amcharts.com/docs/v5/concepts/animations/)を設定した後、データセットを受け渡して、最後にチャートをロードしています。それぞれの設定箇所は、コメントアウト行をご確認下さい

## 3. 動作確認
* チャートを表示してみると、2024 年の年間の輸出入の流れと量が確認できます
* 丸の大きさが量、Arc が流れを示します。両国間にパイプラインが通っているカナダとメキシコとのやり取りが、他の国々と比較して大きいことが分かります
![All.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2128512/28cd09a1-32b8-4a9b-abf5-50ea93c4bbd0.gif)

* フィルターで LNG に絞り込みをかけます。欧州やアジアには、LNG で輸出されていることが分かります。日本にもたくさん輸出されていますね
![LNG.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2128512/8a858f2d-61d3-4dbb-953b-12c7ce03b845.gif)

## 最後に
モノや情報の流れを把握する時、やはり動きのあるチャートで表現すると分かりやすいですね。
[サンキーダイアグラム](https://qiita.com/ExtremeCarvFJ/items/01e6c0eaad6864ef4c8f)や[コードダイアグラム](https://qiita.com/ExtremeCarvFJ/items/db5105e1dc90807bbcb5)と比較すると、一般的な認知度が低い感じはしますが、Arc ダイアグラムも結構データの流れを分かりやすく表現すると感じました。あと、なにも操作しなくてもちょこまかと動いてくれるし。

では皆様、良いデータ分析を！