D3.js Pie-chart: 如何根据我自己的分类自动创建和分配渐变色阶
D3.js Pie-chart: how to automatically create and assign gradual scale of colors based on my own categorization
我希望标题不会太混乱,但我很难清楚地表述这个问题。无论如何我都会试一试。首先,
这是 my pie chart,这是我的数据:
DATA.CSV
voce;categoria;val2015
amministrazione;funzioni;404571081
sociale;funzioni;235251679
territorio e ambiente;funzioni;286164667
viabilità e trasporti;funzioni;144185664
istruzione;funzioni;168774925
cultura;funzioni;55868045
sport;funzioni;27219432
turismo;funzioni;9544845
sviluppo economico;funzioni;14790363
servizi produttivi;funzioni;4334
polizia locale;funzioni;99007202
giustizia;funzioni;12147068
anticipazioni di cassa;rimborso prestiti;304323808
finanziamenti a breve termine;rimborso prestiti;0
prestiti obbligazionari;rimborso prestiti;38842996
quota capitale di debiti pluriennali;rimborso prestiti;0
quota capitale di mutui e prestiti;rimborso prestiti;128508755
spese per conto terzi;altro;232661261
disavanzo di amministrazione;altro;0
这张图表应该显示政府如何将预算分配给不同的职能。每个数据点的特点是:
- "voce": 分配部分预算的职能
- "categoria":每笔费用的macro-category。其中有三个:"funzioni"; "rimborso prestiti";和 "altro"
- "val2015":2015 年分配给该功能的资金绝对值
在我的可视化中,我创建了两个相互重叠的馅饼。 innerPie 将预算分配给三个 macro-categories。 outerPie 将其划分为 19 个不同的功能。
我想为每个 macro-category 分配一种颜色(分别说绿色、红色和蓝色)。然后我想 将类别的所有函数着色为它们 macro-category 颜色的渐变色。
例如,如果 "funzioni" 变绿,那么我希望属于该类别的 12 个函数是从 light-green 到 dark-green 的渐变色。
首先,我想我会手动分配颜色。但是,我找不到可以提供给定颜色的十二种色调的颜色调酒器。
然后我想我会改变颜色。如果 macro-category1 有绿色,那么 function1 将是 light-green,function2 dark-green,function3 light-green 等等。但是有些函数的绝对值为 0(即对于那一年没有预算分配给该职能)。由于这些未出现在饼图中,因此相同颜色的两个函数可能会相邻出现。这是代码:
var color = d3.scale.ordinal()
.domain(["amministrazione", "sociale", "territorio e ambiente", "viabilità e trasporti", "istruzione", "cultura", "sport", "turismo", "sviluppo economico", "servizi produttivi", "polizia locale", "giustizia", "anticipazioni di cassa", "finanziamenti a breve termine", "prestiti obbligazionari", "quota capitale di debiti pluriennali;", "quota capitale di mutui e prestiti", "spese per conto terzi", "disavanzo di amministrazione"])
.range(["rgb(116,196,118)", "rgb(35,139,69)", "rgb(116,196,118)", "rgb(35,139,69)", "rgb(116,196,118)", "rgb(35,139,69)", "rgb(116,196,118)", "rgb(35,139,69)", "rgb(116,196,118)", "rgb(35,139,69)", "rgb(116,196,118)", "rgb(35,139,69)", "rgb(251,106,74)", "rgb(103,0,13)", "rgb(251,106,74)", "rgb(103,0,13)", "rgb(251,106,74)", "rgb(116,196,118)", "rgb(0,68,27)"]);
var colorInner = d3.scale.ordinal()
.domain(["funzioni", "rimborso prestiti", "altro"])
.range(["rgb(0,68,27)", "rgb(203,24,29)" , "rgb(33,113,181)"]);
一个解决方案是对每个 macro-category 使用 three-shades。那会解决问题,但我正在寻找更灵活的解决方案。
由于我计划在我的数据集中添加更多年份,我该如何创建一个函数:
- 将macro-category的颜色作为输入
- 计算该类别中值大于 0 的函数数
- 为函数分配 x 色度
-----编辑-----
既然我创建了一个色阶,我需要根据其类别(对于 macro-color)和索引(对于宏观颜色的阴影)将颜色分配给每个路径。
我尝试了两种方法,但都不起作用。
var greenRange = ["rgb(199,233,192)", "rgb(0,68,27)"];
var redRange = ["rgb(252,187,161)", "rgb(103,0,13)"];
var blueRange = ["rgb(198,219,239)", "rgb(8,48,107)"];
选项 1
function draw () {
//(1) count the number of data points with value > in each category
var countFunzioni=0;
dataset.forEach (function (d) {if (d.categoria=="funzioni" && d.val2015>0) { countFunzioni += 1;}})
var countRimborso=0;
dataset.forEach (function (d) {if (d.categoria=="rimborso prestiti" && d.val2015>0) { countRimborso += 1;}})
var countAltro=0;
dataset.forEach (function (d) {if (d.categoria=="altro" && d.val2015>0) { countAltro += 1;}})
//(2) create a color method for each category based on a the count calculated above and the range I determined
var colorFunzioni = d3.scale.linear()
.domain([0, countFunzioni])
.range(redRange);
var colorRimborso = d3.scale.linear()
.domain([0, countRimborso])
.range(redRange);
var colorAltro = d3.scale.linear()
.domain([0, countAltro])
.range(blueRange);
//draw the chart
chart = d3.select("#visualizationInner")
.append("svg")
.attr("id", "visualization")
.attr("width", w)
.attr("height", h)
.append("g")
.attr("transform", "translate(" + w / 2 + "," + h / 2 + ")");
//draw and color the paths
var path = chart.datum(dataset).selectAll("path")
.data(pie)
.enter()
.append("path")
//(3) return the appropriate color method depending on the datum's category
.attr("fill", function(d, i) {
if (d.categoria=="funzioni") {return colorFunzioni(i);}
else if (d.categoria=="rimborso prestiti") {return colorRimborso(i);}
else if (d.categoria=="altro") {return colorAltro(i);}
})
.style("fill-opacity", 0.75)
.attr("d", arc);
}
选项 2
function draw () {
//(1) same as above
//(2) create a color method that adapts to each category's count and range
var color = d3.scale.linear()
.domain([0, function (d) {
if (d.categoria=="funzioni") {return countFunzioni;}
else if (d.categoria=="rimborso prestiti") {return countRimborso;}
else if (d.categoria=="altro") {return countAltro;}
}])
.range(function (d) {
if (d.categoria=="funzioni") {return greenRange;}
else if (d.categoria=="rimborso prestiti") {return redRange;}
else if (d.categoria=="altro") {return blueRange;}
});
////(3) return the appropriate color method depending on the datum's category
.attr("fill", function(d, i) {return color(i);}
}
选择一种颜色和select浅色和深色,这就是您的范围。然后,您的域是 [0, N]
颜色。例如,对于绿色,我们可以这样做:
var N = 12;
var greenRange = ['#cce0cc', '#001e00'];
var color = d3.scale.linear()
.domain([0, N])
.range(greenRange);
d3.select('body')
.append('div')
.selectAll('.color')
.data(d3.range(N))
.enter()
.append('div')
.attr('class', 'color')
.style('height', '50px')
.style('width', '50px')
.style('background-color', function(d,i){
return color(i);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
用不同的 N 重新 运行 以了解它是如何产生颜色的。
编辑
我想 选项 1 不起作用,因为当您调用 return colorFunzioni(i);
(或其他)时,i
将超出范围。您真正需要的是每种颜色的索引。这是一个快速重构:
var countFunzioni = 0,
countRimborso = 0,
countAltro = 0;
dataset.forEach(function(d){
if (d.categoria=="funzioni" && d.val2015>0){
d.colorIdx = countFunzioni;
countFunzioni += 1;
} else if (d.categoria=="rimborso prestiti" && d.val2015>0){
d.colorIdx = countRimborso;
countRimborso += 1;
} else if (d.categoria=="altro" && d.val2015>0) {
d.colorIdx = countAltro;
countAltro += 1;
}
});
var colors = {};
colors.funzioni = d3.scale.linear()
.domain([0, countFunzioni])
.range(redRange);
colors.rimborso = d3.scale.linear()
.domain([0, countRimborso])
.range(redRange);
colors.altro = d3.scale.linear()
.domain([0, countAltro])
.range(blueRange);
之后:
...
.attr("fill", function(d, i) {
return colors[d.categoria](d.colorIdx);
});
我希望标题不会太混乱,但我很难清楚地表述这个问题。无论如何我都会试一试。首先, 这是 my pie chart,这是我的数据:
DATA.CSV
voce;categoria;val2015
amministrazione;funzioni;404571081
sociale;funzioni;235251679
territorio e ambiente;funzioni;286164667
viabilità e trasporti;funzioni;144185664
istruzione;funzioni;168774925
cultura;funzioni;55868045
sport;funzioni;27219432
turismo;funzioni;9544845
sviluppo economico;funzioni;14790363
servizi produttivi;funzioni;4334
polizia locale;funzioni;99007202
giustizia;funzioni;12147068
anticipazioni di cassa;rimborso prestiti;304323808
finanziamenti a breve termine;rimborso prestiti;0
prestiti obbligazionari;rimborso prestiti;38842996
quota capitale di debiti pluriennali;rimborso prestiti;0
quota capitale di mutui e prestiti;rimborso prestiti;128508755
spese per conto terzi;altro;232661261
disavanzo di amministrazione;altro;0
这张图表应该显示政府如何将预算分配给不同的职能。每个数据点的特点是:
- "voce": 分配部分预算的职能
- "categoria":每笔费用的macro-category。其中有三个:"funzioni"; "rimborso prestiti";和 "altro"
- "val2015":2015 年分配给该功能的资金绝对值
在我的可视化中,我创建了两个相互重叠的馅饼。 innerPie 将预算分配给三个 macro-categories。 outerPie 将其划分为 19 个不同的功能。
我想为每个 macro-category 分配一种颜色(分别说绿色、红色和蓝色)。然后我想 将类别的所有函数着色为它们 macro-category 颜色的渐变色。
例如,如果 "funzioni" 变绿,那么我希望属于该类别的 12 个函数是从 light-green 到 dark-green 的渐变色。
首先,我想我会手动分配颜色。但是,我找不到可以提供给定颜色的十二种色调的颜色调酒器。
然后我想我会改变颜色。如果 macro-category1 有绿色,那么 function1 将是 light-green,function2 dark-green,function3 light-green 等等。但是有些函数的绝对值为 0(即对于那一年没有预算分配给该职能)。由于这些未出现在饼图中,因此相同颜色的两个函数可能会相邻出现。这是代码:
var color = d3.scale.ordinal()
.domain(["amministrazione", "sociale", "territorio e ambiente", "viabilità e trasporti", "istruzione", "cultura", "sport", "turismo", "sviluppo economico", "servizi produttivi", "polizia locale", "giustizia", "anticipazioni di cassa", "finanziamenti a breve termine", "prestiti obbligazionari", "quota capitale di debiti pluriennali;", "quota capitale di mutui e prestiti", "spese per conto terzi", "disavanzo di amministrazione"])
.range(["rgb(116,196,118)", "rgb(35,139,69)", "rgb(116,196,118)", "rgb(35,139,69)", "rgb(116,196,118)", "rgb(35,139,69)", "rgb(116,196,118)", "rgb(35,139,69)", "rgb(116,196,118)", "rgb(35,139,69)", "rgb(116,196,118)", "rgb(35,139,69)", "rgb(251,106,74)", "rgb(103,0,13)", "rgb(251,106,74)", "rgb(103,0,13)", "rgb(251,106,74)", "rgb(116,196,118)", "rgb(0,68,27)"]);
var colorInner = d3.scale.ordinal()
.domain(["funzioni", "rimborso prestiti", "altro"])
.range(["rgb(0,68,27)", "rgb(203,24,29)" , "rgb(33,113,181)"]);
一个解决方案是对每个 macro-category 使用 three-shades。那会解决问题,但我正在寻找更灵活的解决方案。
由于我计划在我的数据集中添加更多年份,我该如何创建一个函数:
- 将macro-category的颜色作为输入
- 计算该类别中值大于 0 的函数数
- 为函数分配 x 色度
-----编辑-----
既然我创建了一个色阶,我需要根据其类别(对于 macro-color)和索引(对于宏观颜色的阴影)将颜色分配给每个路径。 我尝试了两种方法,但都不起作用。
var greenRange = ["rgb(199,233,192)", "rgb(0,68,27)"];
var redRange = ["rgb(252,187,161)", "rgb(103,0,13)"];
var blueRange = ["rgb(198,219,239)", "rgb(8,48,107)"];
选项 1
function draw () {
//(1) count the number of data points with value > in each category
var countFunzioni=0;
dataset.forEach (function (d) {if (d.categoria=="funzioni" && d.val2015>0) { countFunzioni += 1;}})
var countRimborso=0;
dataset.forEach (function (d) {if (d.categoria=="rimborso prestiti" && d.val2015>0) { countRimborso += 1;}})
var countAltro=0;
dataset.forEach (function (d) {if (d.categoria=="altro" && d.val2015>0) { countAltro += 1;}})
//(2) create a color method for each category based on a the count calculated above and the range I determined
var colorFunzioni = d3.scale.linear()
.domain([0, countFunzioni])
.range(redRange);
var colorRimborso = d3.scale.linear()
.domain([0, countRimborso])
.range(redRange);
var colorAltro = d3.scale.linear()
.domain([0, countAltro])
.range(blueRange);
//draw the chart
chart = d3.select("#visualizationInner")
.append("svg")
.attr("id", "visualization")
.attr("width", w)
.attr("height", h)
.append("g")
.attr("transform", "translate(" + w / 2 + "," + h / 2 + ")");
//draw and color the paths
var path = chart.datum(dataset).selectAll("path")
.data(pie)
.enter()
.append("path")
//(3) return the appropriate color method depending on the datum's category
.attr("fill", function(d, i) {
if (d.categoria=="funzioni") {return colorFunzioni(i);}
else if (d.categoria=="rimborso prestiti") {return colorRimborso(i);}
else if (d.categoria=="altro") {return colorAltro(i);}
})
.style("fill-opacity", 0.75)
.attr("d", arc);
}
选项 2
function draw () {
//(1) same as above
//(2) create a color method that adapts to each category's count and range
var color = d3.scale.linear()
.domain([0, function (d) {
if (d.categoria=="funzioni") {return countFunzioni;}
else if (d.categoria=="rimborso prestiti") {return countRimborso;}
else if (d.categoria=="altro") {return countAltro;}
}])
.range(function (d) {
if (d.categoria=="funzioni") {return greenRange;}
else if (d.categoria=="rimborso prestiti") {return redRange;}
else if (d.categoria=="altro") {return blueRange;}
});
////(3) return the appropriate color method depending on the datum's category
.attr("fill", function(d, i) {return color(i);}
}
选择一种颜色和select浅色和深色,这就是您的范围。然后,您的域是 [0, N]
颜色。例如,对于绿色,我们可以这样做:
var N = 12;
var greenRange = ['#cce0cc', '#001e00'];
var color = d3.scale.linear()
.domain([0, N])
.range(greenRange);
d3.select('body')
.append('div')
.selectAll('.color')
.data(d3.range(N))
.enter()
.append('div')
.attr('class', 'color')
.style('height', '50px')
.style('width', '50px')
.style('background-color', function(d,i){
return color(i);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
用不同的 N 重新 运行 以了解它是如何产生颜色的。
编辑
我想 选项 1 不起作用,因为当您调用 return colorFunzioni(i);
(或其他)时,i
将超出范围。您真正需要的是每种颜色的索引。这是一个快速重构:
var countFunzioni = 0,
countRimborso = 0,
countAltro = 0;
dataset.forEach(function(d){
if (d.categoria=="funzioni" && d.val2015>0){
d.colorIdx = countFunzioni;
countFunzioni += 1;
} else if (d.categoria=="rimborso prestiti" && d.val2015>0){
d.colorIdx = countRimborso;
countRimborso += 1;
} else if (d.categoria=="altro" && d.val2015>0) {
d.colorIdx = countAltro;
countAltro += 1;
}
});
var colors = {};
colors.funzioni = d3.scale.linear()
.domain([0, countFunzioni])
.range(redRange);
colors.rimborso = d3.scale.linear()
.domain([0, countRimborso])
.range(redRange);
colors.altro = d3.scale.linear()
.domain([0, countAltro])
.range(blueRange);
之后:
...
.attr("fill", function(d, i) {
return colors[d.categoria](d.colorIdx);
});