Chr*_*ian 5 json visualization vega sankey-diagram vega-lite
有人知道如何创建像 Vega-lite 中那样的 Sankey 图吗?
https://observablehq.com/@d3/sankey-diagram

输入将是这样的数据
From | To | Amount
使用 Yuri Astrakhan 的博客文章,我在 Vega 中创建了以下内容。
\n\n{\n "$schema": "https://vega.github.io/schema/vega/v5.2.json",\n "height": 300,\n "width": 600,\n "data": [\n {\n "name": "rawData",\n "values": [\n {"key": {"stk1": "aa", "stk2": "cc"}, "doc_count": 7},\n {"key": {"stk1": "aa", "stk2": "bb"}, "doc_count": 4},\n {"key": {"stk1": "bb", "stk2": "aa"}, "doc_count": 8},\n {"key": {"stk1": "bb", "stk2": "bb"}, "doc_count": 6},\n {"key": {"stk1": "bb", "stk2": "cc"}, "doc_count": 3},\n {"key": {"stk1": "cc", "stk2": "aa"}, "doc_count": 9}\n ],\n "transform": [\n {"type": "formula", "expr": "datum.key.stk1", "as": "stk1"},\n {"type": "formula", "expr": "datum.key.stk2", "as": "stk2"},\n {"type": "formula", "expr": "datum.doc_count", "as": "size"}\n ]\n },\n {\n "name": "nodes",\n "source": "rawData",\n "transform": [\n {\n "type": "filter",\n "expr": "!groupSelector || groupSelector.stk1 == datum.stk1 || groupSelector.stk2 == datum.stk2"\n },\n {"type": "formula", "expr": "datum.stk1+datum.stk2", "as": "key"},\n {"type": "fold", "fields": ["stk1", "stk2"], "as": ["stack", "grpId"]},\n {\n "type": "formula",\n "expr": "datum.stack == \'stk1\' ? datum.stk1+\' \'+datum.stk2 : datum.stk2+\' \'+datum.stk1",\n "as": "sortField"\n },\n {\n "type": "stack",\n "groupby": ["stack"],\n "sort": {"field": "sortField", "order": "descending"},\n "field": "size"\n },\n {"type": "formula", "expr": "(datum.y0+datum.y1)/2", "as": "yc"}\n ]\n },\n {\n "name": "groups",\n "source": "nodes",\n "transform": [\n {\n "type": "aggregate",\n "groupby": ["stack", "grpId"],\n "fields": ["size"],\n "ops": ["sum"],\n "as": ["total"]\n },\n {\n "type": "stack",\n "groupby": ["stack"],\n "sort": {"field": "grpId", "order": "descending"},\n "field": "total"\n },\n {"type": "formula", "expr": "scale(\'y\', datum.y0)", "as": "scaledY0"},\n {"type": "formula", "expr": "scale(\'y\', datum.y1)", "as": "scaledY1"},\n {\n "type": "formula",\n "expr": "datum.stack == \'stk1\'",\n "as": "rightLabel"\n },\n {\n "type": "formula",\n "expr": "datum.total/domain(\'y\')[1]",\n "as": "percentage"\n }\n ]\n },\n {\n "name": "destinationNodes",\n "source": "nodes",\n "transform": [{"type": "filter", "expr": "datum.stack == \'stk2\'"}]\n },\n {\n "name": "edges",\n "source": "nodes",\n "transform": [\n {"type": "filter", "expr": "datum.stack == \'stk1\'"},\n {\n "type": "lookup",\n "from": "destinationNodes",\n "key": "key",\n "fields": ["key"],\n "as": ["target"]\n },\n {\n "type": "linkpath",\n "orient": "horizontal",\n "shape": "diagonal",\n "sourceY": {"expr": "scale(\'y\', datum.yc)"},\n "sourceX": {"expr": "scale(\'x\', \'stk1\') + bandwidth(\'x\')"},\n "targetY": {"expr": "scale(\'y\', datum.target.yc)"},\n "targetX": {"expr": "scale(\'x\', \'stk2\')"}\n },\n {\n "type": "formula",\n "expr": "range(\'y\')[0]-scale(\'y\', datum.size)",\n "as": "strokeWidth"\n },\n {\n "type": "formula",\n "expr": "datum.size/domain(\'y\')[1]",\n "as": "percentage"\n }\n ]\n }\n ],\n "scales": [\n {\n "name": "x",\n "type": "band",\n "range": "width",\n "domain": ["stk1", "stk2"],\n "paddingOuter": 0.05,\n "paddingInner": 0.95\n },\n {\n "name": "y",\n "type": "linear",\n "range": "height",\n "domain": {"data": "nodes", "field": "y1"}\n },\n {\n "name": "color",\n "type": "ordinal",\n "range": "category",\n "domain": {"data": "rawData", "field": "stk1"}\n },\n {\n "name": "stackNames",\n "type": "ordinal",\n "range": ["Source", "Destination"],\n "domain": ["stk1", "stk2"]\n }\n ],\n "axes": [\n {\n "orient": "bottom",\n "scale": "x",\n "encode": {\n "labels": {\n "update": {"text": {"scale": "stackNames", "field": "value"}}\n }\n }\n },\n {"orient": "left", "scale": "y"}\n ],\n "marks": [\n {\n "type": "path",\n "name": "edgeMark",\n "from": {"data": "edges"},\n "clip": true,\n "encode": {\n "update": {\n "stroke": [\n {\n "test": "groupSelector && groupSelector.stack==\'stk1\'",\n "scale": "color",\n "field": "stk2"\n },\n {"scale": "color", "field": "stk1"}\n ],\n "strokeWidth": {"field": "strokeWidth"},\n "path": {"field": "path"},\n "strokeOpacity": {\n "signal": "!groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 0.9 : 0.3"\n },\n "zindex": {\n "signal": "!groupSelector && (groupHover.stk1 == datum.stk1 || groupHover.stk2 == datum.stk2) ? 1 : 0"\n },\n "tooltip": {\n "signal": "datum.stk1 + \' \xe2\x86\x92 \' + datum.stk2 + \' \' + format(datum.size, \',.0f\') + \' (\' + format(datum.percentage, \'.1%\') + \')\'"\n }\n },\n "hover": {"strokeOpacity": {"value": 1}}\n }\n },\n {\n "type": "rect",\n "name": "groupMark",\n "from": {"data": "groups"},\n "encode": {\n "enter": {\n "fill": {"scale": "color", "field": "grpId"},\n "width": {"scale": "x", "band": 1}\n },\n "update": {\n "x": {"scale": "x", "field": "stack"},\n "y": {"field": "scaledY0"},\n "y2": {"field": "scaledY1"},\n "fillOpacity": {"value": 0.6},\n "tooltip": {\n "signal": "datum.grpId + \' \' + format(datum.total, \',.0f\') + \' (\' + format(datum.percentage, \'.1%\') + \')\'"\n }\n },\n "hover": {"fillOpacity": {"value": 1}}\n }\n },\n {\n "type": "text",\n "from": {"data": "groups"},\n "interactive": false,\n "encode": {\n "update": {\n "x": {\n "signal": "scale(\'x\', datum.stack) + (datum.rightLabel ? bandwidth(\'x\') + 8 : -8)"\n },\n "yc": {"signal": "(datum.scaledY0 + datum.scaledY1)/2"},\n "align": {"signal": "datum.rightLabel ? \'left\' : \'right\'"},\n "baseline": {"value": "middle"},\n "fontWeight": {"value": "bold"},\n "text": {\n "signal": "abs(datum.scaledY0-datum.scaledY1) > 13 ? datum.grpId : \'\'"\n }\n }\n }\n },\n {\n "type": "group",\n "data": [\n {\n "name": "dataForShowAll",\n "values": [{}],\n "transform": [{"type": "filter", "expr": "groupSelector"}]\n }\n ],\n "encode": {\n "enter": {\n "xc": {"signal": "width/2"},\n "y": {"value": 30},\n "width": {"value": 80},\n "height": {"value": 30}\n }\n },\n "marks": [\n {\n "type": "group",\n "name": "groupReset",\n "from": {"data": "dataForShowAll"},\n "encode": {\n "enter": {\n "cornerRadius": {"value": 6},\n "fill": {"value": "#f5f5f5"},\n "stroke": {"value": "#c1c1c1"},\n "strokeWidth": {"value": 2},\n "height": {"field": {"group": "height"}},\n "width": {"field": {"group": "width"}}\n },\n "update": {"opacity": {"value": 1}},\n "hover": {"opacity": {"value": 0.7}}\n },\n "marks": [\n {\n "type": "text",\n "interactive": false,\n "encode": {\n "enter": {\n "xc": {"field": {"group": "width"}, "mult": 0.5},\n "yc": {\n "field": {"group": "height"},\n "mult": 0.5,\n "offset": 2\n },\n "align": {"value": "center"},\n "baseline": {"value": "middle"},\n "fontWeight": {"value": "bold"},\n "text": {"value": "Show All"}\n }\n }\n }\n ]\n }\n ]\n },\n {\n "type": "rect",\n "from": {"data": "nodes"},\n "encode": {\n "enter": {\n "stroke": {"value": "#000"},\n "strokeWidth": {"value": 2},\n "width": {"scale": "x", "band": 1},\n "x": {"scale": "x", "field": "stack"},\n "y": {"field": "y0", "scale": "y"},\n "y2": {"field": "y1", "scale": "y"}\n }\n }\n }\n ],\n "signals": [\n {\n "name": "groupHover",\n "value": {},\n "on": [\n {\n "events": "@groupMark:mouseover",\n "update": "{stk1:datum.stack==\'stk1\' && datum.grpId, stk2:datum.stack==\'stk2\' && datum.grpId}"\n },\n {"events": "mouseout", "update": "{}"}\n ]\n },\n {\n "name": "groupSelector",\n "value": false,\n "on": [\n {\n "events": "@groupMark:click!",\n "update": "{stack:datum.stack, stk1:datum.stack==\'stk1\' && datum.grpId, stk2:datum.stack==\'stk2\' && datum.grpId}"\n },\n {\n "events": [\n {"type": "click", "markname": "groupReset"},\n {"type": "dblclick"}\n ],\n "update": "false"\n }\n ]\n }\n ]\n}\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
5284 次 |
| 最近记录: |