我正在做一个图形可视化玩具项目。我希望能够给它一个边列表或邻接列表作为输入(在文本区域中),并在SVG元素中显示相应的图形绘图。图描述为边列表,但邻接列表以某种方式为所有节点提供相同的索引。
在过去的几天里,我一直在为一个问题发愁。我给出了同样的图表,当描述为边缘列表时,它工作正常…
,当描述为相邻列表时,它给出:
以下是接受文本输入并将其转换为相邻/边缘列表的部分:
export const getAdjacencyListFromText = (text: string) => {
const tokenizedLines = getLines(text)
.map(line => getTokensFromLine(line).map(token => Number(token)))
.filter(tokens => tokens.length >= 2)
const adjacencyList = tokenizedLines.reduce<AdjacencyList>((acc: any, current: any) => {
const source = current[0]
const neighbors = current.slice(1)
acc[source] = neighbors
return acc
}, [])
return adjacencyList
}
export const getEdgeListFromText = (text: string) =>
getLines(text)
.map(line => getTokensFromLine(line).map(e => Number(e)))
.filter(lineTokens => lineTokens.length >= 2)
.map(tokens => {
const source = tokens[0]
const target = tokens[1]
const weight = tokens[2]
if (tokens.length === 2) return { source, target }
else if (tokens.length === 3) return { source, target, weight }
}) as EdgeList
创建图形对象的部分:
export const getGraphFromAdjacencyList = (adjacencyList: AdjacencyList) => {
const nodes = adjacencyList.reduce<G.Node>((acc: G.Node[], current: AdjacencyListEntry, index: number) => {
current.forEach(node => acc.push({ id: node }))
if (!acc.some(el => el.id === index)) acc.push({ id: index })
return acc
}, []) as G.Node[]
const links = nodes.reduce<G.Link>((acc: G.Link[], currentNode: G.Node) => {
const neighbors = adjacencyList[currentNode.id]
if (neighbors === undefined) return acc
const newLinks = neighbors
.map(neighborId => ({ source: currentNode.id, target: neighborId }))
.filter(link => !acc.some(l => l.source === link.source && l.target === link.target))
return acc = acc.concat(newLinks)
}, [])
return { nodes, links }
}
export const getGraphFromEdgeList = (edgeList: EdgeList) => {
const nodes = edgeList.reduce<G.Node>((acc: G.Node[], currentEdge) => {
const { source, target } = currentEdge
if (!acc.some(n => n.id === source)) {
acc.push({ id: source })
}
if (!acc.some(n => n.id === target)) {
acc.push({ id: target })
}
return acc
}, [])
return {
links: edgeList,
nodes,
}
}
我把图表给d3的那部分:
componentDidUpdate() {
const { links: edgeData, nodes: nodeData } = this.props.graph
// newNodes and newLinks is done so that old nodes and links keep their position
// goal is better visuals
const previousNodes = this.simulation.nodes()
const newNodes = nodeData.map(node => {
const existingNode = _(previousNodes).find((n: any) => n.id === node.id)
if (existingNode !== undefined) {
return existingNode
} else {
return node
}
})
const previousLinks = this.simulation.force("link").links()
const newLinks = edgeData.map(edge => {
const existingLink = _(previousLinks).find((l: any) => l.source.id === edge.source && l.target.id === edge.target)
if (existingLink !== undefined) {
return existingLink
} else {
return edge
}
})
const line = this.edgeLayer.selectAll("g").data(newLinks)
const node = this.nodeLayer.selectAll("g").data(newNodes)
node.exit().remove()
line.exit().remove()
this.node = this.createNode(node)
this.edge = this.createEdge(line)
this.simulation = this.simulation.nodes(newNodes)
this.simulation.force("link").links(newLinks).id(d => d.id)
this.simulation.on("tick", this.simulationTick)
this.simulation.alphaTarget(1)
this.simulation.restart()
}
图形对象来自
邻接表
是这样的:
{
nodes: [ { id: 2 }, { id: 1 }, { id: 3 } ],
links: [ { source: 2, target: 3 }, { source: 1, target: 2 } ] }
}
图形对象来自
边缘列表
是这样的:
{
nodes: [ { id: 1 }, { id: 2 }, { id: 3 } ],
links: [ { source: 1, target: 2 }, { source: 1, target: 3 } ]
}
唯一的区别是
节点数组中节点的顺序
.
因此,在为我的边缘列表和邻接列表实用程序函数编写了一个下午的测试用例之后,我决定在将节点交给d3之前对它们进行排序……它工作了……
为什么点菜很重要?
我查看了文档,没有(到目前为止我看到的)提到任何与节点顺序相关的重要性。我很困惑。