d3.nest com profundidade variável de filhos
-
21-12-2019 - |
Pergunta
Eu tenho um arquivo tsv com um conteúdo simples de "tipo" "contagem" como este:
type count
level1/level2/level3/Foo 24
level1/level2/level3/Bar 2
level1/level2/Baz 28
level1/level2/Quz 3
...
O level
strings podem ser qualquer String, apenas as nomeei assim aqui, para transmitir o significado.O último elemento do tipo, Foo
, Bar
, etc.podem ser considerados as folhas dos dados.
Usando d3, quero transformar isso em um diagrama sunburst.Para fazer isso, eu uso o d3.nest
função para dividir o tipo nas barras.
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;
}
...
A questão aqui é que o resultado nestedData
sempre tem as entradas/folhas na profundidade 4.Nos dados de exemplo você pode ver que as folhas podem estar em qualquer profundidade.
Como posso construir dados aninhados para que as entradas possam ocorrer em qualquer profundidade, não apenas em uma profundidade predefinida?
Solução
Desde d3.nest
faz com que você registre as chaves de aninhamento com antecedência, antes de ligar nest.entries
não há como fazer um aninhamento com um número variável de níveis especificando tudo antecipadamente.
Uma coisa que pode funcionar é usar recursivamente um ninho.rollup função para controlar a aparência dos valores em cada nível.Dentro do seu rollup
você pode decidir se cada elemento de entrada deve ser um novo d3.nest
contendo o próximo nível de detalhe ou se deveria ser um nó folha.
Aqui está uma explicação aproximada (assume level1
estará sempre presente):
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);
Os valores em cada nível consistirão em um elemento contendo um objeto d3.nest representando todos os níveis inferiores seguidos por todos os nós folha.Se não houver mais níveis, a matriz de valores será composta por todos os nós folha.
Aqui está um código funcional que você pode percorrer: http://jsfiddle.net/S8aMU/10