Search

Insurance Fraud Analysis

Insurance Fraud Analysis
Scroll to example

It can often be difficult to see the important information in your data, even when everything presented is related. This demo shows just a few techniques for visualising insurance fraud data to get a better understanding of what insights can be found.

The data is a fictional representation of typical insurance claim data.

Each view combines multiple filters and data manipulations, taking advantage of KeyLines' extensive functionality to create a visualisation that shows different anomalies in the data to help detect fraud. Select a different view from the drop-down menu to see what insights remodelling can reveal.

To visualise data on a map, we integrate KeyLines with ESRI Leaflet and the ArcGIS mapping platform.

Key functions used:

import KeyLines from "keylines";
import "leaflet";
import {
  data,
  defaultStyle,
  defaultNodeStyle,
  defaultLinkStyle,
  mapNodeStyle,
  mapLinkStyle,
  comboStyle,
  comboGlyphStyle,
  selectedNodeStyle,
  defaultLabelStyle,
} from "./data";

// Returns colour for betweenness sizing
function getColour(value) {
  if (value < 0.25) {
    return "#A674BA";
  }
  if (value < 0.5) {
    return "#844C9A";
  }
  if (value < 0.75) {
    return "#583267";
  }
  return "#2C1933";
}

let chart;
let underlyingGraph;

const modelElement = document.getElementById("model");
const descriptorElements = Array.from(document.querySelectorAll(".model-text"));

let singleGarageMode = false;

/*  Create chart items helpers */

// Returns the current model
function getSelectedModel() {
  return { model: modelElement.value };
}

// Changes the base map layer
function mapBaseLayer() {
  const leafletMap = chart.map().leafletMap();
  const basemap = L.tileLayer(
    "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
    {
      attribution: "© OpenStreetMap contributors",
    },
  );
  basemap.addTo(leafletMap);
}

// Returns array of all combo node ids
function getComboIds() {
  const comboIds = [];
  chart.each({ type: "node", items: "toplevel" }, (item) => {
    comboIds.push(item.id);
  });
  return comboIds;
}

// Returns the single selected id or null
function getSelection() {
  const selection = chart.selection();
  if (selection.length === 0) {
    return null;
  }
  return selection[0];
}

// Returns array of items excluding selected item and its neighbours
function getUnrelatedItems(nodes, selectedId) {
  const unrelatedItems = [];
  chart.each({ type: "node" }, (item) => {
    if (!nodes.includes(item.id) && item.id !== selectedId) {
      unrelatedItems.push(item.id);
    }
  });
  return unrelatedItems;
}

// Return nodes based on their d.kind property
function getNodesByKind(kind) {
  const nodes = [];
  chart.each({ type: "node" }, (n) => {
    if (n.d && n.d.kind === kind) {
      nodes.push(n);
    }
  });
  return nodes;
}

// Return font icon from item property
function getIconByKind(kind) {
  return defaultStyle.kindIcons[kind];
}

// Returns neighbours of an item filtered by kind
function getNeighboursByKind(id, kind, hops = 1) {
  const neighbourIds = chart.graph().neighbours(id, { hops }).nodes.concat(id);
  const neighbours = chart.getItem(neighbourIds);
  const neighboursOfKind = neighbours.filter((n) => n.d && n.d.kind === kind);
  return neighboursOfKind.map((node) => node.id);
}

// Get the paths for a model
function findNetworkSelection(id) {
  let selectedId = id;
  // If we've selected one of the 4 nodes to do with a policy,
  // then pretend we selected the claim instead
  const item = chart.getItem(id);
  if (item.d.kind === "person") {
    const policies = getNeighboursByKind(id, "policy");
    if (policies.length === 1) {
      selectedId = policies[0];
    }
  } else if (["address", "telephone"].includes(item.d.kind)) {
    selectedId = getNeighboursByKind(id, "policy", 2)[0];
  }
  // Find the neighbouring claims to our selection.
  const claims = getNeighboursByKind(selectedId, "claim");
  if (claims.length === 0) {
    return chart
      .graph()
      .neighbours(selectedId, { hops: 2 })
      .nodes.concat(selectedId);
  }
  const neighbourIds = [...chart.graph().neighbours(claims).nodes, ...claims];
  // Fill in the policy data - next to the person who took the policy
  const policies = getNeighboursByKind(claims.concat(selectedId), "policy");
  const policyTakers = getNeighboursByKind(policies, "person");
  const policyData = [
    ...chart.graph().neighbours(policyTakers).nodes,
    ...policyTakers,
  ];
  return [...neighbourIds, ...policyData];
}

function styleSuspiciousItems(linkId) {
  let comboId;
  if (!chart.combo().isCombo(linkId)) {
    comboId = chart.combo().find(linkId);
  } else {
    comboId = linkId;
  }
  const underlyingLinks = chart.combo().info(comboId).links;
  // Highlight items where there is a large proportion of the same repairs
  if (underlyingLinks.length > 12) {
    const propLink = {
      id: linkId,
      w: 10,
      c: defaultStyle.linkColours.suspiciousConnection,
    };
    const propNode = {
      id: chart.getItem(linkId).id1,
      fi: {
        c: defaultStyle.nodeColours.suspiciousGarage,
        t: getIconByKind("garage"),
      },
    };
    return [propLink, propNode];
  }
  return [];
}

// Get colour of link based on distance between person and garage
function getMapLinkColour(link) {
  const id1 = chart.getItem(link.id1);
  const id2 = chart.getItem(link.id2);
  const xDist = id1.x - id2.x;
  const yDist = id1.y - id2.y;
  const distanceSquared = xDist * xDist + yDist * yDist;
  // Recolour longer distances
  if (distanceSquared > 210 * 210) {
    return defaultStyle.linkColours.suspiciousConnection;
  }
  return defaultStyle.linkColours.normalDistance;
}

/* END of helper functions to create chart items */

function applyMapStyling() {
  const props = [];
  // Styling of nodes in map mode.
  chart.each({ type: "node" }, (item) => {
    if (defaultStyle.nodeColours[item.d.kind]) {
      props.push(
        Object.assign({}, mapNodeStyle, {
          id: item.id,
          g: {
            p: "ne",
            fi: {
              t: getIconByKind(item.d.kind),
              c: defaultStyle.nodeColours[item.d.kind],
            },
            e: 2,
          },
        }),
      );
    }
  });
  // Styling of links
  chart.each({ type: "link" }, (item) => {
    props.push(
      Object.assign({}, mapLinkStyle, {
        id: item.id,
        c: getMapLinkColour(item),
      }),
    );
  });

  chart.animateProperties(props, { time: 250 });
}

/* Controls the chart selection behaviour */

// Default selection foregrounds neighbours of selection
function defaultOnSelect(id) {
  const item = chart.getItem(id);
  if (id === null) {
    // clicked on background - restore all the elements in the foreground
    chart.foreground(() => true, { type: "all" });
    chart.selection([]);
  } else if (item && item.type === "node") {
    // show only direct neighbours of nodes
    const result = chart.graph().neighbours(id).nodes.concat(item.id);
    chart.foreground((node) => result.includes(node.id), { type: "node" });
    chart.selection([id]);
  }
}

// Network selection foregrounds neighbours of nearest claim
function networkOnSelect(id) {
  const item = chart.getItem(id);
  if (id === null) {
    // clicked on background - restore all the elements in the foreground
    chart.foreground(() => true, { type: "all" });
    chart.selection([]);
  } else if (item && item.type === "node") {
    const result = findNetworkSelection(id);
    chart.foreground((node) => result.includes(node.id), { type: "node" });
    chart.selection([id]);
  }
}

// In 'garage-repair' view, we hide items that are not neighbours
async function garageOnSelect(id) {
  const selectedModel = getSelectedModel();

  if (singleGarageMode) {
    // If background selected then return to full garage-damages model
    if (id === null) {
      const filterOptions = {
        hideSingletons: true,
      };

      // Filters the chart to include all previously hidden items for the model
      await chart.filter(
        (node) => node.d.models.includes(selectedModel.model),
        filterOptions,
      );
      singleGarageMode = false;
      chart.combo().reveal([]);
      chart.layout("standard", { consistent: true });
    }
    return;
  }

  const selectedItem = chart.getItem(id);

  if (!(selectedItem && selectedItem.d && selectedItem.d.kind === "garage")) {
    return;
  }

  // Return underlying graph from graph engine
  underlyingGraph = KeyLines.getGraphEngine();
  underlyingGraph.load(chart.serialize());

  // Find neighbours of selected item and hide items that are not
  const garageUnderlyingNeighbours = underlyingGraph.neighbours(
    selectedItem.id,
  );
  const garageComboNeighbours = chart.graph().neighbours(selectedItem.id);
  chart.combo().close(garageComboNeighbours.nodes);
  await chart.hide(
    getUnrelatedItems(garageUnderlyingNeighbours.nodes, selectedItem.id),
  );
  singleGarageMode = true;
  await chart.layout("radial", { top: selectedItem.id });
  chart.combo().reveal(garageUnderlyingNeighbours.links);
  let props = [];
  garageUnderlyingNeighbours.links.forEach((linkId) => {
    const linkProps = styleSuspiciousItems(linkId);
    props = props.concat(linkProps);
  });
  chart.setProperties([
    ...garageComboNeighbours.links.map((comboLinkId) => ({
      id: comboLinkId,
      hi: true,
    })),
    ...props,
  ]);
}
/* END chart selection functions */

// Damages are combined with links to garages
async function combineDamages() {
  const garages = getNodesByKind("garage");
  const damages = getNodesByKind("damage");
  const damagesGroup = damages.reduce((groupBy, damage) => {
    groupBy[damage.d.subkind] = (groupBy[damage.d.subkind] || []).concat(
      damage,
    );
    return groupBy;
  }, {});
  let props = [];

  const comboDefinition = Object.keys(damagesGroup).map((subkind) => ({
    ids: damagesGroup[subkind].map((damage) => damage.id),
    style: {
      ...comboStyle,
      t: {
        ...defaultLabelStyle,
        t: subkind,
      },
    },
    open: false,
  }));
  const comboOptions = { arrange: "concentric", select: false };

  await chart.combo().combine(comboDefinition, comboOptions);
  // Highlight suspicious activity between neighbours
  garages.forEach((g) => {
    const linkIds = chart.graph().neighbours(g.id).links;
    linkIds.forEach((linkId) => {
      const linkProps = styleSuspiciousItems(linkId);
      props = props.concat(linkProps);
    });
    // Resize the garages to scale with combos
    props.push({ id: g.id, e: 10 });
  });
  // Apply properties and run layout
  await chart.setProperties(props, false);
  await chart.layout("standard", { consistent: true });
}

/*  Transition functions for each model  */
async function fullNetworkTransition(props, options) {
  await chart.animateProperties(props, options);
  chart.layout("organic", { tightness: 4, consistent: true });
}

async function peopleTransition(props, options) {
  chart.foreground(() => true, { type: "all" });
  await chart.animateProperties(props, options);
  await chart.layout("organic", { consistent: true, packing: "circle" });
}

async function personGarageTransition(props, options) {
  chart.foreground(() => true, { type: "all" });
  await chart.animateProperties(props, options);
  await chart.map().show();
  await chart.zoom("fit");
}

async function garageDamagesTransition(props, options) {
  chart.foreground(() => true, { type: "all" });
  chart.selection([]);
  singleGarageMode = false;

  await chart.animateProperties(props, options);
  combineDamages();
}

/* END Transition functions for each model */

// Define model object properties
const models = {
  none: {
    transition: fullNetworkTransition,
    onSelect: networkOnSelect,
  },
  "garage-damages": {
    // Return to default settings to avoid unnecessary behaviour on item selection
    transition: garageDamagesTransition,
    onSelect: garageOnSelect,
  },
  people: {
    transition: peopleTransition,
    onSelect: defaultOnSelect,
  },
  "person-garage": {
    transition: personGarageTransition,
    onSelect: defaultOnSelect,
  },
};

/*  Styling for each model view */
async function getStyling() {
  const selectedModel = getSelectedModel();
  const props = [];
  // Apply styling to nodes and make sure all items transition from centre of chart
  chart.each({ type: "node" }, (item) => {
    if (defaultStyle.nodeColours[item.d.kind]) {
      props.push(
        Object.assign({}, defaultNodeStyle, {
          id: item.id,
          t: {
            ...defaultLabelStyle,
            t: item.d.label,
          },
          b: defaultStyle.nodeColours[item.d.kind],
          fi: {
            c: defaultStyle.nodeColours[item.d.kind],
            t: item.d.subkindperson
              ? getIconByKind(item.d.subkindperson)
              : getIconByKind(item.d.kind),
          },
        }),
      );
    } else {
      props.push({ id: item.id, x: 0, y: 0 });
    }
  });
  // Style links
  chart.each({ type: "link" }, (item) => {
    props.push(Object.assign({}, defaultLinkStyle, { id: item.id }));
  });
  if (selectedModel.model === "people") {
    // Calculate betweenness between components
    const betweenness = await chart
      .graph()
      .betweenness({ normalization: "component" });
    // Adjust size and colour of node based on betweenness in component
    const adjustments = {};
    chart
      .graph()
      .components()
      .forEach((component) => {
        const length = component.nodes.length;
        component.nodes.forEach((node) => {
          const enlargement = ((betweenness[node] - 0.5) * length) ** 0.6;
          const size = enlargement >= 1 ? enlargement : 1;
          const colour = getColour(betweenness[node]);
          adjustments[node] = { size, colour };
        });
        props.forEach((item, index) => {
          if (adjustments[item.id]) {
            props[index].e = adjustments[item.id].size;
            props[index].fi.c = adjustments[item.id].colour;
            props[index].b = adjustments[item.id].colour;
          }
        });
      });
  }
  return props;
}

/*  Chart filter and model transition */
async function showSelectedModel() {
  const selectedModel = getSelectedModel();

  if (selectedModel.model !== "garage-damages") {
    // Make sure to uncombine combos and to reveal all links
    chart.combo().reveal([]);
    chart.combo().uncombine(getComboIds(), { time: 0, select: false });
  }

  // Update the chart options select node property
  chart.options({ selectedNode: selectedNodeStyle[selectedModel.model] });

  const transitionOptions = { time: 200 };
  const filterOptions = {
    time: 500,
    hideSingletons: true,
  };
  // Filter the items for the selected model
  await chart.filter(
    (node) => node.d.models.includes(selectedModel.model),
    filterOptions,
  );
  // Transition to the selected model after collecting styling
  const props = await getStyling();
  await models[selectedModel.model].transition(props, transitionOptions);
  modelElement.disabled = false;
  models[selectedModel.model].onSelect(getSelection());
}
/* END Chart filter and Transition */

// Controls the item selection behaviour
function onSelection() {
  chart.off("click");
  chart.on("click", ({ id }) => {
    models[getSelectedModel().model].onSelect(id);
  });
}

function onMap({ type }) {
  if (type === "showend") {
    mapBaseLayer();
    applyMapStyling();
  }
}

function preventCombosOpening() {
  if (!chart) return;

  chart.on("double-click", ({ id, preventDefault }) => {
    if (chart.combo().isCombo(id)) {
      preventDefault();
    }
  });
}

async function startKeyLines() {
  const chartOptions = {
    logo: { u: "/public/images/Logo.png" },
    iconFontFamily: "Font Awesome 5 Free",
    imageAlignment: {
      "fas fa-user": { e: 1.0, dy: -5 },
      "fas fa-car": { e: 0.9, dy: -4 },
      "fas fa-wrench": { e: 0.9, dy: -2 },
      "fas fa-cogs": { e: 0.9, dx: -5 },
      "fas fa-phone": { e: 0.9, dy: 5, dx: 3 },
      "fas fa-file-invoice-dollar": { e: 0.9, dy: 0 },
      "fas fa-file-contract": { e: 0.9, dy: 0 },
      "fas fa-user-md": { dy: -3 },
    },
    selectionColour: defaultStyle.selectionColour,
    selectionFontColour: defaultStyle.selectionFontColour,
    defaultStyles: {
      comboGlyph: comboGlyphStyle,
    },
    handMode: true,
    arrows: "small",
    minZoom: 0.02,
    backColour: "#F0F8FF",
    linkEnds: { avoidLabels: false },
  };

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

  // Reduce chart zoom for smoother load & initial layout animation
  chart.viewOptions({ zoom: 0.05 });

  // Set map options for map view
  chart.map().options({
    time: 250,
    tiles: null, // Remove the default tile layer
    transition: "restore",
    leaflet: {
      minZoom: 10,
    },
  });
  chart.on("map", onMap);

  onSelection();
  // Load the data to the chart component
  chart.load(data);

  preventCombosOpening();

  // Update view and selection behaviour on dropdown change
  modelElement.addEventListener("change", async () => {
    modelElement.disabled = true;
    const modeltype = modelElement.value;
    descriptorElements.forEach((element) => element.classList.add("hide"));
    document.getElementById(modeltype).classList.remove("hide");
    onSelection();
    await chart.map().hide();
    showSelectedModel();
  });

  // Run initial full network layout
  showSelectedModel();
}

function loadKeyLines() {
  document.fonts.load("24px 'Font Awesome 5 Free'").then(startKeyLines);
}

window.addEventListener("DOMContentLoaded", loadKeyLines);
export const data = {
  type: "LinkChart",
  items: [
    {
      type: "link",
      id: "23-21",
      id1: "23",
      id2: "21",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "27-25",
      id1: "27",
      id2: "25",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "32-30",
      id1: "32",
      id2: "30",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "37-35",
      id1: "37",
      id2: "35",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "42-40",
      id1: "42",
      id2: "40",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "47-45",
      id1: "47",
      id2: "45",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "52-50",
      id1: "52",
      id2: "50",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "57-55",
      id1: "57",
      id2: "55",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "62-60",
      id1: "62",
      id2: "60",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "67-65",
      id1: "67",
      id2: "65",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "72-70",
      id1: "72",
      id2: "70",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "77-75",
      id1: "77",
      id2: "75",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "82-80",
      id1: "82",
      id2: "80",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "87-85",
      id1: "87",
      id2: "85",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "92-90",
      id1: "92",
      id2: "90",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "97-95",
      id1: "97",
      id2: "95",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "102-100",
      id1: "102",
      id2: "100",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "107-105",
      id1: "107",
      id2: "105",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "112-110",
      id1: "112",
      id2: "110",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "117-115",
      id1: "117",
      id2: "115",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "122-120",
      id1: "122",
      id2: "120",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "127-125",
      id1: "127",
      id2: "125",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "137-135",
      id1: "137",
      id2: "135",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "142-140",
      id1: "142",
      id2: "140",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "147-145",
      id1: "147",
      id2: "145",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "152-150",
      id1: "152",
      id2: "150",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "157-155",
      id1: "157",
      id2: "155",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "162-160",
      id1: "162",
      id2: "160",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "167-165",
      id1: "167",
      id2: "165",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "172-170",
      id1: "172",
      id2: "170",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "177-175",
      id1: "177",
      id2: "175",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "182-180",
      id1: "182",
      id2: "180",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "187-185",
      id1: "187",
      id2: "185",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "192-190",
      id1: "192",
      id2: "190",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "197-195",
      id1: "197",
      id2: "195",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "202-200",
      id1: "202",
      id2: "200",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "207-205",
      id1: "207",
      id2: "205",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "212-210",
      id1: "212",
      id2: "210",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "217-215",
      id1: "217",
      id2: "215",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "222-220",
      id1: "222",
      id2: "220",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "227-225",
      id1: "227",
      id2: "225",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "232-230",
      id1: "232",
      id2: "230",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "237-235",
      id1: "237",
      id2: "235",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "242-240",
      id1: "242",
      id2: "240",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "247-245",
      id1: "247",
      id2: "245",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "252-250",
      id1: "252",
      id2: "250",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "257-255",
      id1: "257",
      id2: "255",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "262-260",
      id1: "262",
      id2: "260",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "267-265",
      id1: "267",
      id2: "265",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "272-270",
      id1: "272",
      id2: "270",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "277-275",
      id1: "277",
      id2: "275",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "282-280",
      id1: "282",
      id2: "280",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "293-291",
      id1: "293",
      id2: "291",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "23-20",
      id1: "23",
      id2: "20",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "27-8",
      id1: "27",
      id2: "8",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "32-29",
      id1: "32",
      id2: "29",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "37-34",
      id1: "37",
      id2: "34",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "42-39",
      id1: "42",
      id2: "39",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "47-44",
      id1: "47",
      id2: "44",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "52-49",
      id1: "52",
      id2: "49",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "57-54",
      id1: "57",
      id2: "54",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "62-59",
      id1: "62",
      id2: "59",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "67-64",
      id1: "67",
      id2: "64",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "72-69",
      id1: "72",
      id2: "69",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "77-74",
      id1: "77",
      id2: "74",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "82-79",
      id1: "82",
      id2: "79",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "87-84",
      id1: "87",
      id2: "84",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "92-89",
      id1: "92",
      id2: "89",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "97-94",
      id1: "97",
      id2: "94",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "102-99",
      id1: "102",
      id2: "99",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "107-104",
      id1: "107",
      id2: "104",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "112-109",
      id1: "112",
      id2: "109",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "117-114",
      id1: "117",
      id2: "114",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "122-119",
      id1: "122",
      id2: "119",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "127-124",
      id1: "127",
      id2: "124",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "137-134",
      id1: "137",
      id2: "134",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "142-139",
      id1: "142",
      id2: "139",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "147-144",
      id1: "147",
      id2: "144",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "152-149",
      id1: "152",
      id2: "149",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "157-154",
      id1: "157",
      id2: "154",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "162-159",
      id1: "162",
      id2: "159",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "167-164",
      id1: "167",
      id2: "164",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "172-169",
      id1: "172",
      id2: "169",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "177-174",
      id1: "177",
      id2: "174",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "182-179",
      id1: "182",
      id2: "179",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "187-184",
      id1: "187",
      id2: "184",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "192-189",
      id1: "192",
      id2: "189",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "197-194",
      id1: "197",
      id2: "194",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "202-199",
      id1: "202",
      id2: "199",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "207-204",
      id1: "207",
      id2: "204",
      c: "#c0c0c0",
      w: 3,
      d: {
        models: ["none"],
      },
    },
    {
      type: "link",
      id: "212-209",
      id1: "212",
      id2: "209",
      c: "#c0c0c0",
      w: 3,

// ...truncated 20690 lines
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Insurance Fraud Analysis</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"
      type="text/css"
      href="@ci/theme/kl/css/examples-chrome.css"
    >
    <link rel="stylesheet" type="text/css" href="leaflet/dist/leaflet.css">
    <link
      rel="stylesheet"
      type="text/css"
      href="@fortawesome/[email protected]/css/fontawesome.css"
    >
    <link
      rel="stylesheet"
      type="text/css"
      href="@fortawesome/[email protected]/css/solid.css"
    >
  </head>
  <body>
    <div class="chart-wrapper demo-cols">
      <div id="lhs" class="flex1">
        <div
          id="lhsVisual"
          class="toggle-content is-visible"
          style="width: 100%; height: 100%"
        >
          <div id="klchart" class="klchart"></div>
        </div>
      </div>
      <div class="rhs citext" id="demorhscontent">
        <div class="tab-content-panel">
          <div id="controlsTab" class="toggle-content is-visible tabcontent">
            <p>Explore insurance data for fraudulent activity.</p>
            <form id="rhsForm" autocomplete="off" onsubmit="return false;">
              <div class="cicontent">
                <p>Choose a view below to investigate the data.</p>
                <h4>Select view</h4>
                <select class="input-block-level" id="model" disabled>
                  <option value="none">Full Network</option>
                  <option value="people">Individuals' Connections</option>
                  <option value="person-garage">Distances Travelled</option>
                  <option value="garage-damages">Damage Types</option>
                </select><span class="model-text" id="none"
                  ><p>
                    This view shows the raw data model. Although we already see
                    some clustering in this view, by remodelling the data we can
                    reveal further insights.
                  </p>
                  <p>
                    Select another view from the drop-down menu to see what we
                    can do with KeyLines.
                  </p></span
                ><span class="model-text hide" id="people"
                  ><p>
                    In this view links between individuals are dynamically
                    created based on whether they have an item in common.
                    Individuals are then sized based on betweenness which is a
                    measure of how well connected a node is.
                  </p>
                  <p>
                    Typical claims are small isolated clusters of people. We can
                    see some suspicious individuals who link multiple claims
                    together.
                  </p>
                  <p>
                    While it's normal for a doctor to be involved in multiple
                    claims, it's unusual for other individuals - such as a
                    witness, and may be worth further investigation.
                  </p></span
                ><span class="model-text hide" id="garage-damages"
                  ><p>
                    This view displays the type of damage caused, with
                    connections to the garages that fixed them. The damages are
                    grouped by car part and links represent nodes with a shared
                    claim.
                  </p>
                  <p>
                    By grouping the repairs based on the damaged vehicle part,
                    we can see that one garage is repairing a disproportionate
                    number of OS rear doors. This could indicate claim
                    inflation.
                  </p>
                  <p>
                    Click on a
                    <i class="fas fa-wrench" style="font-size: 1.5em"></i>
                    garage to explore the types of damage it has repaired.
                  </p>
                  <p>
                    Click anywhere on the chart background to undo the
                    selection.
                  </p></span
                ><span class="model-text hide" id="person-garage"
                  ><p>
                    This view places policy holders' and garages' addresses on a
                    map. Links are inferred by identifying paths between garages
                    and policy holders via damages.
                  </p>
                  <p>
                    By using geocoded data we can see patterns and other
                    information that we couldn't before. Here we see that some
                    people are travelling long distances to get to a particular
                    garage, which could indicate fraud.
                  </p></span
                >
              </div>
            </form>
          </div>
        </div>
      </div>
    </div>
    <script type="module" src="./code.js"></script>
  </body>
</html>

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.