LoginSignup
0
1

More than 1 year has passed since last update.

vega-liteのmulti-view

Last updated at Posted at 2022-03-30

■ はじめに

前回の記事vega-liteの基本の続きになります。
前回ご紹介したviewは一つのviewを表示するシングルviewです。
今回は同じデータで複数のview表示を行うMulti-viewついて書いていこうと思います。

Multi-view

Composing Layered & Multi-view Plots
vegaには以下の演算子(公式ドキュメントには演算子と記載されている)を用いて複数のviewを表示する事ができます。

  • Concatenation
  • Layering
  • Faceting
  • Repeating
  • Resolution

■ concat

Concatenating views

To place views side-by-side, Vega-Lite’s view composition provides the following concatenation operators:

記載の通り複数のviewを並べて配置します。
以下の3つ種類があります。

  • vconcat
    • 垂直に並べる
  • hconcat
    • 水平に並べる
  • concat
    • 一般的な連結(columnsプロパティで水平に並べる数を指定する)

公式の例

facet pageのvconcatの例です。

vega-lite
{
  "data": {
    "url": "data/weather.csv"
  },
  "transform": [{
    "filter": "datum.location === 'Seattle'"
  }],
  "hconcat": [
    {
      "mark": "bar",
      "encoding": {
        "x": {
          "timeUnit": "month",
          "field": "date",
          "type": "ordinal"
        },
        "y": {
          "aggregate": "mean",
          "field": "precipitation"
        }
      }
    },
    {
      "mark": "point",
      "encoding": {
        "x": {
          "field": "temp_min",
          "bin": true
        },
        "y": {
          "field": "temp_max",
          "bin": true
        },
        "size": {"aggregate": "count"}
      }
    }
  ]
}

09.png
見ての通り、水平に別々のviewを並べて表示しています。

入れ子にする

concatは入れ子にできます。
上記の例を少し拡張します。

vega-lite
{
  "data": {
    "url": "data/weather.csv"
  },

  "vconcat": [
    {
      "transform": [{
        "filter": "datum.location === 'Seattle'"
      }],
      "hconcat": [
        {
          "mark": "bar",
          "encoding": {
            "x": {
              "timeUnit": "month",
              "field": "date",
              "type": "ordinal"
            },
            "y": {
              "aggregate": "mean",
              "field": "precipitation"
            }
          }
        },
        {
          "mark": "point",
          "encoding": {
            "x": {
              "field": "temp_min",
              "bin": true
            },
            "y": {
              "field": "temp_max",
              "bin": true
            },
            "size": {"aggregate": "count"}
          }
        }
      ]
    },
    {
      "transform": [{
        "filter": "datum.location === 'New York'"
      }],
      "hconcat": [
        {
          "mark": "bar",
          "encoding": {
            "x": {
              "timeUnit": "month",
              "field": "date",
              "type": "ordinal"
            },
            "y": {
              "aggregate": "mean",
              "field": "precipitation"
            }
          }
        },
        {
          "mark": "point",
          "encoding": {
            "x": {
              "field": "temp_min",
              "bin": true
            },
            "y": {
              "field": "temp_max",
              "bin": true
            },
            "size": {"aggregate": "count"}
          }
        }
      ]
    }
  ]
}

10.png

hconcatで水平に並べたviewを縦に並べてます。
元々の例にはtransformのfilterを使って、locationをシアトルに絞って表示してましたので、加えたもう一つのviewでは同じデータにあるニューヨークでfilterしてみました。

vconcat[
  {
    transform: [filter]  // (locaton=シアトル)
    hconcat: [
      {view},
      {view}
    ]
  },
  {
    transform: [filter]  // (locaton=ニューヨーク)
    hconcat: [
      {view},
      {view}
    ]
  }

■ layer

Layering views

Sometimes, it’s useful to superimpose one chart on top of another. You can accomplish this by using the layer operator. This operator is one of Vega-Lite’s view composition operators. To define a layered chart, put multiple specifications into an array under the layer property.

チャートを別のチャートに重ねるとありますね。
1つのviewに複数のグラフを重ねるのがlayerになります。

公式の例

facet pageのvconcatの例です。

vega-lite
{
  "data": {"url": "data/stocks.csv"},
  "layer": [
    {
      "mark": "line",
      "encoding": {
        "x": {"field": "date", "type": "temporal"},
        "y": {"field": "price", "type": "quantitative"},
        "color": {"field": "symbol", "type": "nominal"}
      }
    },
    {
      "mark": "rule",
      "encoding": {
        "y": {"field": "price", "aggregate": "mean"},
        "size": {"value": 2},
        "color": {"field": "symbol"}
      }
    }
  ]
}

1個目のview

04-2.png

2個目のview

04-1.png

合わせて表示

04.png
これは、mark:lineとmark:ruleの二つのviewを同じviewに重ねて表示しています。

その他の例1

棒グラフと線グラフを同じviewに表示してみます。

vega-lite
{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "data": {
    "values": [
      {
        "a": 1,
        "b": "hoge",
        "c": 100
      },
      {
        "a": 2,
        "b": "hoge",
        "c": 90
      },
      {
        "a": 3,
        "b": "hoge",
        "c": 115
      },
      {
        "a": 4,
        "b": "fuga",
        "c": 233
      },
      {
        "a": 5,
        "b": "fuga",
        "c": 68
      }
    ]
  },
  "layer": [
    {
      "mark": "bar",
      "encoding": {
        "x": {
          "field": "a"
        },
        "y": {
          "field": "c",
          "type": "quantitative"
        },
        "color": {
          "field": "c"
        }
      }
    },
    {
      "mark": "line",
      "encoding": {
        "x": {
          "field": "a"
        },
        "y": {
          "field": "c",
          "type": "quantitative"
        },
        "color": {
          "value": "#196004"
        }
      }
    }
  ]
}

05.png

その他の例2

ヒートマップと値を表示します。
ここでは上記の例と同じデータを使ってます。

vega-lite
{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "data": {
     ・・・
  },
  "encoding": {
    "x": {
      "field": "a"
    },
    "y": {
      "field": "b"
    }
  },
  "layer": [
    {
      "mark": "rect",
      "encoding": {
        "color": {
          "field": "c"
        }

      },
    },
    {
      "mark": "text",
      "encoding": {
        "text": {
          "field": "c"
        }
      }
    }
  ]
}

06.png

ここではてなが一つ。
一つ目のencodingがlayerの外にありますよね、これが混乱の種なんですよ・・・
公式ドキュメントを見てもencodengやcolorやfield等が様々な個所で出てくるので慣れるまではちょっと辛かったですね。

この例に関して私なりに解釈すると、

  • まずは大外のブロックが単一のviewと考えます。
  • 大外のviewに対してencodingでx軸y軸を定義します。
  • layerブロックの一つ目で、mark:rectを描画する(軸は外で指定したもの)
  • もう一つで同じviewに数値を表示するようなイメージですかね。

この解釈や表現が正しいかわかりませんが、現状このように言えると考えてます。

■ Facet

Faceting a Plot into a Trellis Plot

A Trellis plot (or small multiple) is a series of similar plots that displays different subsets of the same data, facilitating comparison across subsets.

トレリスプロット(または小さな倍数)は、同じデータの異なるサブセットを表示する一連の類似したプロットであり、サブセット間の比較を容易にします。

はい、なんのこっちゃですね。

公式の例

facet pageの一番最初の例です。

vega-lite
{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "data": {"url": "data/cars.json"},
  "facet": {"row": {"field": "Origin"}},
  "spec": {
    "mark": "bar",
    "encoding": {
      "x": {
        "bin": {"maxbins": 15},
        "field": "Horsepower",
        "type": "quantitative"
      },
      "y": {"aggregate": "count", "type": "quantitative"}
    }
  }
}

03.png

使用しているデータは/data/cars.jsonです。
データとソースとviewを見ていただければわかると思いますが

  • field"Horsepower"をx軸として
  • y軸が個数(count)の棒グラフを
  • filed"Origin"をkeyとして垂直に並べる

これをやってくれる機能facetとなります。

このfacetプロパティの"row"を"culmun"に変更して見て下さい。
横に並びます。

vega-lite
{
  ・・・
-  "facet": {"row": {"field": "Origin"}},
+  "facet": {"column": {"field": "Origin"}},
  "spec": {
    ・・・
  }
}

入れ子にする

はい、concatでも示したようにfacetの中でも入れ子にできます。
事例やソースは複雑な入れ子のviewの方に記載します。

■ repeat

Repeat a View

The repeat operator is part of Vega-Lite’s view composition. It provides a shortcut that creates a view for each entry in an array of fields. This operator generates multiple plots like facet. However, unlike facet it allows full replication of a data set in each view.

facetのような複数プロットの表示をするが、対象のフィールドを指定して表示できるものです。
ただし、facetとは異なり、各ビューでデータセットを完全に複製できます。

公式の例

vega-lite
{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "description": "",
  "data": {"url": "data/weather.csv"},
  "repeat": ["temp_max", "precipitation", "wind"],
  "spec": {
    "mark": "line",
    "encoding": {
      "x": {"field": "date", "timeUnit": "month"},
      "y": {"field": {"repeat": "repeat"}, "aggregate": "mean"},
      "color": {"field": "location"}
    }
  }
}

07.png

この例のデータ

ここではdocs/data/weather.csvを使ってます。

location,date,precipitation,temp_max,temp_min,wind,weather
Seattle,2012-01-01,0.0,12.8,5.0,4.7,drizzle
Seattle,2012-01-02,10.9,10.6,2.8,4.5,rain
・・・
New York,2012-01-01,1.8,10.0,3.3,5.1,rain
New York,2012-01-02,0.0,10.0,0.6,8.7,sun

これは、x軸がdateで、y軸がrepeatに設定したフィールドになります。
一つ目が最高気温、二つ目が降水量、三つめが風速で、colorに指定してるのがlocationなのでここではニューヨークとシアトルの地名が対象となってます。
データには最低気温もあるので以下のように変更するとviewが一つ増えます。

vega-lite
{
  ・・・
-    "repeat": ["temp_max", "precipitation", "wind"],
+    "repeat": ["temp_max", "precipitation", "wind", "temp_min"],
}

facetでやってみた

facetを使ってに同じようにできないものかと思い試してみる。
上記と同様のデータを使いますが、facetで扱うにはデータ構造を変更する必要があります。
ちゃんとしたデータを作るのがしんどかったので、簡単にそして少しだけのデータでやってみました。

vega-lite
{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "description": "",
  "data": {
    "values": [
      {
        "location": "Seattle",
        "data": [
          {
            "date": "2012-01-01",
            "values": [
              {
                "type": "precipitation",
                "value": 0
              },
              {
                "type": "temp_max",
                "value": 12.8
              },
              {
                "type": "temp_min",
                "value": 5
              },
              {
                "type": "wind",
                "value": 4.7
              }
            ]
          },
          {
            "date": "2012-01-02",
            "values": [
              {
                "type": "precipitation",
                "value": 10.9
              },
              {
                "type": "temp_max",
                "value": 10.6
              },
              {
                "type": "temp_min",
                "value": 2.8
              },
              {
                "type": "wind",
                "value": 4.8
              }
            ]
          },
          {
            "date": "2012-01-03",
            "values": [
              {
                "type": "precipitation",
                "value": 0.8
              },
              {
                "type": "temp_max",
                "value": 11.7
              },
              {
                "type": "temp_min",
                "value": 7.2
              },
              {
                "type": "wind",
                "value": 2.3
              }
            ]
          }
        ]
      },
      {
        "location": "New York",
        "data": [
          {
            "date": "2012-01-01",
            "values": [
              {
                "type": "precipitation",
                "value": 1.8
              },
              {
                "type": "temp_max",
                "value": 10
              },
              {
                "type": "temp_min",
                "value": 3.3
              },
              {
                "type": "wind",
                "value": 5.1
              }
            ]
          },
          {
            "date": "2012-01-02",
            "values": [
              {
                "type": "precipitation",
                "value": 0
              },
              {
                "type": "temp_max",
                "value": 10
              },
              {
                "type": "temp_min",
                "value": 0.6
              },
              {
                "type": "wind",
                "value": 8.7
              }
            ]
          },
          {
            "date": "2012-01-03",
            "values": [
              {
                "type": "precipitation",
                "value": 0.1
              },
              {
                "type": "temp_max",
                "value": 0.6
              },
              {
                "type": "temp_min",
                "value": -8.9
              },
              {
                "type": "wind",
                "value": 8.2
              }
            ]
          }

        ]
      }
    ]
  },
  "transform": [
    {
      "flatten": ["data"],
      "as": ["_data"]
    },
    {
      "flatten": ["_data.values"],
      "as": ["_values"]
    }
  ],
  "facet": {
    "column": {
      "field": "_values.type"
    }
  },
  "spec": {
    "mark": "line",
    "encoding": {
      "x": {
        "field": "_data.date"
      },
      "y": {
        "field": "_values.value",
        "type": "quantitative"
      },
      "color": {
        "field": "location"
      }
    }
  },
  "resolve": {"scale": {"color": "independent"}}
}

08.png
とりあえず近しい形にはできました。
データが少ないので時間軸が三日分です。

データ構造の違い

公式のサンプルデータではcsv形式だったけど、私が作ったfacetの例ですと、location毎に日付毎のlistを持ち、日付毎にtypeとvalueを持つlistを持ってます。
facetで並べるためには、keyとなるフィールドを指定する必要がなります。
ですので、データ上に対象項目(ここではtemp_max等)の名前をtypeとして持たせて、facetのfieldに指定する方法を取りました。

ただ、そのままのデータではネストされたオブジェクトですのでデータ変換を行います。
vega-liteではview上でネストされたリスト内のオブジェクトにはアクセスできないので、ここではtransformのflattenを使用してデータ構造を変換してます。
transform,flattenにに関してはここでは詳しく触れませんが、データ構造としては以下のように変更されます。

location data _values.type _data.date _values.value
Seattle ・・・ precipitation 2012-01-01 0
Seattle ・・・ temp_max 2012-01-01 12.8
Seattle ・・・ temp_min 2012-01-01 5
・・・ ・・・ ・・・ ・・・ ・・・
New York ・・・ precipitation 2012-01-01 1.8
New York ・・・ temp_max 2012-01-01 10
・・・ ・・・ ・・・ ・・・ ・・・

そして以下のように軸を設定してます。

  • facetのkeyに_data.type
  • specのx軸に_data.date
  • specのy軸に_values.value

最後に "resolve": {"scale": {"color": "independent"}}を設定してますが、これは範囲を個別にする設定になります。
これが無ければ、facet上のy軸の範囲が全て同じになりますので、この例で言うと全valueの範囲になり何のデータかわかりにくくなります。
Scale and Guide Resolution

■ 複雑な入れ子のview

この記事で紹介した内容を使って、少し複雑なviewを作ってみました。
M1グランプリの歴代の得点データ1を作ってみましたのでこちらからデータを取得して色々いじってみて下さい。

11.png
全大会を表示すると画像が大きくなるのでfilterで3大会まで表示しています。

以下のような構成になっています。
詳細はvega-liteのソースを御覧ください。

vega-lite
{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "description": "",
  "data": {
    "url": "../data/m1_points.json"
  },
  "transform": [
  ],
  "facet": {
    "row": {}
  },
  "spec": {
    "hconcat": [
      {
        "layer": [
          {
            "mark": {},
            "encoding": {}
          },
          {
            "mark": {},
            "encoding": {}
          }
        ]
      },
      {
        "layer": [
          {
            "mark": {},
            "encoding": {}
          }
        ]
      },
      {
        "transform": [],
        "facet": {
          "row": {}
        },
        "spec": {
          "layer": [
            {
              "mark": {},
              "encoding": {}
            },
            {
              "mark": {},
              "encoding": {}
            }
          ]
        }
      }
    ]
  },
  "config": {},
  "resolve": {}
  }
}

■ あとがき

ちょっと思ってたよりもボリューミーな内容(データ作るのもちょっとしんどかった)になってしまいましたが、vega-liteのMulti-viewについて紹介させてもらいました。
multi-viewを用いて入れ子にすることで別々のviewを重ねたり、垂直や水平に並べる事ができます。
まだ詳しく紹介していないtransformの機能もたっぷり入ってしまって申し訳ないですが、今後また記事にしていきたいと思います。

■ 関連記事

vega-liteの基本
vega-liteのParameter

  1. 出典wikipedia

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1