d3.nest с переменной глубиной для детей
У меня есть файл tsv с простым содержимым типа "count" следующим образом:
type count
level1/level2/level3/Foo 24
level1/level2/level3/Bar 2
level1/level2/Baz 28
level1/level2/Quz 3
...
level
Строки могут быть любыми. Я просто назвал их вот так, чтобы передать смысл. Последний элемент типа, Foo
, Bar
и т. д. можно считать листьями данных.
Используя d3, я хочу превратить это в диаграмму солнечных лучей. Для этого я использую d3.nest
функция разбить тип по слешам.
d3.tsv("data.tsv", function(error, data) {
data.forEach(function(d) {
d.count = +d.count;
});
// define key functions for a depth up to 4
var nestedData = d3.nest()
.key(function(d) { return level(d.type, 2); })
.key(function(d) { return level(d.type, 3); })
.key(function(d) { return level(d.type, 4); })
.entries(data);
// create the key for a specified level
function level(type, l) {
var parts = type.split("/");
var result = "";
for (i = 0; i < l && i < parts.length; i++) {
result += parts[i];
if (i < l-1 && i < parts.length-1) {
result += "/";
}
}
return result;
}
...
Проблема здесь в том, что в результате nestedData
всегда есть записи / листы на глубине 4. В примере данных вы можете видеть, что листы могут быть на любой глубине.
Как я могу построить вложенные данные так, чтобы записи могли происходить на любой глубине, а не только на предопределенной глубине?
1 ответ
Поскольку d3.nest
заставляет вас зарегистрировать вложенные ключи заранее, прежде чем позвонить nest.entries
нет способа сделать вложение с переменным количеством уровней, указав все заранее.
Единственное, что может сработать, - это рекурсивно использовать функцию nest.rollup для контроля того, как будут выглядеть значения на каждом уровне. Внутри вашего rollup
Вы можете решить, должен ли каждый элемент записи быть новым d3.nest
содержащий следующий уровень детализации, или если это должен быть листовой узел.
Вот грубая трещина на нем (предполагает level1
всегда будет присутствовать)
function rollupFn(group) {
var leaves = [],
groups = [];
// Split the items into leaves and groups
group.forEach(function(item) {
// First eliminate level already accounted for by d3.nest key
item.type = item.type.substr(item.type.indexOf('/')+1);
if (item.type.indexOf('/') > -1) {
groups.push(item);
} else {
leaves.push(item);
}
});
// Convert the "group" items into a d3.nest object
groups = d3.nest().key(function(d) {
return d.type.split('/')[0];
})
.rollup(rollupFn)
.entries(groups);
var results = [];
if (groups.length > 0) { results.push(groups); }
if (leaves.length > 0) { results.push(leaves); }
return results;
};
var nestedData = d3.nest()
.key(function(d) { return "level1" }) // optional: first group contains everything
.rollup(rollupFn)
.entries(data);
Значения на каждом уровне будут состоять из одного элемента, содержащего объект d3.nest, представляющий все нижние уровни, за которыми следуют все конечные узлы. Если дальнейших уровней нет, массивом значений будут все конечные узлы.
Вот несколько рабочих кодов, через которые вы можете пройти: http://jsfiddle.net/S8aMU/10