vega-liteのParameter

# ■ はじめに
前回の記事[vega-liteのmulti-view](https://qiita.com/contrail_triple_crown/items/41d72702340f3aedbd7c)の続きです。
今回は画面上の入力によって可変できるParameterについて記載していこうと思います。

## Parameters
[Dynamic Behaviors with Parameters](https://vega.github.io/vega-lite/docs/parameter.html)
公式の例を見ても分かる通り、画面入力によって値を変化させる機能です。
クリックやドラッグ等のviewに対するマウス操作や、ドロップダウンやスライダーなど外部入力要素を設定する事が可能です。

必須のプロパティは```name```です。
各viewでparameterを参照する際に使うプロパティになります。

その他、主に以下のプロパティを用いて構築します。
- Value
- Expr
- Bind
- Select

# ■ Value
[Initializing a Parameter](https://vega.github.io/vega-lite/docs/value.html)
パラメータの初期値ですね。
文字列、bool、数値、JSON等の値を設定できます。

# ■ Expr
[Expression](https://vega.github.io/vega-lite/docs/parameter.html#expr)
式を利用して入力されたパラメーターを変化させることができます。

## 公式の例（カスタム）
以下はの公式の例を少しカスタマイズしたものになります。
スライダーで入力された値を1/2、1/4、1/8にして使用しており、それぞれ濃いグレーのbarと薄いブルーのbarと濃いブルーのbarの幅に指定してます。

```json-doc:vega-lite
{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "data": {
    "values": [
      {
        "title": "Revenue",
        "subtitle": "US$, in thousands",
        "ranges": [150, 225, 300],
        "measures": [220, 270],
        "markers": [250]
      }
    ]
  },
  "params": [
    {
      "name": "height",
      "value": 20,
      "bind": {"input": "range", "min": 1, "max": 100, "step": 1}
    },
    {"name": "innerBarSize1", "expr": "height/2"},
    {"name": "innerBarSize2", "expr": "height/4"},
    {"name": "innerBarSize3", "expr": "height/8"}


  ],
  "encoding": {
    "x": {"type": "quantitative", "scale": {"nice": false}, "title": null}
  },
  "layer": [
    {
      "mark": {"type": "bar", "color": "#eee", "size": {"expr": "height"}},
      "encoding": {"x": {"field": "ranges[2]"}}
    },
    {
      "mark": {"type": "bar", "color": "#ddd", "size": {"expr": "height"}},
      "encoding": {"x": {"field": "ranges[1]"}}
    },
    {
      "mark": {"type": "bar", "color": "#ccc", "size": {"expr": "innerBarSize1"}},
      "encoding": {"x": {"field": "ranges[0]"}}
    },
    {
      "mark": {
        "type": "bar",
        "color": "lightsteelblue",
        "size": {"expr": "innerBarSize2"}
      },
      "encoding": {"x": {"field": "measures[1]"}}
    },
    {
      "mark": {
        "type": "bar",
        "color": "steelblue",
        "size": {"expr": "innerBarSize3"}
      },
      "encoding": {"x": {"field": "measures[0]"}}
    },
    {
      "mark": {"type": "tick", "color": "black", "size": {"expr": "height"}},
      "encoding": {"x": {"field": "markers[0]"}}
    }
  ],
  "config": {"tick": {"thickness": 2}}
}
```
![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1230674/31bbdb74-c4ae-1ce1-d00d-42b33bd976b2.png)
![13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1230674/f37ca725-24a9-5383-1518-be65e4f8a144.png)

# ■ Bind
[Binding a Parameter](https://vega.github.io/vega-lite/docs/bind.html)
外部入力要素・判例・スケールに対しパラメータをバインドする事ができます。

## 入力要素へのバインドの例
公式ドキュメントには無いMark:lineの例を作りました。
ドロップダウンの選択によって表示するグラフが変わります。
transform.filterを使って選択した値で絞ってるのです。

また選択した値を```title: { "text": { "expr": "filter" } }```でタイトルを可変にしてます。
（数値はでたらめなのでM1の点数とは関係ありません）

```json-doc:vega-lite
{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "description": "A simple bar chart with embedded data.",
  "title": {
    "text": {
      "expr": "filter"
    }
  },
  "params": [
    {
      "name": "filter",
      "value": "錦鯉",
      "bind": {
        "input": "select", "options": ["錦鯉", "オズワルド", "インディアンス"]
      }
    }
  ],
  "data": {
    "values": [
      {"a": "A", "b": 28, "c": "錦鯉"},{"a": "B", "b": 40, "c": "錦鯉"},{"a": "C", "b": 43, "c": "錦鯉"},{"a": "D", "b": 31, "c": "錦鯉"},{"a": "E", "b": 34, "c": "錦鯉"},{"a": "F", "b": 26, "c": "錦鯉"},{"a": "G", "b": 38, "c": "錦鯉"},{"a": "H", "b": 41, "c": "錦鯉"},{"a": "I", "b": 43, "c": "錦鯉"},
      {"a": "A", "b": 23, "c": "オズワルド"},{"a": "B", "b": 34, "c": "オズワルド"},{"a": "C", "b": 40, "c": "オズワルド"},{"a": "D", "b": 41, "c": "オズワルド"},{"a": "E", "b": 39, "c": "オズワルド"},{"a": "F", "b": 36, "c": "オズワルド"},{"a": "G", "b": 34, "c": "オズワルド"},{"a": "H", "b": 44, "c": "オズワルド"},{"a": "I", "b": 46, "c": "オズワルド"},
      {"a": "A", "b": 30, "c": "インディアンス"},{"a": "B", "b": 33, "c": "インディアンス"},{"a": "C", "b": 30, "c": "インディアンス"},{"a": "D", "b": 25, "c": "インディアンス"},{"a": "E", "b": 32, "c": "インディアンス"},{"a": "F", "b": 22, "c": "インディアンス"},{"a": "G", "b": 35, "c": "インディアンス"},{"a": "H", "b": 37, "c": "インディアンス"},{"a": "I", "b": 40, "c": "インディアンス"}
    ]
  },
  "transform": [
    {
      "filter": "datum.c == filter"
    }
  ],
  "vconcat": [
     {
      "mark": {
        "type": "line"
      },
      "encoding": {
        "x": {
          "field": "a",
          "type": "nominal",
          "axis": {"labelAngle": 0}
        },
        "y": {"field": "b", "type": "quantitative"}
      }
    }
  ]
}
```
![14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1230674/75b89ac3-4a15-5d1d-3399-da3fa16323c3.png)
![15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1230674/0221fdd3-7074-454a-0ef2-3589e7bf6976.png)
![16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1230674/0428fbe1-4ce0-504c-f4f5-41cf7f689097.png)


# ■ Select
[Selection Parameters](https://vega.github.io/vega-lite/docs/selection.html)
fieldsでフィールド、encodingsで軸を指定する等してviewへの入力イベント（クリックやドラッグ）による値をパラメータとするものです。

## 入力要素へのバインドの例
Bindの章で使用したデータを使います。
ヒートマップの行をクリックすると下の折れ線グラフとタイトルが変更されます。

paramsのselectに、fields:"c"を指定してます。
```"title": {}```の中で、```_fil.c```でタイトルを可変にできます。

折れ線グラフのviewブロックでは、filterを用いて選択したフィールドcの値でフィルタリングしてます。

この場合、お気づきかと思いますが、外のviewブロックではなく、markにrectを指定しているブロックでparamsを設定してます。

```json-doc:vega-lite
{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "description": "A simple bar chart with embedded data.",
  "title": {
    "text": {
      "expr": "_fil.c"
    }
  },
  "data": {
    "values": [
      {"a": "A", "b": 28, "c": "錦鯉"},{"a": "B", "b": 40, "c": "錦鯉"},{"a": "C", "b": 43, "c": "錦鯉"},{"a": "D", "b": 31, "c": "錦鯉"},{"a": "E", "b": 34, "c": "錦鯉"},{"a": "F", "b": 26, "c": "錦鯉"},{"a": "G", "b": 38, "c": "錦鯉"},{"a": "H", "b": 41, "c": "錦鯉"},{"a": "I", "b": 43, "c": "錦鯉"},
      {"a": "A", "b": 23, "c": "オズワルド"},{"a": "B", "b": 34, "c": "オズワルド"},{"a": "C", "b": 40, "c": "オズワルド"},{"a": "D", "b": 41, "c": "オズワルド"},{"a": "E", "b": 39, "c": "オズワルド"},{"a": "F", "b": 36, "c": "オズワルド"},{"a": "G", "b": 34, "c": "オズワルド"},{"a": "H", "b": 44, "c": "オズワルド"},{"a": "I", "b": 46, "c": "オズワルド"},
      {"a": "A", "b": 30, "c": "インディアンス"},{"a": "B", "b": 33, "c": "インディアンス"},{"a": "C", "b": 30, "c": "インディアンス"},{"a": "D", "b": 25, "c": "インディアンス"},{"a": "E", "b": 32, "c": "インディアンス"},{"a": "F", "b": 22, "c": "インディアンス"},{"a": "G", "b": 35, "c": "インディアンス"},{"a": "H", "b": 37, "c": "インディアンス"},{"a": "I", "b": 40, "c": "インディアンス"}
    ]
  },
  "vconcat": [
    {
      "encoding": {
        "x": {
          "field": "a",
          "type": "nominal"
        },
        "y": {
          "field": "c",
          "type": "nominal"
        }
      },
      "layer": [
        {
          "params": [
            {
              "name": "_fil",
              "value": "錦鯉",
              "select": {"type": "point", "fields": ["c"]}
            }
          ],
          "mark": {
            "type": "rect"
          },
          "encoding": {
            "color": {
              "condition": [
                {
                  "test": "datum.b >= 40",
                  "value": "aquamarine"
                },
                {
                  "test": "datum.b >= 30 && datum.b < 40",
                  "value": "thistle"
                },
                {
                  "test": "datum.b >= 20 && datum.b < 30",
                  "value": "crimson"
                }
              ]
            }
          }
        },
        {

          "mark": "text",
          "encoding": {
            "text": {
              "field": "b"
            },
            "color": {
              "condition": {"test": "datum['b'] >= 40", "value": "red"},
              "value": "black"
            }
          }
        }
      ]
    },
    {
      "transform": [
        {
          "filter": "_fil.c == datum.c"
        }
      ],
      "mark": {
        "type": "line",
        "size": 4
      },
      "encoding": {
        
        "x": {
          "field": "a",
          "type": "nominal",
          "axis": {"labelAngle": 0}
        },
        "y": {"field": "b", "type": "quantitative", "scale": {"zero": false}},
        "color": {
          "field": "c"
        }
      }
    }
  ]
}
```
![17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1230674/e3aea491-1b6f-1319-1fbc-209ae6c8116e.png)
![18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1230674/13d30453-befe-ba31-f9d2-fc67dca393af.png)
![19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1230674/7c562976-24d6-f2d8-e4f2-f90f292e7293.png)

## selectのプロパティ
| プロパティ     | 型                                            | 設定値                                                                                                      | 説明                                                                                                                                           |
|-----------|----------------------------------------------|----------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------|
| type      | String                                       | "point" &#124; "interval"                                                                                | 必須<br>デフォルトでは、pointはclickイベント、intervalはdragイベント                                                                                              |
| encodings | String[]                                     | "x"または"y"等のencodingに指定した軸                                                                                | 指定した軸の値を取得する                                                                                                                                 |
| fields    | String[]                                     | データのフィールド名                                                                                               | 指定したフィールドの値を取得する                                                                                                                             |
| on        | VegaEventStream &#124; String                | イベント名または[Vega Event Streams](https://vega.github.io/vega/docs/event-streams/)を指定する                       | 入力イベントを指定出来る                                                                                                                                 |
| clear     | VegaEventStream &#124; String &#124; Boolean | イベント名または[Vega Event Streams](https://vega.github.io/vega/docs/event-streams/)またはBooleanを指定して、選択した値をクリアする | 値をクリアする                                                                                                                                      |
| resolve   | String                                       | "point" &#124; "interval" &#124; "intersect"                                                             | facet等を使用したマルチView表示の際に、選択した値をどのように扱うかを決める。<br>global:全体を通して一つの値を持つ<br>interval:個々のセル毎に設定される<br>intersect:個々のセル毎に値は持つが、すべてに含まれる場合に強調表示される |

# ■ まとめ
おおむね以下の使い方である程度の事ができると思います。
あとは応用になってきますが、それは数ある公式サンプルを見たり、あれこれ弄り倒して見て下さい。

```json-doc:vega-lite
{
  "params": [
    {
      // ドロップダウン
      "name": "params1",
      "expr": 
      "value": 1,
      "bind": {
        "input": "select",
        "options": [1, 2, 3]
      }
    },
    {
      // スライダー
      "name": "params2",
      "value": "50",
      "bind": {
        "input": "range",
        "max": 80,
        "min": 25,
        "step": 8
      }
    },
    {
      // param2の値を加工する
      "name": "params3",
      "expr": "params2 / 3"
    },
    {
      // マルチViewで、x軸の値を取得する場合
      "name": "params4",
      "value": "XXXXX",
      "select": {
        "type": "point",
        "encodings": ["x"],
        "resolve": "global"
      }
    },
    {
      // フィールドの値を取得する場合
      "name": "params5",
      "value": "XXXXX",
      "select": {
        "type": "point",
        "fields": ["field1", "field2"]
      }
    },
  ],
}
```

最後に以下のviewを作ってみました。
ソースは[こちら](https://github.com/contrail-triple-crown/vega-lite/blob/master/views/m1_judge_ponts%20copy.json)です。
select_noの選択で、大会ごとのヒートマップを表示し、ヒートマップの審査員の列をクリックすると対象の審査員の得点推移をしたの線グラフに表示します。
select_aggで最大/平均/最少を選択し線グラフのviewを変更できます。

![20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1230674/d83f1e41-4a30-4ea2-c283-825bd89e159e.png)



## うまくいかなかった事
### Duplicate signal name
facetの外でselectを使ったparamsを設定しようとすると```Duplicate signal name: "XXXXXX"```というエラーが発生。
多分外部で定義したparamsが、specで定義したそれぞれのセルに展開されているのではないかと思います。
そうならない為に、spec内でparamsを定義して、resolveを使って各セル内にあるparamをコントロールしてねって事と考えてます。
使用する際はお気を付けください。

### resolve.union
facet内のparams.selectを使った際に、"resolve": "union"にすると、facetのセル毎に直接値を持つと思って、concatを使ってfacetのセル毎に複数Viewを作り、paramsを持つviewを選択したら片方のviewに反映させようと思ったら、選択した値の持ち方がいまいちわからず断念しました。
- 1セル目-1view選択, 2view変化
- 2セル目-1view選択, 2view変化
- 3セル目-1view選択, 2view変化

みたいにできるかと思ったのですが…
ご存知の方がいたら教えて頂きたいです。

# ■ あとがき
出来ると思ってたことができず、ブラックボックスな部分も多く難儀しました・・・
改めてVegaを覚える気はないですが、ガチンコでやるならそこから深堀していかねばと思いましたね。
簡単にデータの可視化ができる!!と思わせて中々奥が深いですね。
今回も相変わらずボリューミーになってしまいましたが、いずれにせよ誰かのお役に立てれば幸いです。

# ■ 関連記事
[vega-liteの基本](https://qiita.com/contrail_triple_crown/items/b7cc0c3922657b17327c)
[vega-liteのmulti-view](https://qiita.com/contrail_triple_crown/items/41d72702340f3aedbd7c)