如何使用Vega/Vega-lite绘制类、组织、流程或状态图表?

9

我找不到使用Vega制作的状态/类/流程/组织图表的示例。有吗?

感觉Vega非常适合这个(虽然有点过于强大),但是没有起点示例,学习曲线相当陡峭。"How Vega Works"页面上有一些示例,但没有链接说明如何构建它们。

how vega works chart

还有一个树形布局的例子,但是不清楚如何开始将其转换为适合流程图样式的块。

enter image description here

这里有一些期望的输出样例(还包括其他形状,如菱形/三角形),来自于例如mermaid.js的内容。

class diagram


我不清楚你在问什么。也许这篇文章可以帮到你 https://medium.com/@pbesh/react-and-vega-an-alternative-visualization-example-cd76e07dc1cd - sancho.s ReinstateMonicaCellio
3个回答

7
假设您能够将您的图表表示如下:
"values": [
        {"id": "1", "parent": null, "title": "Animal"},
        {"id": "2", "parent": "1", "title": "Duck"},
        {"id": "3", "parent": "1", "title": "Fish"},
        {"id": "4", "parent": "1", "title": "Zebra"}
      ]

你可以将节点按照树形结构排列(stratify 可以完成此任务):
"transform": [
        {
          "type": "stratify",
          "key": "id",
          "parentKey": "parent"
        },
        {
          "type": "tree",
          "method": "tidy",
          "separation": true,
          "size": [{"signal": "width"}, {"signal": "height"}]
        }
      ]

在摆放节点之后,您需要生成连接线条,treelinks + linkpath 组合正是这样做的:

{
      "name": "links",
      "source": "tree", // take datasource "tree" as input
      "transform": [
        { "type": "treelinks" }, // apply transform 1
        { "type": "linkpath", // follow up with next transform
          "shape": "diagonal"
          }
      ]
    }

现在你已经拥有了数据源,想要绘制实际的对象。在 Vega 中,这些被称为 marks。我猜这就是我要偏离你所期望的输出的地方,因为我只会为每个数据点绘制一个带标题的矩形和一些基本线条进行连接:

"marks": [
    {
      "type": "path",
      "from": {"data": "links"}, // dataset we defined above
      "encode": {
        "enter": {
          "path": {"field": "path"} // linkpath generated a dataset with "path" field in it - we just grab it here
        }
      }
    },
    {
      "type": "rect",
      "from": {"data": "tree"},
      "encode": {
        "enter": {
          "stroke": {"value": "black"},
          "width": {"value": 100},
          "height": {"value": 20},
          "x": {"field": "x"},
          "y": {"field": "y"}
        }
      }
    },
    {
      "type": "text",
      "from": {"data": "tree"}, // use data set we defined earlier
      "encode": {
        "enter": {
          "stroke": {"value": "black"},
          "text": {"field": "title"}, // we can use data fields to display actual values
          "x": {"field": "x"}, // use data fields to draw values from
          "y": {"field": "y"},
          "dx": {"value":50}, // offset the mark to appear in rectangle center
          "dy": {"value":13},
          "align": {"value": "center"}
        }
      }
    }
  ]

总的来说,我已经得出了一个非常基本的近似你的目标状态。这绝对不是一个精确的匹配:那里的矩形应该用替换,并且连接路径也需要一些工作。您会注意到我没有使用任何信号来提供动态用户输入和更新/退出/悬停指令 - 再次因为简单。

{
  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "width": 800,
  "height": 300,
  "padding": 5,

  "data": [
    {
      "name": "tree",
      "values": [
        {"id": "1", "parent": null, "title": "Animal"},
        {"id": "2", "parent": "1", "title": "Duck"},
        {"id": "3", "parent": "1", "title": "Fish"},
        {"id": "4", "parent": "1", "title": "Zebra"}
      ],
      "transform": [
        {
          "type": "stratify",
          "key": "id",
          "parentKey": "parent"
        },
        {
          "type": "tree",
          "method": "tidy",
          "separation": true,
          "size": [{"signal": "width"}, {"signal": "height"}]
        }
      ]      
    },
    {
      "name": "links",
      "source": "tree",
      "transform": [
        { "type": "treelinks" },
        { "type": "linkpath",
          "shape": "diagonal"
          }
      ]
    }, 
    {
      "name": "tree-boxes",
      "source": "tree",
      "transform": [
          { 
            "type": "filter",
            "expr": "datum.parent == null"
          }
        ]
    },
    {
      "name": "tree-circles",
      "source": "tree",
      "transform": [
        {
          "type": "filter",
          "expr": "datum.parent != null"
        }
      ]
    }
  ],
  "marks": [
    {
      "type": "path",
      "from": {"data": "links"},
      "encode": {
        "enter": {
          "path": {"field": "path"}
        }
      }
    },
    {
      "type": "rect",
      "from": {"data": "tree-boxes"},
      "encode": {
        "enter": {
          "stroke": {"value": "black"},
          "width": {"value": 100},
          "height": {"value": 20},
          "x": {"field": "x"},
          "y": {"field": "y"}
        }
      }
    },
    {
      "type": "symbol",
      "from": {"data": "tree-circles"},
      "encode": {
        "enter": {
          "stroke": {"value": "black"},
          "width": {"value": 100},
          "height": {"value": 20},
          "x": {"field": "x"},
          "y": {"field": "y"}
        }
      }
    },
    {
      "type": "rect",
      "from": {"data": "tree"},
      "encode": {
        "enter": {
          "stroke": {"value": "black"},
          "width": {"value": 100},
          "height": {"value": 20},
          "x": {"field": "x"},
          "y": {"field": "y"}
        }
      }
    },
    {
      "type": "text",
      "from": {"data": "tree"},
      "encode": {
        "enter": {
          "stroke": {"value": "black"},
          "text": {"field": "title"},
          "x": {"field": "x"},
          "y": {"field": "y"},
          "dx": {"value":50},
          "dy": {"value":13},
          "align": {"value": "center"}
        }
      }
    }
  ]
}
更新: 假设您想为图表的根节点和叶节点呈现不同的形状。 实现此目的的一种方法是,在您的tree数据集上添加两个基于filter转换,并相应地对它们进行过滤:
    {
      "name": "tree-boxes",
      "source": "tree", // grab the existing data
      "transform": [
          { 
            "type": "filter",
            "expr": "datum.parent == null" // run it through a filter defined by expression
          }
        ]
    },
    {
      "name": "tree-circles",
      "source": "tree",
      "transform": [
        {
          "type": "filter",
          "expr": "datum.parent != null"
        }
      ]
    }

那么,与其将所有标记渲染为 rect,您需要为各自转换的数据集使用两种不同的形状:

{
      "type": "rect",
      "from": {"data": "tree-boxes"},
      "encode": {
        "enter": {
          "stroke": {"value": "black"},
          "width": {"value": 100},
          "height": {"value": 20},
          "x": {"field": "x"},
          "y": {"field": "y"}
        }
      }
    },
    {
      "type": "symbol",
      "from": {"data": "tree-circles"},
      "encode": {
        "enter": {
          "stroke": {"value": "black"},
          "width": {"value": 100},
          "height": {"value": 20},
          "x": {"field": "x"},
          "y": {"field": "y"}
        }
      }
    }

谢谢Timur,这非常有帮助。我们想要以编程方式生成节点在正确位置的布局,你肯定让我走上了正确的轨道。我不确定你所说的WYSIWYG是什么意思,那不是问题(或赏金)的一部分 - 所需的只是可视化,而编辑可能会对我们的用例造成损害。节点布局仍然存在一些挑战,但这些可能很简单。 - Brian M. Hunt
好的,我会将所述的WYSIWYG部分删除,以便答案更加专注。 - timur
谢谢@timur,这很有帮助。我会保持这个问题开放,给其他人一个机会来获得赏金,但如果没有更好的答案出现,我会授予这个答案赏金。 - Brian M. Hunt

3
您可以参考这个解决方案-使用树,其中包括:
步骤1-从表格数据中提取节点
步骤2-从分层节点数据中提取链接
步骤3-如何将它们结合起来
步骤4-添加标签
步骤5-添加颜色

1
谢谢。这很有用,但是除了分解树之外,它并没有完全回答问题。 - Brian M. Hunt

2

我已经建立了一个示例,它是迄今为止最接近本问题所描述的内容。我的解决方案基于这里被接受的答案,感谢@timur。

Click here to view it in the Vega Editor.

它将树节点显示为具有多个文本的组。它支持展开和折叠节点以及在水平和垂直布局之间切换(您可以通过设置horizontal信号的默认值来控制)。然而,我遇到了一些限制:
  • 在水平和垂直布局之间切换时,不能正确地重新呈现所有标记(在此处提出了问题here)。仅当您在代码中手动更改horizontal信号的默认值时才有效。
  • 我找不到一种适当的方法来折叠树的根节点,而无需手动折叠嵌套节点(在此处发布了相关问题here)。
无论如何,对于任何寻找使用Vega构建组织图表类可视化的人来说,这都应该是有用的 - 没有更接近的示例,我不得不花费很多时间来弄清楚所有注意事项并解决几乎所有问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接