"use strict"; function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } d3.json('https://gist.githubusercontent.com/susielu/3d194b8660ec6ab214a3/raw/farmers-markets.json', function (error, data) { var variables = [{ "key": "vegetables", "label": "Vegetables 96%", "percent": .96 }, { "key": "bakedgoods", "label": "Baked Goods 88%", "percent": .88 }, { "key": "honey", "label": "Honey 81%", "percent": .81 }, { "key": "jams", "label": "Jams 80%", "percent": .80 }, { "key": "fruits", "label": "Fruits 80%", "percent": .80 }, { "key": "herbs", "label": "Herbs 79%", "percent": .79 }, { "key": "eggs", "label": "Eggs 74%", "percent": .74 }, { "key": "flower", "label": "Flowers 69%", "percent": .69 }, { "key": "soap", "label": "Soap 67%", "percent": .67 }, { "key": "plants", "label": "Plants 66%", "percent": .66 }, { "key": "crafts", "label": "Crafts 61%", "percent": .61 }, { "key": "prepared", "label": "Prepared Food 61%", "percent": .61 }, { "key": "meat", "label": "Meat 55%", "percent": .55 }, { "key": "cheese", "label": "Cheese 50%", "percent": .50 }, { "key": "poultry", "label": "Poultry 45%", "percent": .45 }, { "key": "coffee", "label": "Coffee 33%", "percent": .33 }, { "key": "maple", "label": "Maple 32%", "percent": .32 }, { "key": "nuts", "label": "Nuts 29%", "percent": .29 }, { "key": "trees", "label": "Trees 29%", "percent": .29 }, { "key": "seafood", "label": "Seafood 24%", "percent": .24 }, { "key": "juices", "label": "Juices 22%", "percent": .22 }, { "key": "mushrooms", "label": "Mushrooms 22%", "percent": .22 }, { "key": "petfood", "label": "Pet Food 18%", "percent": .18 }, { "key": "wine", "label": "Wine 17%", "percent": .17 }, { "key": "beans", "label": "Beans 14%", "percent": .14 }, { "key": "grains", "label": "Grains 14%", "percent": .14 }, { "key": "wildharvest", "label": "Wild Harvest 13%", "percent": .13 }, { "key": "nursery", "label": "Nursery 6%", "percent": .06 }, { "key": "tofu", "label": "Tofu 4%", "percent": .04 }]; var features = []; var clusters = 5; data.forEach(function (d) { var f = []; variables.forEach(function (k) { if (d[k.key] === "Y") { f.push(1); } else { f.push(0); } }); features.push(f); }); var km = new kMeans({ K: clusters }); var cluster = function cluster() { km.cluster(features); while (km.step()) { km.findClosestCentroids(); km.moveCentroids(); var hasConverged = void 0; try { hasConverged = km.hasConverged(); if (hasConverged) break; } catch (e) { console.log('error', e); } } console.log(km.centroids, km.clusters); variables.forEach(function (v, i) { v.clusterValues = []; km.centroids.forEach(function (c, j) { v.clusterValues.push({ value: c[i], cluster: j }); }); v.clusterValues.forEach(function (c) { c.minDiff = Math.min.apply(Math, _toConsumableArray(v.clusterValues.map(function (m) { return c.value !== m.value ? Math.abs(m.value - c.value) : 1; }))); }); }); }; cluster(); var svg = d3.select('svg'); var w = 29; var wPadding = 65; var h = 130; var yOffset = 220; var y = d3.scaleLinear().range([0 + yOffset, h + yOffset]).domain([1, 0]); var colors = d3.scaleOrdinal().domain(d3.range(0, clusters, 1)).range(['rgb(30, 0, 255)', '#0960ff', '#00ffc4', '#2bff1d', '#a4e800', 'rgb(0, 92, 255)', 'rgb(255, 0, 92)', 'rgb(255, 92, 0)', 'orange']); var getOffset = function getOffset(d, i, j) { var minDiff = variables[j].clusterValues[i].minDiff; return y(d + minDiff) - y(d); }; var drawArea = function drawArea(d, i) { var line = d3.area().x(function (d, i) { return i * w + wPadding; }).y1(function (d, j) { return y(d) + getOffset(d, i, j); }).y0(function (d, j) { return y(d) - getOffset(d, i, j); }).curve(d3.curveCardinal); return line(d); }; var drawLine = d3.line().x(function (d, i) { return i * w + wPadding; }).y(function (d) { return y(d); }).curve(d3.curveCardinal); variables.forEach(function (v, i) { var variable = svg.append('g').attr('class', 'variable'); var xOffset = i * w + wPadding; variable.append('line').attr('x1', xOffset).attr('x2', xOffset).attr('y1', y(0)).attr('y2', y(1)); variable.append('text').attr('x', xOffset).attr('y', h + yOffset + 50).attr('transform', "rotate(45, " + (xOffset - 15) + ", " + (h + yOffset + 50) + ")").text(v.label); }); svg.append('g').attr('class', 'connectors'); var colorLegend = d3.legendColor().orient('horizontal').shapeWidth(30).scale(colors); svg.append('g').attr('class', 'legend').attr('transform', 'translate(300, 120)').call(colorLegend); var legendScale = d3.scaleLinear().domain([0, d3.max(km.clusters, function (d) { return d.length; })]).range([0, 60]); var t = d3.transition().ease(d3.easeLinear); var recluster = function recluster() { cluster(); var connectorsArea = svg.select('g.connectors').selectAll('path.area').data(km.centroids); var connectorsLine = svg.select('g.connectors').selectAll('path.line').data(km.centroids); var hoverPath = function hoverPath() {}; connectorsArea.enter().append('path').attr('class', 'area').on('hover', hoverPath).merge(connectorsArea).attr('stroke', function (d, i) { return colors(i); }).attr('fill', function (d, i) { return colors(i); }).transition().attr('d', function (d, i) { return drawArea(d, i); }); connectorsArea.exit().remove(); connectorsLine.enter().append('path').attr('class', 'line').on('hover', hoverPath).merge(connectorsLine).attr('stroke', function (d, i) { return d3.color(colors(i)).darker(); }).transition().attr('d', function (d, i) { return drawLine(d, i); }); connectorsLine.exit().remove(); svg.selectAll('g.variable').each(function () { legendScale.domain([0, d3.max(km.clusters, function (d) { return d.length; })]); svg.selectAll('.cell .swatch').data(km.clusters).transition(t).attr('height', function (d) { return legendScale(d.length); }).attr('y', function (d) { return -legendScale(d.length) + 15; }); svg.select('.legendCells').selectAll('text.count').data(km.clusters).enter().append('text').attr('class', 'count'); svg.selectAll('text.count').text(function (d) { return d.length; }).transition(t).attr('x', function (d, i) { return i * 32 + 15; }).attr('y', function (d) { return -legendScale(d.length) + 10; }); }); }; recluster(); // interactions var buttons = svg.append('g').attr('class', 'buttons').attr('transform', 'translate(30, 130)'); buttons.append('rect').attr('width', 100).attr('height', 30).on('click', recluster); buttons.append('text').text('Recluster').attr('x', 50).attr('y', 20); });