如何解析来自 SciKitLearn 凝聚聚类的数据

How to Parse Data from SciKitLearn Agglomerative Clustering

我觉得有人问过这个问题的版本非常相似,在尝试解决这个问题的过程中我学到了很多东西,但是我缺少一些(可能是非常基本的)概念。

这里有三个非常相似的question/answers非常好的:

How do you visualize a ward tree from sklearn.cluster.ward_tree?

Mike Bostock 的 D3 上有一些很棒的东西 Git:Mike Bostock's D3 Git Repo

现在说说我的具体情况:

我在 Python 中使用 sklearn Agglomerative Clustering 做了一些分析。我正在生成我想在 MatplotLib 中看到的树状图:

T=7 T=7 Dendrogram

T=2 T=2 Dendrogram

现在我想将这些树状图和一些其他功能添加到网站。我已经使用 Django 建立了一个网站。

我已经使用了一些 D3 Javascript 功能来实现像这样的动态和交互式树图:

https://bl.ocks.org/d3noob/8375092

我已经做到了,所以它从 json 文件加载每个分支的信息。所以它既是动态的又是互动的。 Interactive Tree Diagram

现在我想使用凝聚聚类中的信息来模拟其中的一些功能。

我想:

  1. 做一个类似于MatplotLib的树状图,并制作它 交互,这样应该有一个滑块允许用户 改变T值,树状图应该重新绘制。

    一个。我愿意接受任何建议。我可以通过简单地重新计算 Python 中的树状图(作为内部的模块 Django 应用程序),保存图像并将该图像加载到 javascript 模板中的一侧。我认为可能还有更多 D3 的优雅解决方案,但我 运行 没有时间去做 研究。

  2. 使用来自聚类的信息创建交互式树图。 我希望看到树状图作为一棵树更具交互性。看来我应该可以使用 agglo_model.children_ 或 linkage_matrix.

agglo_model.children_: 
[[ 35  36][ 13  18][ 19  20]...[ 22  69][ 33  34][ 14  32]]

or the linkage_matrix:
linkage_matrix: 
[[ 35.          36.           0.           2.        ]
 [ 22.          69.           1.73205081   4.        ]
...
 [ 50.          57.           4.47213595   2.        ]
 [  9.          41.           4.69041576   2.        ]
...
 [116.         126.          12.62713128  36.        ]
 [128.         129.          17.97652791  66.        ]]

我缺少的关键部分是如何从 scikit 转到 d3.js

的以下树格式
    var treeData = [
  {
    "name": "Top Level",
    "parent": "null",
    "children": [
      {
        "name": "Level 2: A",
        "parent": "Top Level",
        "children": [
          {
            "name": "Son of A",
            "parent": "Level 2: A"
          },
          {
            "name": "Daughter of A",
            "parent": "Level 2: A"
          }
        ]
      },
      {
        "name": "Level 2: B",
        "parent": "Top Level"
      }
    ]
  }
];
  1. 在网页上显示聚类图。基本上我想模仿这个页面:Agglomerative Clustering and MatplotLib Diagrams and Dendrograms 和交互式 javascript.

再一次 - 任何建议表示赞赏。

这个问题有很多,但我认为它归结为如何将数据从链接矩阵获取到 d3 树图?

鉴于此 python 代码:

from scipy.cluster.hierarchy import dendrogram, linkage
import numpy as np
from matplotlib import pyplot as plt
   
X = np.array([[5,3],
    [10,15],
    [15,12],
    [24,10],
    [30,30],
    [85,70],
    [71,80],
    [60,78],
    [70,55],
    [80,91],])

linked = linkage(X, "single")

labelList = range(0, 10)

plt.figure(figsize=(10, 7))
dendrogram(linked,
            orientation='top',
            labels=labelList,
            distance_sort='descending',
            show_leaf_counts=False)
plt.show()

生成这个链接矩阵:

[[ 1.          2.          5.83095189  2.        ]
 [ 3.         10.          9.21954446  3.        ]
 [ 6.          7.         11.18033989  2.        ]
 [ 0.         11.         13.          4.        ]
 [ 9.         12.         14.2126704   3.        ]
 [ 5.         14.         17.20465053  4.        ]
 [ 4.         13.         20.88061302  5.        ]
 [ 8.         15.         21.21320344  5.        ]
 [16.         17.         47.16990566 10.        ]]

这个树状图:

然后我们如何将链接矩阵转换为与 d3 树兼容的数据结构?现在我花了一点时间来理解矩阵的结构以及它是如何传达层次结构的。这个 and this post真是解释的很好。所以,让我们在 Python 中做一些数据操作,然后 JSON-ify 出来。如果您使用的是 Django,则可以通过 API 调用 return 此 JSON。

import json

def create_tree(linked):
    ## inner func to recurvise-ly walk the linkage matrix
    def recurTree(tree):
        k = tree['name']
        ## no children for this node
        if k not in inter:
            return
        for n in inter[k]:
            ## build child nodes
            node = {
                "name": n,
                "parent": k,
                "children": []
            }
            ## add to children
            tree['children'].append(node)
            ## next recursion
            recurTree(node)      
    
    num_rows, _ = linked.shape
    inter = {}
    i = 0
    // loop linked matrix convert to dictionary
    for row in linked:
        i += 1
        inter[float(i + num_rows)] = [row[0],row[1]]

    // start tree data structure
    tree = {
        "name": float(i + num_rows),
        "parent": None,
        "children": []
    }
    // start recursion
    recurTree(tree);
    return tree

print(json.dumps(create_tree(linked), indent = 2))

这会产生 JSON,如下所示:

{
  "name": 18.0,
  "parent": null,
  "children": [
    {
      "name": 16.0,
      "parent": 18.0,
      "children": [
        {
          "name": 4.0,
          "parent": 16.0,
          "children": []
        },
        {
          "name": 13.0,
          "parent": 16.0,
          "children": [
            {
              "name": 0.0,
              "parent": 13.0,
              "children": []
            },
            {
              "name": 11.0,
              "parent": 13.0,
              "children": [
                {
                  "name": 3.0,
                  "parent": 11.0,
                  "children": []
                },
                {
                  "name": 10.0,
                  "parent": 11.0,
                  "children": [
                    {
                      "name": 1.0,
                      "parent": 10.0,
                      "children": []
                    },
                    {
                      "name": 2.0,
                      "parent": 10.0,
                      "children": []
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    },
    {
      "name": 17.0,
      "parent": 18.0,
      "children": [
        {
          "name": 8.0,
          "parent": 17.0,
          "children": []
        },
        {
          "name": 15.0,
          "parent": 17.0,
          "children": [
            {
              "name": 5.0,
              "parent": 15.0,
              "children": []
            },
            {
              "name": 14.0,
              "parent": 15.0,
              "children": [
                {
                  "name": 9.0,
                  "parent": 14.0,
                  "children": []
                },
                {
                  "name": 12.0,
                  "parent": 14.0,
                  "children": [
                    {
                      "name": 6.0,
                      "parent": 12.0,
                      "children": []
                    },
                    {
                      "name": 7.0,
                      "parent": 12.0,
                      "children": []
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

如果我们随后将其转储到 d3 树示例中(我将其设为垂直),您将得到以下结果:

运行 代码 d3:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />

    <title>Tree Example</title>

    <style>
      .node {
        cursor: pointer;
      }

      .node circle {
        fill: #fff;
        stroke: steelblue;
        stroke-width: 3px;
      }

      .node text {
        font: 12px sans-serif;
      }

      .link {
        fill: none;
        stroke: #ccc;
        stroke-width: 2px;
      }
    </style>
  </head>

  <body>
    <!-- load the d3.js library -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>

    <script>
      var treeData = {
        name: 18.0,
        parent: null,
        children: [
          {
            name: 16.0,
            parent: 18.0,
            children: [
              {
                name: 4.0,
                parent: 16.0,
                children: [],
              },
              {
                name: 13.0,
                parent: 16.0,
                children: [
                  {
                    name: 0.0,
                    parent: 13.0,
                    children: [],
                  },
                  {
                    name: 11.0,
                    parent: 13.0,
                    children: [
                      {
                        name: 3.0,
                        parent: 11.0,
                        children: [],
                      },
                      {
                        name: 10.0,
                        parent: 11.0,
                        children: [
                          {
                            name: 1.0,
                            parent: 10.0,
                            children: [],
                          },
                          {
                            name: 2.0,
                            parent: 10.0,
                            children: [],
                          },
                        ],
                      },
                    ],
                  },
                ],
              },
            ],
          },
          {
            name: 17.0,
            parent: 18.0,
            children: [
              {
                name: 8.0,
                parent: 17.0,
                children: [],
              },
              {
                name: 15.0,
                parent: 17.0,
                children: [
                  {
                    name: 5.0,
                    parent: 15.0,
                    children: [],
                  },
                  {
                    name: 14.0,
                    parent: 15.0,
                    children: [
                      {
                        name: 9.0,
                        parent: 14.0,
                        children: [],
                      },
                      {
                        name: 12.0,
                        parent: 14.0,
                        children: [
                          {
                            name: 6.0,
                            parent: 12.0,
                            children: [],
                          },
                          {
                            name: 7.0,
                            parent: 12.0,
                            children: [],
                          },
                        ],
                      },
                    ],
                  },
                ],
              },
            ],
          },
        ],
      };

      // ************** Generate the tree diagram    *****************
      var margin = { top: 40, right: 120, bottom: 20, left: 120 },
        width = 960 - margin.right - margin.left,
        height = 600 - margin.top - margin.bottom;

      var i = 0;

      var tree = d3.layout.tree().size([height, width]);

      var diagonal = d3.svg.diagonal().projection(function (d) {
        return [d.x, d.y];
      });

      var svg = d3
        .select('body')
        .append('svg')
        .attr('width', width + margin.right + margin.left)
        .attr('height', height + margin.top + margin.bottom)
        .append('g')
        .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

      root = treeData;

      update(root);

      function update(source) {
        // Compute the new tree layout.
        var nodes = tree.nodes(root).reverse(),
          links = tree.links(nodes);

        // Normalize for fixed-depth.
        nodes.forEach(function (d) {
          d.y = d.depth * 100;
        });

        // Declare the nodes…
        var node = svg.selectAll('g.node').data(nodes, function (d) {
          return d.id || (d.id = ++i);
        });

        // Enter the nodes.
        var nodeEnter = node
          .enter()
          .append('g')
          .attr('class', 'node')
          .attr('transform', function (d) {
            return 'translate(' + d.x + ',' + d.y + ')';
          });

        nodeEnter.append('circle').attr('r', 10).style('fill', '#fff');

        nodeEnter
          .append('text')
          .attr('y', function (d) {
            return d.children || d._children ? -18 : 18;
          })
          .attr('dy', '.35em')
          .attr('text-anchor', 'middle')
          .text(function (d) {
            return d.name;
          })
          .style('fill-opacity', 1);

        // Declare the links…
        var link = svg.selectAll('path.link').data(links, function (d) {
          return d.target.id;
        });

        // Enter the links.
        link
          .enter()
          .insert('path', 'g')
          .attr('class', 'link')
          .attr('d', diagonal);
      }
    </script>
  </body>
</html>