Kibana Vega力有向图

9njqaruj  于 9个月前  发布在  Kibana
关注(0)|答案(1)|浏览(114)

刚接触vega和可视化,但我在一个弹性示例中有数据,想尝试可视化一些关系。我最近发现了这个主题,它使用了在我的本地示例中显示的硬值。text
我很确定我的问题会集中在这里:

"data": [
    {
      "name": "node-data",
      "url": "data/miserables.json",
      "format": {"type": "json", "property": "nodes"}
    },
    {
      "name": "link-data",
      "url": "data/miserables.json",
      "format": {"type": "json", "property": "links"}
    }
  ],

字符串
我想使用我的索引作为数据源,而不是硬编码的数据或托管的json文件。我的索引和类似的数据包括:

公司

  • name(text)[example:'alphabet',' google','youtube','microsoft']
  • group(text)[example:'holding company','technology & services',' streaming ','technology & services']
  • unique_id(integer)[示例:0,1,2,3]
    关系
  • source(integer)[example:0,0,0]
  • target(integer)[示例:1,2,3]
  • relationship(text)[example:'parent','parent','competitor']
  • group_id(integer)[示例:0,0,1]

我的基本目标是可视化关系,应用标签,并基于group_id进行颜色编码。

  • 按名称标记的节点
  • 基于group_id的节点颜色
  • 按关系标记的链接

更先进的是,如果节点的形状可以根据公司索引中的组进行更改,例如:

  • 控股公司= Circle
  • 技术和服务=三角形
  • 流=平方

我将继续寻找可能有助于解决这个问题的主题和代码片段,但我感谢任何可以提供的帮助。(值得注意的是,我确实从堆栈溢出的代码中创建了一个很好的多部分sankey,并试图以这种方式导入我的变量,但我刚刚收到错误。可能是由于数据的调用/格式化方式。)

gt0wga4j

gt0wga4j1#

这是我创建的一个力定向图,它支持平移、缩放、节点拖动、动态标签、网络突出显示和显示传输方向的箭头。


的数据
https://github.com/PBI-David/Deneb-Showcase#force-direct-graph-dynamic

{
  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "description": "Dataviz by David Bacci: https://www.linkedin.com/in/davbacci/",
  "width": 1400,
  "height": 800,
  "padding": {"left": 0, "right": 0, "top": 0, "bottom": 0},
  "autosize": "pad",
  "background": "#f4f7ff",
  "signals": [
    {"name": "xrange", "update": "[0, width]"},
    {"name": "yrange", "update": "[height, 0]"},
    {"name": "xext", "update": "[0, width]"},
    {"name": "yext", "update": "[height, 0]"},
    {
      "name": "down",
      "value": null,
      "on": [
        {"events": "mouseup,touchend", "update": "null"},
        {"events": "mousedown, touchstart", "update": "xy()"},
        {"events": "symbol:mousedown, symbol:touchstart", "update": "null"}
      ]
    },
    {
      "name": "xcur",
      "value": null,
      "on": [{"events": "mousedown, touchstart, touchend", "update": "xdom"}]
    },
    {
      "name": "ycur",
      "value": null,
      "on": [{"events": "mousedown, touchstart, touchend", "update": "ydom"}]
    },
    {
      "name": "delta",
      "value": [0, 0],
      "on": [
        {
          "events": [
            {
              "source": "window",
              "type": "mousemove",
              "consume": true,
              "between": [
                {"type": "mousedown"},
                {"source": "window", "type": "mouseup"}
              ]
            },
            {
              "type": "touchmove",
              "consume": true,
              "filter": "event.touches.length === 1"
            }
          ],
          "update": "down ? [down[0]-x(), y()-down[1]] : [0,0]"
        }
      ]
    },
    {
      "name": "anchor",
      "value": [0, 0],
      "on": [
        {
          "events": "wheel",
          "update": "[invert('xscale', x()), invert('yscale', y())]"
        },
        {
          "events": {
            "type": "touchstart",
            "filter": "event.touches.length===2"
          },
          "update": "[(xdom[0] + xdom[1]) / 2, (ydom[0] + ydom[1]) / 2]"
        }
      ]
    },
    {
      "name": "zoom",
      "value": 1,
      "on": [
        {
          "events": "wheel!",
          "force": true,
          "update": "pow(1.001, event.deltaY * pow(16, event.deltaMode))"
        },
        {
          "events": {"signal": "dist2"},
          "force": true,
          "update": "dist1 / dist2"
        },
        {"events": [{"source": "view", "type": "dblclick"}], "update": "1"}
      ]
    },
    {
      "name": "dist1",
      "value": 0,
      "on": [
        {
          "events": {
            "type": "touchstart",
            "filter": "event.touches.length===2"
          },
          "update": "pinchDistance(event)"
        },
        {"events": {"signal": "dist2"}, "update": "dist2"}
      ]
    },
    {
      "name": "dist2",
      "value": 0,
      "on": [
        {
          "events": {
            "type": "touchmove",
            "consume": true,
            "filter": "event.touches.length===2"
          },
          "update": "pinchDistance(event)"
        }
      ]
    },
    {
      "name": "xdom",
      "update": "xext",
      "on": [
        {
          "events": {"signal": "delta"},
          "update": "[xcur[0] + span(xcur) * delta[0] / width, xcur[1] + span(xcur) * delta[0] / width]"
        },
        {
          "events": {"signal": "zoom"},
          "update": "[anchor[0] + (xdom[0] - anchor[0]) * zoom, anchor[0] + (xdom[1] - anchor[0]) * zoom]"
        },
        {"events": [{"source": "view", "type": "dblclick"}], "update": "xrange"}
      ]
    },
    {
      "name": "ydom",
      "update": "yext",
      "on": [
        {
          "events": {"signal": "delta"},
          "update": "[ycur[0] + span(ycur) * delta[1] / height, ycur[1] + span(ycur) * delta[1] / height]"
        },
        {
          "events": {"signal": "zoom"},
          "update": "[anchor[1] + (ydom[0] - anchor[1]) * zoom, anchor[1] + (ydom[1] - anchor[1]) * zoom]"
        },
        {"events": [{"source": "view", "type": "dblclick"}], "update": "yrange"}
      ]
    },
    {"name": "size", "update": "clamp(20 / span(xdom), 1, 1000)"},
    {
      "name": "cx",
      "update": "width / 2",
      "on": [
        {
          "events": "[symbol:mousedown, window:mouseup] > window:mousemove",
          "update": " cx==width/2?cx+0.001:width/2"
        }
      ]
    },
    {"name": "cy", "update": "height / 2"},
    {
      "name": "nodeRadiusKey",
      "description": "q=increase size, a=decrease size",
      "value": 8,
      "on": [
        {
          "events": "window:keypress",
          "update": "event.key=='a'&&nodeRadiusKey>1?nodeRadiusKey-1:event.key=='q'?nodeRadiusKey+1:nodeRadiusKey"
        }
      ]
    },
    {
      "name": "nodeRadius",
      "value": 8,
      "bind": {"input": "range", "min": 1, "max": 50, "step": 1},
      "on": [{"events": {"signal": "nodeRadiusKey"}, "update": "nodeRadiusKey"}]
    },
    {
      "name": "nodeCharge",
      "value": -30,
      "bind": {"input": "range", "min": -100, "max": 10, "step": 1}
    },
    {
      "name": "linkDistance",
      "value": 30,
      "bind": {"input": "range", "min": 5, "max": 300, "step": 1}
    },
    {
      "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", "force": 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 ? datum.index : node"
        }
      ]
    },
    {
      "name": "nodeHover",
      "value": {"id": null, "connections": []},
      "on": [
        {
          "events": "symbol:mouseover",
          "update": "{'id':datum.index, 'connections':split(datum.sources+','+datum.targets,',')}"
        },
        {"events": "symbol:mouseout", "update": "{'id':null, 'connections':[]}"}
      ]
    },
    {
      "description": "Flag to restart Force simulation upon data changes.",
      "name": "restart",
      "value": false,
      "on": [{"events": {"signal": "fix"}, "update": "fix && fix.length"}]
    }
  ],
  "data": [
    {
      "name": "link-data-raw",
      "url": "https://raw.githubusercontent.com/PBI-David/Deneb-Showcase/main/Force%20Directed%20Graph/data.json",
      "format": {"type": "json", "property": "links"}
    },
    {"name": "link-data", "source": "link-data-raw"},
    {
      "name": "source-connections",
      "source": "link-data-raw",
      "transform": [
        {
          "type": "aggregate",
          "groupby": ["source"],
          "ops": ["values"],
          "fields": ["target"],
          "as": ["connections"]
        },
        {
          "type": "formula",
          "as": "targets",
          "expr": "pluck(datum.connections, 'target')"
        }
      ]
    },
    {
      "name": "target-connections",
      "source": "link-data-raw",
      "transform": [
        {
          "type": "aggregate",
          "groupby": ["target"],
          "ops": ["values"],
          "fields": ["source"],
          "as": ["connections"]
        },
        {
          "type": "formula",
          "as": "sources",
          "expr": "pluck(datum.connections, 'source')"
        }
      ]
    },
    {
      "name": "node-data",
      "url": "https://raw.githubusercontent.com/PBI-David/Deneb-Showcase/main/Force%20Directed%20Graph/data.json",
      "format": {"type": "json", "property": "nodes"},
      "transform": [
        {
          "type": "lookup",
          "from": "source-connections",
          "key": "source",
          "fields": ["name"],
          "values": ["targets"],
          "as": ["targets"],
          "default": [""]
        },
        {
          "type": "lookup",
          "from": "target-connections",
          "key": "target",
          "fields": ["name"],
          "values": ["sources"],
          "as": ["sources"],
          "default": [""]
        },
        {
          "type": "force",
          "iterations": 300,
          "restart": {"signal": "restart"},
          "signal": "force",
          "forces": [
            {"force": "center", "x": {"signal": "cx"}, "y": {"signal": "cy"}},
            {
              "force": "collide",
              "radius": {"signal": "sqrt(4 * nodeRadius * nodeRadius)"},
              "iterations": 1,
              "strength": 0.7
            },
            {"force": "nbody", "strength": {"signal": "nodeCharge"}},
            {
              "force": "link",
              "links": "link-data-raw",
              "distance": {"signal": "linkDistance"},
              "id": "name"
            }
          ]
        },
        {
          "type": "formula",
          "as": "fx",
          "expr": "fix[0]!=null && node==datum.index ?invert('xscale',fix[0]):null"
        },
        {
          "type": "formula",
          "as": "fy",
          "expr": "fix[1]!=null && node==datum.index ?invert('yscale',fix[1]):null"
        }
      ]
    }
  ],
  "scales": [
    {
      "name": "color",
      "type": "ordinal",
      "domain": {"data": "node-data", "field": "group"},
      "range": [
        "#4682b4",
        "#4666b4",
        "#46b494",
        "#b46746",
        "#b44662",
        "#a44fa3"
      ]
    },
    {
      "name": "xscale",
      "zero": false,
      "domain": {"signal": "xdom"},
      "range": {"signal": "xrange"}
    },
    {
      "name": "yscale",
      "zero": false,
      "domain": {"signal": "ydom"},
      "range": {"signal": "yrange"}
    }
  ],
  "marks": [
    {
      "type": "path",
      "name": "links",
      "from": {"data": "link-data"},
      "interactive": false,
      "encode": {
        "update": {
          "stroke": {
            "signal": "datum.source.index!=nodeHover.id && datum.target.index!=nodeHover.id ? '#929399':merge(hsl(scale('color', datum.source.group)), {l:0.64})"
          },
          "strokeWidth": {
            "signal": "datum.source.index!=nodeHover.id && datum.target.index!=nodeHover.id ? 0.5:2"
          }
        }
      },
      "transform": [
        {
          "type": "linkpath",
          "require": {"signal": "force"},
          "shape": "line",
          "sourceX": {"expr": "scale('xscale', datum.datum.source.x)"},
          "sourceY": {"expr": "scale('yscale', datum.datum.source.y)"},
          "targetX": {"expr": "scale('xscale', datum.datum.target.x)"},
          "targetY": {"expr": "scale('yscale', datum.datum.target.y)"}
        },
        {
          "type": "formula",
          "expr": "atan2(datum.datum.target.y - datum.datum.source.y,datum.datum.source.x - datum.datum.target.x)",
          "as": "angle1"
        },
        {
          "type": "formula",
          "expr": "(datum.angle1>=0?datum.angle1:(2*PI + datum.angle1)) * (360 / (2*PI))",
          "as": "angle2"
        },
        {
          "type": "formula",
          "expr": "(360-datum.angle2)*(PI/180)",
          "as": "angle3"
        },
        {
          "type": "formula",
          "expr": "(cos(datum.angle3)*(nodeRadius+5))+(scale('xscale',datum.datum.target.x))",
          "as": "arrowX"
        },
        {
          "type": "formula",
          "expr": "(sin(datum.angle3)*(nodeRadius+5))+(scale('yscale',datum.datum.target.y))",
          "as": "arrowY"
        }
      ]
    },
    {
      "type": "symbol",
      "name": "arrows",
      "zindex": 1,
      "from": {"data": "links"},
      "encode": {
        "update": {
          "shape": {"value": "triangle"},
          "angle": {"signal": "-datum.angle2-90"},
          "x": {"signal": "datum.arrowX"},
          "y": {"signal": "datum.arrowY"},
          "text": {"signal": "'▲'"},
          "fill": {
            "signal": "datum.datum.source.index!=nodeHover.id && datum.datum.target.index!=nodeHover.id ? '#929399':merge(hsl(scale('color', datum.datum.source.group)), {l:0.64})"
          },
          "size": {"signal": "nodeRadius==1?0:60"}
        }
      }
    },
    {
      "name": "nodes",
      "type": "symbol",
      "zindex": 1,
      "from": {"data": "node-data"},
      "encode": {
        "update": {
          "opacity": {"value": 1},
          "fill": {
            "signal": "nodeHover.id===datum.index || indexof(nodeHover.connections, datum.name)>-1 ?scale('color', datum.group):merge(hsl(scale('color', datum.group)), {l:0.64})"
          },
          "stroke": {
            "signal": "nodeHover.id===datum.index || indexof(nodeHover.connections, datum.name)>-1 ?scale('color', datum.group):merge(hsl(scale('color', datum.group)), {l:0.84})"
          },
          "strokeWidth": {"value": 3},
          "strokeOpacity": {"value": 1},
          "size": {"signal": "4 * nodeRadius * nodeRadius"},
          "cursor": {"value": "pointer"},
          "x": {
            "signal": "fix[0]!=null && node===datum.index ?fix[0]:scale('xscale', datum.x)"
          },
          "y": {
            "signal": "fix[1]!=null && node===datum.index ?fix[1]:scale('yscale', datum.y)"
          }
        },
        "hover": {"tooltip": {"signal": "datum.name"}}
      }
    },
    {
      "type": "text",
      "name": "labels",
      "from": {"data": "nodes"},
      "zindex": 2,
      "interactive": false,
      "enter": {},
      "encode": {
        "update": {
          "fill": {"signal": "'white'"},
          "y": {"field": "y"},
          "x": {"field": "x"},
          "text": {"field": "datum.name"},
          "align": {"value": "center"},
          "fontSize": {"value": 10},
          "baseline": {"value": "middle"},
          "limit": {
            "signal": "clamp(sqrt(4 * nodeRadius * nodeRadius)-7,1,1000)"
          },
          "ellipsis": {"value": " "}
        }
      }
    },
    {
      "type": "text",
      "data": [{}],
      "encode": {
        "update": {
          "text": {"value": ["Dataviz: David Bacci"]},
          "align": {"value": "left"},
          "lineHeight": {"value": 16},
          "fill": {"value": "#595959"},
          "x": {"signal": "width - 100"},
          "y": {"signal": "height + 200"},
          "fontSize": {"value": 10}
        }
      }
    }
  ]
}

字符串

相关问题