前回は以下
散布図
このExample の場合、dataは外部から取得しています("url": "data/cars.json"
)。
dataの中身は、ここで確認できます。
データ投入
Elasticsearch へのデータ投入は、今回は[Machine Learning]-[Data Visualizer]-[Import Data]を利用しました。
ただし、Importするには、Json ではなくNewline-delimited JSONである必要があるので、以下のスクリプトで変換。
https://gist.github.com/ihgs/caf192943889607afe3c4175fd776917
cat cars.json | python json2ndjson.py > cars.ndjson
Vega HJSON
{
$schema: https://vega.github.io/schema/vega/v3.3.1.json
width: 200
height: 200
padding: 5
data: [
{
name: source
url: { // imoprt したデータのindexを指定
index: vega_example_cars
body: {size: 1000}
}
format: {property: "hits.hits"}
transform: [
{
type: filter
expr: datum['_source'].Horsepower != null && datum['_source'].Miles_per_Gallon != null && datum['_source'].Acceleration != null
}
]
}
]
scales: [
{
name: x
type: linear
round: true
nice: true
zero: true
domain: {data: "source", field: "_source.Horsepower"}
range: width
}
{
name: y
type: linear
round: true
nice: true
zero: true
domain: {data: "source", field: "_source.Miles_per_Gallon"}
range: height
}
{
name: size
type: linear
round: true
nice: false
zero: true
domain: {data: "source", field: "_source.Acceleration"}
range: [4, 361]
}
]
axes: [
{
scale: x
grid: true
domain: false
orient: bottom
tickCount: 5
title: Horsepower
}
{
scale: y
grid: true
domain: false
orient: left
titlePadding: 5
title: Miles_per_Gallon
}
]
legends: [
{
size: size
title: Acceleration
format: s
symbolStrokeColor: "#4682b4"
symbolStrokeWidth: 2
symbolOpacity: 0.5
symbolType: circle
}
]
marks: [
{
name: marks
type: symbol
from: {data: "source"}
encode: {
update: {
x: {scale: "x", field: "_source.Horsepower"}
y: {scale: "y", field: "_source.Miles_per_Gallon"}
size: {scale: "size", field: "_source.Acceleration"}
shape: {value: "circle"}
strokeWidth: {value: 2}
opacity: {value: 0.5}
stroke: {value: "#4682b4"}
fill: {value: "transparent"}
}
}
}
]
}
メモ
- transform: filterを使い欠損データを除外
- marks: type にsymbolを指定。
ネットワーク図
データ投入
上と同様に、[Machine Learning]-[Data Visualizer]-[Import Data]を利用。
今回のデータは、{"nodes": ..., "links": ...}
といった構造になっており、vegaで描画する際は、nodes, links を別々に扱っているので、それぞれindexをわけてImport してみます。
投入データは同様にスクリプトで変換しておきます。
cat miserables.json | python json2ndjson.py --key nodes > nodes.ndjson
cat miserables.json | python json2ndjson.py --key links > links.ndjson
Vega HJSON
{
$schema: https://vega.github.io/schema/vega/v3.3.1.json
width: 700
height: 500
padding: 0
autosize: none
signals: [
{name: "cx", update: "width / 2"}
{name: "cy", update: "height / 2"}
{
name: nodeRadius
value: 8
bind: {input: "range", min: 1, max: 50, step: 1}
}
{
name: nodeCharge
value: -30
bind: {input: "range", min: -100, max: 10, step: 1}
}
{
name: linkDistance
value: 30
bind: {input: "range", min: 5, max: 100, step: 1}
}
{
name: static
value: true
bind: {input: "checkbox"}
}
{
description: State variable for active node fix status.
name: fix
value: false
on: [
{
events: symbol:mouseout[!event.buttons], window:mouseup
update: "false"
}
{events: "symbol:mouseover", update: "fix || true"}
{
events: "[symbol:mousedown, window:mouseup] > window:mousemove!"
update: xy()
force: true
}
]
}
{
description: Graph node most recently interacted with.
name: node
value: null
on: [
{
events: symbol:mouseover
update: fix === true ? item() : node
}
]
}
{
description: Flag to restart Force simulation upon data changes.
name: restart
value: false
on: [
{
events: {signal: "fix"}
update: fix && fix.length
}
]
}
]
data: [
//修正
{
name: node-data
url: {index: "test_vega_tree_nodes", size: 1000}
format: {type: "json", property: "hits.hits"}
}
{
name: link-data
url: {index: "test_vega_tree_links", size: 1000}
format: {type: "json", property: "hits.hits"}
transform: [
{
type: formula
expr: datum['_source'].source
as: source
}
{
type: formula
expr: datum['_source'].target
as: target
}
{type: "formula", expr: "datum['_source'].value", as: "value"}
]
}
]
scales: [
{
name: color
type: ordinal
domain: {data: "node-data", field: "_source.group"}
range: {scheme: "category20c"}
}
]
marks: [
{
name: nodes
type: symbol
zindex: 1
from: {data: "node-data"}
on: [
{
trigger: fix
modify: node
values: fix === true ? {fx: node.x, fy:node.y} : {fx: fix[0], fy: fix[1]}
}
{
trigger: !fix
modify: node
values: "{fx: null, fy: null}"
}
]
encode: {
enter: {
fill: {scale: "color", field: "_source.group"}
stroke: {value: "white"}
}
update: {
size: {signal: "2 * nodeRadius * nodeRadius"}
cursor: {value: "pointer"}
}
}
transform: [
{
type: force
iterations: 300
restart: {signal: "restart"}
static: {signal: "static"}
signal: force
forces: [
{
force: center
x: {signal: "cx"}
y: {signal: "cy"}
}
{
force: collide
radius: {signal: "nodeRadius"}
}
{
force: nbody
strength: {signal: "nodeCharge"}
}
{
force: link
links: link-data
distance: {signal: "linkDistance"}
}
]
}
]
}
{
type: path
from: {data: "link-data"}
interactive: false
encode: {
update: {
stroke: {value: "#ccc"}
strokeWidth: {value: 0.5}
}
}
transform: [
{
type: linkpath
require: {signal: "force"}
shape: line
sourceX: datum.source.x
sourceY: datum.source.y
targetX: datum.target.x
targetY: datum.target.y
}
]
}
]
}
メモ
- data: node,linkをそれぞれで取得。
- marks内のtransform のtype:force, force:link linkのデータをきちんと値を渡せるようにするため、source とtarget を設定するように変換を実施。なお、一番下のtype:linkpath で使っているdatum.source.xとかは、force:link で変換されたものを利用。
まとめ
ファイルからデータを投入できる[Machine Learning]-[Data Visualizer]-[Import Data]は便利。
次回は、kibanaとより連携する部分を調べていこうと思います。 vega そのものについて解説していく記事を優先することにしました。