D3 пузырьковая диаграмма / макет пакета — Как сделать так, чтобы пузырьки расходились от самых больших пузырьков к самым маленьким?

#javascript #sorting #svg #d3.js #circle-pack

#javascript #сортировка #svg #d3.js #круг-пакет

Вопрос:

Итак, я использую тот же код, что и в этом примере галереи D3 (с моими собственными данными):

http://bl.ocks.org/mbostock/4063269

Я хотел бы получить пузырьковую диаграмму, в которой круги расположены с наибольшим в центре, а затем расходятся до наименьшего.

Вот макет, который я создал в Photoshop:

Чего я на самом деле хочу

Вот что я получаю, когда использую пример (алгоритм упаковки круга по умолчанию с сортировкой по умолчанию):

Упаковка по умолчанию

Я попытался настроить сортировку (включая попытку d3.ascending и d3.descending). Лучшее, что я мог придумать, просто в основном подрывает сортировку с константой (ха!), Но все еще далеко от того, что я хотел бы:

 //...
.sort( function(a, b) { return -1;} )
//...
  

Лучшее, что я мог получить, настроив сортировку

Хорошо, есть ли шанс, что это можно сделать без необходимости изменять фактический алгоритм компоновки пакета D3? Если нет, возможно, кто-то расширил или изменил макет пакета и мог бы подсказать мне 5 строк, которые я мог бы изменить в исходном коде D3, чтобы взломать это.

Заранее спасибо!

Редактировать:

Как и было запрошено, вот код, который я использую. В основном то же самое, что и в приведенном выше примере, с несколькими поверхностными изменениями, как указано в комментариях:

 var diameter = 960,
format = d3.format(",d"),
color = d3.scale.category20c();

var bubble = d3.layout.pack()
//  .sort(null)
//  .sort(d3.ascending)
//  .sort(d3.descending)
    .sort( function(a, b) { return  -1;} ) // basically a < b always
    .size([diameter, diameter])
    .padding(1.5);

var svg = d3.select("body").append("svg")
    .attr("width", diameter)
    .attr("height", diameter)
    .attr("class", "bubble");

d3.json("data.json", function(error, root) 
{
  var node = svg.selectAll(".node")
        .data(bubble.nodes(classes(root))
        .filter(function(d) { return !d.children; }))
        .enter().append("g")
        .attr("class", "node")
        .attr("transform", function(d) { return "translate("   d.x   ","   d.y   ")"; });

  node.append("title")
      .text(function(d) { return d.className   ": "   format(d.value); });

  node.append("circle")
      .attr("r", function(d) { return d.r; })
      .style("fill", function(d) 
            { 
                // return color(d.packageName); 
                return color(d.value); // this gives a different color for every leaf node
            });

  node.append("text")
      .attr("dy", ".3em")
      .style("text-anchor", "middle")
     // .text(function(d) { return d.className.substring(0, d.r / 3); });
});

// Returns a flattened hierarchy containing all leaf nodes under the root.
function classes(root) 
{
  var classes = [];

  function recurse(name, node) {
    if (node.children) node.children.forEach(function(child) { recurse(node.name, child); });
    else classes.push({packageName: name, className: node.name, value: node.size});
  }

  recurse(null, root);
  return {children: classes};
}

d3.select(self.frameElement).style("height", diameter   "px");
  

И мой файл data.json:

 {
    "name": "Root",
    "children": [
        {
            "name": "Leaf",
            "children": null,
            "size": 2098629
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 104720
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 5430
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 102096
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 986974
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 59735
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 1902
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 120
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 870751
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 36672
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 274338
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 517693
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 145807
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 476178
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 11771
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 153
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 2138
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 8436
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 3572
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 120235
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 210945
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 56033
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 358704
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 295736
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 26087
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 33110
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 3828
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 1105544
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 98740
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 80723
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 5766
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 1453
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 10443176
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 14055
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 1890127
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 404575
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 272777
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 1269763
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 5081
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 3168510
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 717031
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 88418
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 762084
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 255055
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 535
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 81238
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 17075
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 5331
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 74834
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 110359
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 27333
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 143
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 12721
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 529
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 115684
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 3990850
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 6045060
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 2445766
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 479865
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 105743
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 183750
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 661
        },
        {
            "name": "Leaf",
            "children": null,
            "size": 11181
        }
    ],
    "size": 41103329
}
  

Комментарии:

1. Для этого вам понадобится пользовательский макет — макет пакета всегда будет упаковывать маленькие круги между большими кругами, поскольку это уменьшает потерянное пространство. О, и нет необходимости выделять все жирным шрифтом.

2. Конечно, имеет смысл, просто надеясь, что кто-то столкнулся с достаточно похожим вариантом использования и уже нашел решение. Да, не нужно выделять все жирным шрифтом; в основном это была ошибка с вырезанием и вставкой. Теперь выделены жирным шрифтом только заголовки изображений 🙂

3. @SoldierOfFortran, ты можешь просто прикрепить данные из своего примера? Код (по крайней мере, ключевые части) также желателен.

4. @VividD — Готово. Надеюсь, это поможет.

Ответ №1:

Все, что вам нужно сделать, это указать:

 .sort(function(a, b) {
    return -(a.value - b.value);
})
  

Это отличается от указания .sort(d3.ascending) или .sort(d3.descending) , поскольку d3.ascending и d3.descending определяются как

 function(a, b) {
  return a < b ? -1 : a > b ? 1 : 0;
}
  

и

 function(a, b) {
  return b < a ? -1 : b > a ? 1 : 0;
}
  

соответственно, и на макет пакета влияет их «нечувствительность» к разнице точек данных.

Это мой тестовый пример: (с вашими данными) jsfiddle

введите описание изображения здесь


В качестве эксперимента я также применил следующую функцию сортировки: (это своего рода гибрид)

 .sort( function(a, b) {
    var threshold = 10000000;
    if ((a.value > threshold) amp;amp; (b.value > threshold)) {
        return -(a.value - b.value);
    } else {
        return -1;
    }
})
  

… и для значений для порога 10000000, 3000000, 1000000, 300000, 100000, 30000 соответственно, я получил: jsfiddle

введите описание изображения здесь

введите описание изображения здесь

введите описание изображения здесь

введите описание изображения здесь

введите описание изображения здесь

введите описание изображения здесь

Комментарии:

1. Привет @VividD ! возможно ли получить разрыв между этими пузырьками?