Я хотел бы знать, есть ли простой способ изменить диаграмму диаграммы Sankey, чтобы плавный переход к новым данным. Например, представьте, что у меня есть разные файлы данных (energy1.json, energy2.json...), как d3 может построить диаграмму Санки для первого набора данных, затем ждет, а затем переводит расположение ящиков для представления второго набора данных?
Преобразование Санки
Ответ 1
Это возможно. Здесь один подход с использованием файла csv. Рабочий sankey здесь: https://www.betterment.com/resources/investment-strategy/portfolio-management/portfolio-diversification/
-
Определите глобальный массив вне вашего вызова d3.csv.
var portfolioValues = [];
-
При анализе csv для создания структуры node/link, введите значения в свой глобальный массив.
d3.csv("etf-geo.csv", function(error, data) { graph = {"nodes" : [], "links" : []}; data.forEach(function (d, i) { var item = { source: d.source, target: d.target, values: [] }; for (var j=0; j < 101; j++) { item.values.push(d['value'+j.toString()]); } portfolioValues.push(item); graph.nodes.push({ "name": d.source }); graph.nodes.push({ "name": d.target }); graph.links.push({ source: portfolioValues[i].source, target: portfolioValues[i].target, value: portfolioValues[i].values[startingAllocation] }); }); //this handy little function returns only the distinct / unique nodes graph.nodes = d3.keys( d3.nest() .key(function (d) { return d.name; }) .map(graph.nodes) ); // it appears d3 with force layout wants a numeric source and target // so loop through each link replacing the text with its index from node graph.links.forEach(function (d, i) { graph.links[i].source = graph.nodes.indexOf(graph.links[i].source); graph.links[i].target = graph.nodes.indexOf(graph.links[i].target); portfolioValues[i].source = graph.links[i].source; portfolioValues[i].target = graph.links[i].target; }); // now loop through each nodes to make nodes an array of objects // rather than an array of strings graph.nodes.forEach(function (d, i) { graph.nodes[i] = { "name": d }; }); // construct sankey sankey .nodes(graph.nodes) .links(graph.links) .layout();
-
Прослушивание изменений и передача пользовательского ввода в вашу функцию обновления.
$(".sankey-slider").bind("slider:changed", function (event, data) { slideValue = data.value; updateData(parseInt(slideValue)); });
-
Создайте временный массив и верните правильные значения из глобального массива. Вызовите функции sankey, чтобы пересчитать макет.
var newLinks = []; portfolioValues.forEach(function(p, i) { newLinks.push({ source: p.source, target: p.target, value: p.values[allocation] }); }); graph.links = newLinks; sankey .nodes(graph.nodes) .links(graph.links) .size([width, height]) .layout();
-
Выберите каждый элемент, который необходимо изменить, и передайте новые значения данных.
d3.selectAll(".link") .data(graph.links) .attr("d", path) .attr("id", function(d,i){ d.id = i; return "link-"+i; }) .style("stroke-width", function(d) { return Math.max(1, d.dy); }) .sort(function(a, b) { return b.dy - a.dy; }); d3.selectAll(".node").attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); d3.selectAll("rect") .attr("height", function(d) { return d.dy; }) .on("mouseover",highlight_node_links) .on("mouseout",onNodeMouseout);
Рабочие санки здесь: https://www.betterment.com/resources/investment-strategy/portfolio-management/portfolio-diversification/
Ответ 2
Поскольку автоматическое позиционирование узлов включает в себя часть, которая пытается минимизировать расстояние связи в связанном графике, что является проблемой оптимизации np, любой оптимизатор может потенциально переходить от одного минимума к другому, что приводит к скачку макета. Таким образом, гарантированный плавный переход невозможен.
Ближайшим возможным решением, вероятно, будет линейная интерполяция между двумя наборами входных данных и тем самым генерация серии графиков, которые (в зависимости от данных) более или менее плавно переходят от одного другого к другому.
Надеюсь, что это поможет.