Search

Clusters

Analysis

Find communities in your network.

Clusters
View live example →

This demo demonstrates how the clusters function of KeyLines can be used to find communities in a graph. By moving the slider, it is possible to change the sensitivity of clusters. Higher values create a larger number of smaller clusters, while lower values produce fewer, larger clusters. In this demo we assign each cluster a random colour.

Note also this demo uses the 'Lens' layout - see Layouts.

The data used in this demo shows companies and their relationship to other companies by ownership. The dataset authors are Kim Norlen, Gabriel Lucas, Mike Gebbie and John Chuang. EVA: Extraction, Visualization and Analysis of the Telecommunications and Media Ownership Network. Proceedings of the International Telecommunications Society's 14th Biennial Conference, Seoul Korea, August 2002.

Key functions used:

import KeyLines from "keylines";
import { data } from "./data.js";

let chart;

const clustersSlider = document.getElementById("clustersSlider");

// Generate a random hex colour
function randomColour() {
  return `#${`${Math.random().toString(16)}0000000`.slice(2, 8)}`;
}

// Generate a new colour for each cluster:
// for each colour created save it inside an array:
// this works well with the stability factor of the cluster algo
const coloursList = [];

function getColour(i) {
  // Cache a colour list and generate a new colour if not already present.
  if (!coloursList[i]) {
    coloursList[i] = randomColour();
  }
  return coloursList[i];
}

function runClusters() {
  // Don't bother running unless the value has changed
  const newValue = clustersSlider.value;

  // Update the label
  document.getElementById("clustersVal").innerText = newValue;

  // Get the clusters - a 2d array of nodes
  const result = chart.graph().clusters({
    factor: newValue,
    consistent: true,
  });
  // Get the changes - the first array is of groups, with the contents being the nodes in each
  // group. As we're looking for changes, we use flatMap to go through all groups and merge
  // them into a single array.
  const changes = [];
  result.forEach((inner, k1) => {
    inner.forEach((id) => {
      changes.push({ id, c: getColour(k1) });
    });
  });
  // Apply the changes
  chart.setProperties(changes);
}

async function startKeyLines() {
  const options = {
    logo: { u: "/public/images/Logo.png" },
    linkEnds: { avoidLabels: false },
    handMode: true,
  };

  chart = await KeyLines.create({
    container: "klchart",
    options,
  });

  chart.load(data);

  clustersSlider.addEventListener("input", runClusters);

  chart.zoom("fit");
  runClusters();
}

window.addEventListener("DOMContentLoaded", startKeyLines);
export const data = {
  type: "LinkChart",
  items: [
    {
      type: "link",
      id: "link0",
      id1: "node1",
      id2: "node37",
    },
    {
      type: "link",
      id: "link1",
      id1: "node1",
      id2: "node38",
    },
    {
      type: "link",
      id: "link2",
      id1: "node1",
      id2: "node39",
    },
    {
      type: "link",
      id: "link3",
      id1: "node1",
      id2: "node40",
    },
    {
      type: "link",
      id: "link4",
      id1: "node1",
      id2: "node41",
    },
    {
      type: "link",
      id: "link5",
      id1: "node1",
      id2: "node42",
    },
    {
      type: "link",
      id: "link6",
      id1: "node1",
      id2: "node43",
    },
    {
      type: "link",
      id: "link7",
      id1: "node1",
      id2: "node44",
    },
    {
      type: "link",
      id: "link8",
      id1: "node1",
      id2: "node45",
    },
    {
      type: "link",
      id: "link9",
      id1: "node1",
      id2: "node46",
    },
    {
      type: "link",
      id: "link10",
      id1: "node46",
      id2: "node47",
    },
    {
      type: "link",
      id: "link11",
      id1: "node1",
      id2: "node48",
    },
    {
      type: "link",
      id: "link12",
      id1: "node1",
      id2: "node49",
    },
    {
      type: "link",
      id: "link13",
      id1: "node1",
      id2: "node50",
    },
    {
      type: "link",
      id: "link14",
      id1: "node1",
      id2: "node51",
    },
    {
      type: "link",
      id: "link15",
      id1: "node1",
      id2: "node52",
    },
    {
      type: "link",
      id: "link16",
      id1: "node1",
      id2: "node53",
    },
    {
      type: "link",
      id: "link17",
      id1: "node53",
      id2: "node54",
    },
    {
      type: "link",
      id: "link18",
      id1: "node53",
      id2: "node55",
    },
    {
      type: "link",
      id: "link19",
      id1: "node53",
      id2: "node56",
    },
    {
      type: "link",
      id: "link20",
      id1: "node53",
      id2: "node57",
    },
    {
      type: "link",
      id: "link21",
      id1: "node53",
      id2: "node58",
    },
    {
      type: "link",
      id: "link22",
      id1: "node53",
      id2: "node59",
    },
    {
      type: "link",
      id: "link23",
      id1: "node53",
      id2: "node60",
    },
    {
      type: "link",
      id: "link24",
      id1: "node53",
      id2: "node61",
    },
    {
      type: "link",
      id: "link25",
      id1: "node53",
      id2: "node62",
    },
    {
      type: "link",
      id: "link26",
      id1: "node1",
      id2: "node63",
    },
    {
      type: "link",
      id: "link27",
      id1: "node1",
      id2: "node64",
    },
    {
      type: "link",
      id: "link28",
      id1: "node64",
      id2: "node65",
    },
    {
      type: "link",
      id: "link29",
      id1: "node64",
      id2: "node66",
    },
    {
      type: "link",
      id: "link30",
      id1: "node64",
      id2: "node67",
    },
    {
      type: "link",
      id: "link31",
      id1: "node64",
      id2: "node68",
    },
    {
      type: "link",
      id: "link32",
      id1: "node64",
      id2: "node69",
    },
    {
      type: "link",
      id: "link33",
      id1: "node64",
      id2: "node70",
    },
    {
      type: "link",
      id: "link34",
      id1: "node64",
      id2: "node71",
    },
    {
      type: "link",
      id: "link35",
      id1: "node64",
      id2: "node73",
    },
    {
      type: "link",
      id: "link36",
      id1: "node64",
      id2: "node74",
    },
    {
      type: "link",
      id: "link37",
      id1: "node64",
      id2: "node75",
    },
    {
      type: "link",
      id: "link38",
      id1: "node64",
      id2: "node76",
    },
    {
      type: "link",
      id: "link39",
      id1: "node64",
      id2: "node77",
    },
    {
      type: "link",
      id: "link40",
      id1: "node64",
      id2: "node78",
    },
    {
      type: "link",
      id: "link41",
      id1: "node64",
      id2: "node79",
    },
    {
      type: "link",
      id: "link42",
      id1: "node64",
      id2: "node80",
    },
    {
      type: "link",
      id: "link43",
      id1: "node64",
      id2: "node81",
    },
    {
      type: "link",
      id: "link44",
      id1: "node64",
      id2: "node82",
    },
    {
      type: "link",
      id: "link45",
      id1: "node64",
      id2: "node83",
    },
    {
      type: "link",
      id: "link46",
      id1: "node64",
      id2: "node84",
    },
    {
      type: "link",
      id: "link47",
      id1: "node64",
      id2: "node85",
    },
    {
      type: "link",
      id: "link48",
      id1: "node85",
      id2: "node86",
    },
    {
      type: "link",
      id: "link49",
      id1: "node64",
      id2: "node3864",
    },
    {
      type: "link",
      id: "link50",
      id1: "node64",
      id2: "node87",
    },
    {
      type: "link",
      id: "link51",
      id1: "node1",
      id2: "node88",
    },
    {
      type: "link",
      id: "link52",
      id1: "node88",
      id2: "node89",
    },
    {
      type: "link",
      id: "link53",
      id1: "node88",
      id2: "node90",
    },
    {
      type: "link",
      id: "link54",
      id1: "node88",
      id2: "node72",
    },
    {
      type: "link",
      id: "link55",
      id1: "node88",
      id2: "node91",
    },
    {
      type: "link",
      id: "link56",
      id1: "node88",
      id2: "node3870",
    },
    {
      type: "link",
      id: "link57",
      id1: "node88",
      id2: "node92",
    },
    {
      type: "link",
      id: "link58",
      id1: "node88",
      id2: "node93",
    },
    {
      type: "link",
      id: "link59",
      id1: "node88",
      id2: "node94",
    },
    {
      type: "link",
      id: "link60",
      id1: "node88",
      id2: "node95",
    },
    {
      type: "link",
      id: "link61",
      id1: "node88",
      id2: "node96",
    },
    {
      type: "link",
      id: "link62",
      id1: "node88",
      id2: "node97",
    },
    {
      type: "link",
      id: "link63",
      id1: "node88",
      id2: "node98",
    },
    {
      type: "link",
      id: "link64",
      id1: "node88",
      id2: "node99",
    },
    {
      type: "link",
      id: "link65",
      id1: "node88",
      id2: "node100",
    },
    {
      type: "link",
      id: "link66",
      id1: "node1",
      id2: "node177",
    },
    {
      type: "link",
      id: "link67",
      id1: "node1",
      id2: "node102",
    },
    {
      type: "link",
      id: "link68",
      id1: "node1",
      id2: "node103",
    },
    {
      type: "link",
      id: "link69",
      id1: "node1",
      id2: "node104",
    },
    {
      type: "link",
      id: "link70",
      id1: "node1",
      id2: "node3850",
    },
    {
      type: "link",
      id: "link71",
      id1: "node3850",
      id2: "node3851",
    },
    {
      type: "link",
      id: "link72",
      id1: "node3850",
      id2: "node3852",
    },
    {
      type: "link",
      id: "link73",
      id1: "node3850",
      id2: "node3853",
    },
    {
      type: "link",
      id: "link74",
      id1: "node3850",
      id2: "node3854",
    },
    {
      type: "link",
      id: "link75",
      id1: "node3850",
      id2: "node3855",
    },
    {
      type: "link",
      id: "link76",
      id1: "node3850",
      id2: "node3856",
    },
    {
      type: "link",
      id: "link77",
      id1: "node3850",
      id2: "node3857",
    },
    {
      type: "link",
      id: "link78",
      id1: "node3850",
      id2: "node3858",
    },
    {
      type: "link",
      id: "link79",
      id1: "node3850",
      id2: "node3859",
    },
    {
      type: "link",
      id: "link80",
      id1: "node1",
      id2: "node3860",
    },
    {
      type: "link",
      id: "link81",
      id1: "node3860",
      id2: "node3849",
    },
    {
      type: "link",
      id: "link82",
      id1: "node3860",
      id2: "node103",
    },
    {
      type: "link",
      id: "link83",
      id1: "node3860",
      id2: "node3862",
    },
    {
      type: "link",
      id: "link84",
      id1: "node3860",
      id2: "node3863",
    },
    {
      type: "link",
      id: "link85",
      id1: "node1",
      id2: "node3865",
    },
    {
      type: "link",
      id: "link86",
      id1: "node1",
      id2: "node3867",
    },
    {
      type: "link",
      id: "link87",
      id1: "node1",
      id2: "node3868",
    },
    {
      type: "link",
      id: "link88",
      id1: "node1",
      id2: "node3869",
    },
    {
      type: "link",
      id: "link89",
      id1: "node1",
      id2: "node3871",
    },
    {
      type: "link",
      id: "link90",
      id1: "node3872",
      id2: "node107",
    },
    {
      type: "link",
      id: "link91",
      id1: "node3872",
      id2: "node108",
    },
    {
      type: "link",
      id: "link92",
      id1: "node3872",
      id2: "node109",
    },
    {
      type: "link",
      id: "link93",
      id1: "node3872",
      id2: "node110",
    },
    {
      type: "link",
      id: "link94",
      id1: "node3872",
      id2: "node111",
    },
    {
      type: "link",
      id: "link95",
      id1: "node3872",
      id2: "node112",
    },
    {
      type: "link",
      id: "link96",
      id1: "node3872",
      id2: "node113",
    },
    {
      type: "link",
      id: "link97",
      id1: "node3872",
      id2: "node114",
    },
    {
      type: "link",
      id: "link98",
      id1: "node3872",
      id2: "node115",
    },
    {
      type: "link",
      id: "link99",
      id1: "node3872",
      id2: "node116",
    },
    {
      type: "link",
      id: "link100",
      id1: "node3872",
      id2: "node117",
    },
    {
      type: "link",
      id: "link101",
      id1: "node3872",
      id2: "node118",
    },
    {
      type: "link",
      id: "link102",
      id1: "node3872",
      id2: "node119",
    },
    {
      type: "link",
      id: "link103",
      id1: "node3872",
      id2: "node120",
    },
    {
      type: "link",
      id: "link104",
      id1: "node3872",
      id2: "node121",
    },
    {
      type: "link",
      id: "link105",
      id1: "node3872",
      id2: "node122",
    },
    {
      type: "link",
      id: "link106",
      id1: "node3872",
      id2: "node123",
    },
    {
      type: "link",
      id: "link107",
      id1: "node3872",
      id2: "node3875",
    },
    {
      type: "link",
      id: "link108",
      id1: "node3872",
      id2: "node125",
    },
    {
      type: "link",
      id: "link109",
      id1: "node3872",
      id2: "node3876",
    },
    {
      type: "link",
      id: "link110",
      id1: "node3872",
      id2: "node127",
    },
    {
      type: "link",
      id: "link111",
      id1: "node3872",
      id2: "node128",
    },
    {
      type: "link",
      id: "link112",
      id1: "node3872",
      id2: "node3877",
    },
    {
      type: "link",
      id: "link113",
      id1: "node3872",
      id2: "node3878",
    },
    {
      type: "link",
      id: "link114",
      id1: "node3872",
      id2: "node129",
    },
    {
      type: "link",
      id: "link115",
      id1: "node129",
      id2: "node130",
    },
    {
      type: "link",
      id: "link116",
      id1: "node129",
      id2: "node131",
    },
    {
      type: "link",
      id: "link117",
      id1: "node3872",
      id2: "node132",
    },
    {
      type: "link",
      id: "link118",
      id1: "node3872",
      id2: "node133",
    },
    {
      type: "link",
      id: "link119",
      id1: "node3872",
      id2: "node134",
    },
    {
      type: "link",
      id: "link120",
      id1: "node3872",
      id2: "node135",
    },
    {
      type: "link",
      id: "link121",
      id1: "node3872",
      id2: "node136",
    },
    {
      type: "link",
      id: "link122",
      id1: "node3872",
      id2: "node137",
    },
    {
      type: "link",
      id: "link123",
      id1: "node3872",
      id2: "node138",
    },
    {
      type: "link",
      id: "link124",
      id1: "node3872",
      id2: "node139",
    },
    {
      type: "link",
      id: "link125",
      id1: "node139",
      id2: "node140",
    },
    {
      type: "link",
      id: "link126",
      id1: "node139",
      id2: "node141",
    },
    {
      type: "link",
      id: "link127",
      id1: "node139",
      id2: "node142",
    },
    {
      type: "link",
      id: "link128",
      id1: "node3872",
      id2: "node143",
    },
    {
      type: "link",
      id: "link129",
      id1: "node4",
      id2: "node3879",
    },
    {
      type: "link",
      id: "link130",
      id1: "node4",
      id2: "node3880",
    },
    {
      type: "link",
      id: "link131",
      id1: "node4",
      id2: "node3881",
    },
    {
      type: "link",
      id: "link132",
      id1: "node4",
      id2: "node3882",
    },
    {
      type: "link",
      id: "link133",
      id1: "node4",
      id2: "node3884",
    },
    {
      type: "link",
      id: "link134",
      id1: "node4",
      id2: "node3885",
    },
    {
      type: "link",
      id: "link135",
      id1: "node4",
      id2: "node3886",
    },
    {
      type: "link",
      id: "link136",
      id1: "node4",
      id2: "node3887",
    },
    {
      type: "link",
      id: "link137",
      id1: "node4",
      id2: "node3888",
    },
    {
      type: "link",
      id: "link138",
      id1: "node4",
      id2: "node3889",
    },
    {
      type: "link",
      id: "link139",
      id1: "node4",
      id2: "node3890",
    },
    {
      type: "link",
      id: "link140",
      id1: "node4",
      id2: "node170",
    },
    {
      type: "link",
      id: "link141",
      id1: "node4",
      id2: "node3891",
    },
    {
      type: "link",
      id: "link142",
      id1: "node4",
      id2: "node1297",
    },
    {
      type: "link",
      id: "link143",
      id1: "node4",
      id2: "node164",
    },
    {
      type: "link",
      id: "link144",
      id1: "node4",
      id2: "node167",
    },
    {
      type: "link",
      id: "link145",
      id1: "node4",
      id2: "node3892",
    },
    {
      type: "link",
      id: "link146",
      id1: "node170",
      id2: "node177",
    },
    {
      type: "link",
      id: "link147",
      id1: "node171",
      id2: "node174",
    },
    {
      type: "link",
      id: "link148",
      id1: "node171",
      id2: "node173",
    },
    {
      type: "link",
      id: "link149",
      id1: "node171",
      id2: "node3893",
    },
    {
      type: "link",
      id: "link150",
      id1: "node171",
      id2: "node3894",
    },
    {
      type: "link",
      id: "link151",
      id1: "node170",
      id2: "node3951",
    },
    {
      type: "link",
      id: "link152",
      id1: "node170",
      id2: "node175",
    },
    {
      type: "link",
      id: "link153",
      id1: "node170",
      id2: "node3896",
    },
    {
      type: "link",
      id: "link154",
      id1: "node170",
      id2: "node3897",
    },
    {
      type: "link",
      id: "link155",
      id1: "node170",
      id2: "node220",
    },
    {
      type: "link",
      id: "link156",
      id1: "node171",
      id2: "node3898",
    },
    {
      type: "link",
      id: "link157",
      id1: "node171",
      id2: "node3899",
    },
    {
      type: "link",
      id: "link158",
      id1: "node171",
      id2: "node3900",
    },
    {
      type: "link",
      id: "link159",
      id1: "node171",
      id2: "node3901",
    },
    {
      type: "link",
      id: "link160",
      id1: "node171",
      id2: "node3902",
    },
    {
      type: "link",
      id: "link161",
      id1: "node170",
      id2: "node3903",
    },
    {
      type: "link",
      id: "link162",
      id1: "node170",
      id2: "node238",
    },
    {
      type: "link",
      id: "link163",
      id1: "node170",
      id2: "node239",
    },
    {
      type: "link",
      id: "link164",
      id1: "node170",
      id2: "node246",
    },
    {
      type: "link",
      id: "link165",
      id1: "node170",
      id2: "node247",
    },
    {

// ...truncated 13097 lines
<!doctype html>
<html lang="en" style="background-color: #2d383f">
  <head>
    <meta charset="utf-8" />
    <title>Clusters</title>
    <link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/keylines.css" />
    <link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/minimalsdk.css" />
    <link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/sdk-layout.css" />
    <link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/demo.css" />
    <link rel="stylesheet" href="./style.css" />
  </head>
  <body>
    <div id="klchart" class="klchart"></div>
    <script type="module" src="./code.js"></script>
  </body>
</html>
.sliderScale {
  float: left;
  line-height: 33px;
}

.sliderContainer {
  float: left;
  width: calc(100% - 30px);
}

#clustersSlider {
  margin: 10px;
  width: calc(100% - 20px);
}

Terms of use

These terms do not alter or supersede any existing agreements between you (or your employer) and us.

By accessing or using any Content you agree to be bound by these Terms of Use. Please review these terms carefully before using the website.

The contents of this website, including but not limited to any text, code samples, API references, schemas, interactive tools, and other materials (collectively, the 'Content'), are made available for informational and internal evaluation purposes only. All intellectual property rights in the Content are reserved. No licence is granted to use the Content for any commercial purpose, or to copy, distribute, modify, reverse-engineer, or incorporate any part of the Content into any product or service, without our prior written consent.

This Content is provided “as is” and “as available,” without any representations, warranties, or guarantees of any kind, whether express or implied, including but not limited to implied warranties of merchantability, fitness for a particular purpose, non-infringement, or accuracy. To the fullest extent permitted by applicable law, we expressly exclude and disclaim all implied warranties, conditions, and other terms that might otherwise be implied.

We disclaim all liability for any loss or damage, whether direct, indirect, incidental, consequential, or otherwise, arising from any reliance placed on the Content or from your use of it, to the fullest extent permitted by applicable law. By continuing to access or use the Content, you acknowledge and agree to these terms.