具有一致比例的圆形包装矩阵
Circle packing matrix with consistent scale
将 for 循环与 d3 视觉效果结合使用时,我得到了不同的结果;在这种情况下,拥有圆形包矩阵似乎是最直接的解决方案。但是,一个问题是,如果我以这种方式创建视觉效果,输出可能会有点误导。在下面的代码片段中,您会注意到第三个圆包 (152) 中最大的圆看起来与第一个圆包 (200) 中的最大圆一样大。所以在目前的形式中,圆包只是反映了比例,并没有描绘出绝对大小的变化。
var margins = {top:20, bottom:300, left:30, right:100};
var height = 600;
var width = 1080;
var totalWidth = width+margins.left+margins.right;
var totalHeight = height+margins.top+margins.bottom;
var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);
var graphGroup = svg.append('g')
.attr('transform', "translate("+margins.left+","+margins.top+")");
var data = [
[
{'id':'1Q19'},
{'id':'pooled','parentId':'1Q19','size':29.5},
{'id':'spv','parentId':'1Q19', 'size':11},
{'id':'single','parentId':'1Q19', 'size':200}
],
[
{'id':'2Q19'},
{'id':'pooled','parentId':'2Q19','size':31},
{'id':'spv','parentId':'2Q19', 'size':15},
{'id':'single','parentId':'2Q19', 'size':171}
],
[
{'id':'3Q19'},
{'id':'pooled','parentId':'3Q19','size':28},
{'id':'spv','parentId':'3Q19', 'size':12},
{'id':'single','parentId':'3Q19', 'size':152}
],
[
{'id':'4Q19'},
{'id':'pooled','parentId':'4Q19','size':25},
{'id':'spv','parentId':'4Q19', 'size':214},
{'id':'single','parentId':'4Q19', 'size':101}
],
];
var colorMap = {
'1Q19':"#e7eef8",
'2Q19':"#e7eef8",
'3Q19':"#e7eef8",
'4Q19':"#e7eef8",
'pooled':"#f6d18b",
'spv':"#366092",
'single':"#95b3d7"
};
var strokeMap = {
"pooled":"#000",
"single":"#000",
"spv":"#fff"
};
for (var j=0; j <(data.length); j++) {
var vData = d3.stratify()(data[j]);
var vLayout = d3.pack().size([250, 250]);
var vRoot = d3.hierarchy(vData).sum(function (d) { return d.data.size; });
var vNodes = vRoot.descendants();
vLayout(vRoot);
var thisClass = "circ"+String(j);
var vSlices = graphGroup.selectAll('.'+thisClass).data(vNodes).attr('class',thisClass).enter().append('g');
//console.log(vNodes)
vSlices.append('circle')
.attr('cx', function(d, i) {
return d.x+(j*300)
})
.attr('cy', function (d) { return d.y; })
.attr('r', function (d) { return d.r; })
.style('fill', function(d) { return colorMap[d.data.id]});
vSlices.append('text')
.attr('x', function(d,i) {return d.x+(j*300)})
.attr('y', function(d) {return d.y+5})
.attr('text-anchor','middle')
.style('fill', function(d) {return strokeMap[d.data.id]})
.text(function(d) {return d.data.data.size ? d.data.data.size : null});
}
<script src="https://d3js.org/d3.v5.min.js"></script>
问题
如何为圆包矩阵中的每个圆包建立一个 baseline/uniform 比例?我希望 background/overall 父圆的大小相同,但子圆在打包过程中要考虑绝对值。
注意:我很满意圆包里有更多的空space;也许在某些情况下,直径可能不会完全跨越父圆。只要圆相切,整体的审美主题就贯穿始终。
您使用循环在 D3 代码中创建元素这一事实是非常有问题的,这是真的...但是,这不是这里的问题。让我们看看你说了什么:
I want the background/overall parent circle to be the same size, but the child circles to factor in absolute values in the packing process [...] I'm content with there being more empty space in the circle pack.
嗯,不幸的是,这不是不是圆形包装的工作原理。您现在拥有的是 correct 数据可视化:叶子会有不同的大小,即使它们具有相同的值,这取决于其他叶子的值。圆形包装是动态的 process/algorithm.
话虽如此,我的建议是:保持原样(但修复那个繁琐的循环)。
但是,即使我不同意(从 dataviz 的角度)您的要求,这里也有一个解决方案。设置平方根比例:
var radiusScale = d3.scaleSqrt()
.domain([0,250])
.range([0,125]);
并将 size
值传递给 pack.radius
:
var vLayout = d3.pack().size([250, 250])
.radius(function(d){
return radiusScale(d.data.data.size)
});
结果如下:
var margins = {top:20, bottom:300, left:30, right:100};
var height = 600;
var width = 1200;
var totalWidth = width+margins.left+margins.right;
var totalHeight = height+margins.top+margins.bottom;
var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);
var graphGroup = svg.append('g')
.attr('transform', "translate("+margins.left+","+margins.top+")");
const radiusScale = d3.scaleSqrt()
.domain([0,250])
.range([0,125]);
var data = [
[
{'id':'1Q19'},
{'id':'pooled','parentId':'1Q19','size':29.5},
{'id':'spv','parentId':'1Q19', 'size':11},
{'id':'single','parentId':'1Q19', 'size':200}
],
[
{'id':'2Q19'},
{'id':'pooled','parentId':'2Q19','size':31},
{'id':'spv','parentId':'2Q19', 'size':15},
{'id':'single','parentId':'2Q19', 'size':171}
],
[
{'id':'3Q19'},
{'id':'pooled','parentId':'3Q19','size':28},
{'id':'spv','parentId':'3Q19', 'size':12},
{'id':'single','parentId':'3Q19', 'size':152}
],
[
{'id':'4Q19'},
{'id':'pooled','parentId':'4Q19','size':25},
{'id':'spv','parentId':'4Q19', 'size':214},
{'id':'single','parentId':'4Q19', 'size':101}
],
];
var colorMap = {
'1Q19':"#e7eef8",
'2Q19':"#e7eef8",
'3Q19':"#e7eef8",
'4Q19':"#e7eef8",
'pooled':"#f6d18b",
'spv':"#366092",
'single':"#95b3d7"
};
var strokeMap = {
"pooled":"#000",
"single":"#000",
"spv":"#fff"
};
for (var j=0; j <(data.length); j++) {
var vData = d3.stratify()(data[j]);
var vLayout = d3.pack().size([250, 250])
.radius(function(d){
return radiusScale(d.data.data.size)
});
var vRoot = d3.hierarchy(vData).sum(function (d) { return d.data.size; });
var vNodes = vRoot.descendants();
vLayout(vRoot);
var thisClass = "circ"+String(j);
var vSlices = graphGroup.selectAll('.'+thisClass).data(vNodes).attr('class',thisClass).enter().append('g');
//console.log(vNodes)
vSlices.append('circle')
.attr('cx', function(d, i) {
return d.x+(j*(j === 3 ? 320 : 310))
})
.attr('cy', function (d) { return d.y; })
.attr('r', function (d) { return d.r; })
.style('fill', function(d) { return colorMap[d.data.id]});
vSlices.append('text')
.attr('x', function(d,i) {return d.x+(j*(j === 3 ? 320 : 310))})
.attr('y', function(d) {return d.y+5})
.attr('text-anchor','middle')
.style('fill', function(d) {return strokeMap[d.data.id]})
.text(function(d) {return d.data.data.size ? d.data.data.size : null});
}
<script src="https://d3js.org/d3.v5.min.js"></script>
请注意,在最后一包中,整个圆的大小不一样(更大)。考虑到包装逻辑,尺寸相同是不可能的。
将 for 循环与 d3 视觉效果结合使用时,我得到了不同的结果;在这种情况下,拥有圆形包矩阵似乎是最直接的解决方案。但是,一个问题是,如果我以这种方式创建视觉效果,输出可能会有点误导。在下面的代码片段中,您会注意到第三个圆包 (152) 中最大的圆看起来与第一个圆包 (200) 中的最大圆一样大。所以在目前的形式中,圆包只是反映了比例,并没有描绘出绝对大小的变化。
var margins = {top:20, bottom:300, left:30, right:100};
var height = 600;
var width = 1080;
var totalWidth = width+margins.left+margins.right;
var totalHeight = height+margins.top+margins.bottom;
var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);
var graphGroup = svg.append('g')
.attr('transform', "translate("+margins.left+","+margins.top+")");
var data = [
[
{'id':'1Q19'},
{'id':'pooled','parentId':'1Q19','size':29.5},
{'id':'spv','parentId':'1Q19', 'size':11},
{'id':'single','parentId':'1Q19', 'size':200}
],
[
{'id':'2Q19'},
{'id':'pooled','parentId':'2Q19','size':31},
{'id':'spv','parentId':'2Q19', 'size':15},
{'id':'single','parentId':'2Q19', 'size':171}
],
[
{'id':'3Q19'},
{'id':'pooled','parentId':'3Q19','size':28},
{'id':'spv','parentId':'3Q19', 'size':12},
{'id':'single','parentId':'3Q19', 'size':152}
],
[
{'id':'4Q19'},
{'id':'pooled','parentId':'4Q19','size':25},
{'id':'spv','parentId':'4Q19', 'size':214},
{'id':'single','parentId':'4Q19', 'size':101}
],
];
var colorMap = {
'1Q19':"#e7eef8",
'2Q19':"#e7eef8",
'3Q19':"#e7eef8",
'4Q19':"#e7eef8",
'pooled':"#f6d18b",
'spv':"#366092",
'single':"#95b3d7"
};
var strokeMap = {
"pooled":"#000",
"single":"#000",
"spv":"#fff"
};
for (var j=0; j <(data.length); j++) {
var vData = d3.stratify()(data[j]);
var vLayout = d3.pack().size([250, 250]);
var vRoot = d3.hierarchy(vData).sum(function (d) { return d.data.size; });
var vNodes = vRoot.descendants();
vLayout(vRoot);
var thisClass = "circ"+String(j);
var vSlices = graphGroup.selectAll('.'+thisClass).data(vNodes).attr('class',thisClass).enter().append('g');
//console.log(vNodes)
vSlices.append('circle')
.attr('cx', function(d, i) {
return d.x+(j*300)
})
.attr('cy', function (d) { return d.y; })
.attr('r', function (d) { return d.r; })
.style('fill', function(d) { return colorMap[d.data.id]});
vSlices.append('text')
.attr('x', function(d,i) {return d.x+(j*300)})
.attr('y', function(d) {return d.y+5})
.attr('text-anchor','middle')
.style('fill', function(d) {return strokeMap[d.data.id]})
.text(function(d) {return d.data.data.size ? d.data.data.size : null});
}
<script src="https://d3js.org/d3.v5.min.js"></script>
问题
如何为圆包矩阵中的每个圆包建立一个 baseline/uniform 比例?我希望 background/overall 父圆的大小相同,但子圆在打包过程中要考虑绝对值。
注意:我很满意圆包里有更多的空space;也许在某些情况下,直径可能不会完全跨越父圆。只要圆相切,整体的审美主题就贯穿始终。
您使用循环在 D3 代码中创建元素这一事实是非常有问题的,这是真的...但是,这不是这里的问题。让我们看看你说了什么:
I want the background/overall parent circle to be the same size, but the child circles to factor in absolute values in the packing process [...] I'm content with there being more empty space in the circle pack.
嗯,不幸的是,这不是不是圆形包装的工作原理。您现在拥有的是 correct 数据可视化:叶子会有不同的大小,即使它们具有相同的值,这取决于其他叶子的值。圆形包装是动态的 process/algorithm.
话虽如此,我的建议是:保持原样(但修复那个繁琐的循环)。
但是,即使我不同意(从 dataviz 的角度)您的要求,这里也有一个解决方案。设置平方根比例:
var radiusScale = d3.scaleSqrt()
.domain([0,250])
.range([0,125]);
并将 size
值传递给 pack.radius
:
var vLayout = d3.pack().size([250, 250])
.radius(function(d){
return radiusScale(d.data.data.size)
});
结果如下:
var margins = {top:20, bottom:300, left:30, right:100};
var height = 600;
var width = 1200;
var totalWidth = width+margins.left+margins.right;
var totalHeight = height+margins.top+margins.bottom;
var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);
var graphGroup = svg.append('g')
.attr('transform', "translate("+margins.left+","+margins.top+")");
const radiusScale = d3.scaleSqrt()
.domain([0,250])
.range([0,125]);
var data = [
[
{'id':'1Q19'},
{'id':'pooled','parentId':'1Q19','size':29.5},
{'id':'spv','parentId':'1Q19', 'size':11},
{'id':'single','parentId':'1Q19', 'size':200}
],
[
{'id':'2Q19'},
{'id':'pooled','parentId':'2Q19','size':31},
{'id':'spv','parentId':'2Q19', 'size':15},
{'id':'single','parentId':'2Q19', 'size':171}
],
[
{'id':'3Q19'},
{'id':'pooled','parentId':'3Q19','size':28},
{'id':'spv','parentId':'3Q19', 'size':12},
{'id':'single','parentId':'3Q19', 'size':152}
],
[
{'id':'4Q19'},
{'id':'pooled','parentId':'4Q19','size':25},
{'id':'spv','parentId':'4Q19', 'size':214},
{'id':'single','parentId':'4Q19', 'size':101}
],
];
var colorMap = {
'1Q19':"#e7eef8",
'2Q19':"#e7eef8",
'3Q19':"#e7eef8",
'4Q19':"#e7eef8",
'pooled':"#f6d18b",
'spv':"#366092",
'single':"#95b3d7"
};
var strokeMap = {
"pooled":"#000",
"single":"#000",
"spv":"#fff"
};
for (var j=0; j <(data.length); j++) {
var vData = d3.stratify()(data[j]);
var vLayout = d3.pack().size([250, 250])
.radius(function(d){
return radiusScale(d.data.data.size)
});
var vRoot = d3.hierarchy(vData).sum(function (d) { return d.data.size; });
var vNodes = vRoot.descendants();
vLayout(vRoot);
var thisClass = "circ"+String(j);
var vSlices = graphGroup.selectAll('.'+thisClass).data(vNodes).attr('class',thisClass).enter().append('g');
//console.log(vNodes)
vSlices.append('circle')
.attr('cx', function(d, i) {
return d.x+(j*(j === 3 ? 320 : 310))
})
.attr('cy', function (d) { return d.y; })
.attr('r', function (d) { return d.r; })
.style('fill', function(d) { return colorMap[d.data.id]});
vSlices.append('text')
.attr('x', function(d,i) {return d.x+(j*(j === 3 ? 320 : 310))})
.attr('y', function(d) {return d.y+5})
.attr('text-anchor','middle')
.style('fill', function(d) {return strokeMap[d.data.id]})
.text(function(d) {return d.data.data.size ? d.data.data.size : null});
}
<script src="https://d3js.org/d3.v5.min.js"></script>
请注意,在最后一包中,整个圆的大小不一样(更大)。考虑到包装逻辑,尺寸相同是不可能的。