D3 스택 막대 그래프, 각 스택은 다른 그룹에 의해 다른 색상 설정,
문제
나는 D3 (v5)에서 누적 막대 그래프를 가져 와서 다른 그룹에 대해 개별적으로 색상이 지정된 막대를 얻으려고합니다 (그림 1). 각 스택은 그림 2에 따라 다릅니다.


그림 3에서 스택 색상을 얻는 방법을 찾을 수 없습니다 (즉, 그룹 색상의 다른 음영이 다른 스택 높이에 따라 달라지기를 원합니다) 예제 (다른 그룹이 다른 색상이되기를 원합니다. 그들이 여기에 있기 때문에).

내가 제공 한 코드 예제에는 두 가지 데이터 세트가 있습니다. 데이터를 활용하는 데 도움이되는 간단한 세트 :
Animal,Group,A,B,C,D
Dog,Domestic,10,10,20,5
Cat,Domestic,20,5,10,10
Mouse,Pest,75,5,35,0
Lion,Africa,5,5,30,25
Elephant,Africa,15,15,20,20
Whale,Marine,35,20,10,45
Shark,Marine,45,55,0, 60
Fish,Marine,20, 5,30,10
그리고 제가 실제로 사용하려는 더 큰 세트 입니다. 다음은 bl.ocks.org개발하려는 코드입니다.
// set the dimensions and margins of the graph
const margin = {
top: 90,
right: 20,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 960 - margin.top - margin.bottom;
const svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
const y = d3.scaleBand()
.range([0, height])
.padding(0.1);
const x = d3.scaleLinear()
.range([0, width]);
const z = d3.scaleOrdinal()
.range(["none", "lightsteelblue", "steelblue", "darksteelblue"]);
d3.csv("https://gist.githubusercontent.com/JimMaltby/844ca313589e488b249b86ead0d621a9/raw/f328ad6291ffd3c9767a2dbdba5ce8ade59a5dfa/TimeBarDummyFormat.csv", d3.autoType, (d, i, columns) => {
var i = 3;
var t = 0;
for (; i < columns.length; ++i)
t += d[columns[i]] = +d[columns[i]];
d.total = t;
return d;
}
).then(function(data) {
const keys = data.columns.slice(3); // takes the column names, ignoring the first 3 items = ["EarlyMin","EarlyAll", "LateAll", "LateMax"]
// List of groups = species here = value of the first column called group -> I show them on the X axis
const Groups = d3.map(data, d => d.Group);
y.domain(data.map(d => d.Ser));
x.domain([2000, d3.max(data, d => d.total)]).nice();
z.domain(keys);
svg.append("g")
.selectAll("g")
.data(d3.stack().keys(keys)(data))
.enter().append("g")
.attr("fill", d => z(d.key)) //Color is assigned here because you want everyone for the series to be the same color
.selectAll("rect")
.data(d => d)
.enter()
.append("rect")
.attr("y", d => y(d.data.Ser))
.attr("x", d => x(d[0]))
.attr("height", y.bandwidth())
.attr("width", d => x(d[1]) - x(d[0]));
svg.append("g")
.attr("transform", "translate(0,0)")
.call(d3.axisTop(x));
svg.append("g")
.call(d3.axisLeft(y));
});
.bar {
fill: rgb(70, 131, 180);
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<div id="my_dataviz"></div>
JSFiddle : https://jsfiddle.net/yq7bkvdL/
내가 시도한 것
나는 단순한 것을 놓치고 있다고 생각하지만 나는 코딩 멍청이이고 코딩이 매우 초보적이므로 해결할 수 없습니다.
나는 채우기 attr
를 잘못된 위치에 배치하고 있다고 생각 합니다. 또는의 중첩 / 계층 적 데이터에서 키를 선택하는 방법을 이해하지 못합니다 d3.stack
.
나는 여러 가지를 시도했지만 모두 성공하지 못했습니다.
1. 색상 배열 forEach
"키"및 "그룹"값 / 이름 을 반복 (와 함께 ) 하여 색상 배열을 만드는 함수를 작성 하고 연결하여 함께 사용할 수있는 배열을 만듭니다. d3 스케일 (순서)을 사용하여 올바른 색상을 선택합니다. 예를 들어 첫 번째 데이터 세트를 사용하면 ColoursID
[DomesticA, DomesticB, DomesticC, DomesticD,PestA, PestB...]
다음의 색상과 일치 하는 배열 을 생성합니다 .ColourS
["grey", "lightgreen", "green", "darkgreen", "yellow", ...]
아래는이를위한 시도와 주석 처리 된 다양한 다른 탐색입니다.
// List of groups = species here = value of the first column called group -> I show them on the X axis
const stack = d3.stack().keys(stackKeys)(data);
//const Groups = d3.map(data, d => d.Group);
//const ColourID = d3.map(data, d => d.Group && d.key);
// const stackID = stack.data // //stack[3].key = "D" // [2][6].data.Group = "Marine"
// const Test1 = Object.entries(stack).forEach(d => d.key);
const stackB = stack.forEach(function(d,i,j){
//var a = Object.keys(d)//= list of 3rd Level keys "0,..7,key,index"
//var a = Object.keys(d).forEach((key) => key) "undefined"
//var a = d.key //= "D" for all
d.forEach(function(d,i){
//var a = d.keys // = "function keys{} ([native code])"
//var a = Object.keys(d)
//var a = Object.keys(d) //= list of 2nd Level keys "0,1,data"
var b = data[i]["Group"]
d.forEach(function(d){
//var a = [d]["key"] // = "undefined"
//var a = Object.keys(d).forEach((key) => d[key]) // = "undefined"
var a = Object.keys(d) //= ""
// var a = d.keys //= "undefined"
data[i]["colourID"] = b + " a" + "-b " + a //d.key
})
})
});
console.log(stack)
svg.append("g")
.selectAll("g")
.data(stack)
.enter().append("g")
//.attr("fill", d => z(d.data.Group) ) //Color is assigned here because you want everyone for the series to be the same color
.selectAll("rect")
.data(d => d)
.enter().append("rect")
.attr("fill", d => colourZ(d.data.colourID)) //Color is assigned here because you want each Group to be a different colour **how do you vary the blocks?**
.attr("y", d => y(d.data.Animal) ) //uses the Column of data Animal to seperate bars
.attr("x", d=> x(d[0]) ) //
.attr("height", y.bandwidth() ) //
.attr("width", d=> x(d[1]) - x(d[0])); //
VizHub 코드 : https://vizhub.com/JimMaltby/373f1dbb42ad453787dc0055dee7db81?file=index.js
2. 두 번째 색상 스케일을 만듭니다.
여기에있는 조언 ( d3.js- 누적 막대 차트의 하나의 막대에 다른 색상 추가)을 사용하고 다음 코드를 추가하여 if 함수를 추가하여 다른 색상 스케일을 선택합니다.
//-----------------------------BARS------------------------------//
// append the rectangles for the bar chart
svg.append("g")
.selectAll("g")
.data(stack)
.enter().append("g")
//Color is assigned here because you want everyone for the series to be the same color
.selectAll("rect")
.data(d => d)
.enter().append("rect")
.attr("fill", d =>
d.data.Group == "Infrastructure"
? z2(d.key)
: z(d.key))
//.attr("class", "bar")
.attr("y", d => y(d.data.Ser) ) //Vert2Hor **x to y** **x(d.data.Ser) to y(d.data.Ser)**
.attr("x", d=> x(d[0]) ) //Vert2Hor **y to x** **y(d[1]) to x(d[0])**
.attr("height", y.bandwidth() ) //Vert2Hor **"width" to "height"** **x.bandwidth to y.bandwidth**
.attr("width", d=> x(d[1]) - x(d[0])); //Vert2Hor **"height" to "width"** **y(d[0]) - y(d[1]) to x(d[1]) - x(d[0])**
VizHub 코드
3. 채우기에 큰 IF 함수.
이것이 해결책이라면 a 에 대한 조언을 주시면 감사하겠습니다 . 작동하게 만들고 b. 더 효율적인 방법을 가지고
여기서 다시 "스택"데이터 배열의 "키"를 선택하는 데 어려움을 겪고있는 것 같습니다. 여기서 코드에서 키를 선택하는 다른 방법을 시도했지만 성공하지 못했음을 알 수 있습니다.
.attr("fill", function(d,i, j) {
if (d.data.Group === "Domestic") {
if (d.key === "A") { return "none"}
else if (d.key === "B") { return "lightblue"}
else if (d.key === "C") { return "blue"}
else if (d.key === "D") { return "darkblue"}
else { return "forestgreen"}
}
else if (d.data.Group === "Pest") {
if (d.key === "A") { return "yellow"}
else if (d.key === "B") { return "lightorange"}
else if (d.key === "C") { return "orange"}
else if (d.key === "D") { return "darkorange"}
else { return "Brown"} //"yellow", "lightorange", "orange", ""
}
else if (d.data.Group === "Africa") {
if (Object.keys(root.data) === 1) { return "grey"}
else if (d.key === "B") { return "lightred"}
else if (d.key === "C") { return "red"}
else if (d.key === "D") { return "darkred"}
else { return "pink"}
}
else if (d.data.Group == "Marine") {
if (stackKeys == "A") { return "lightgrey"}
else if (stackKeys[d] == "B") { return "lightblue"}
else if (stackKeys[i] == "C") { return "blue"}
else if (stackKeys[3] == "D") { return "darkblue"}
else { return "steelblue"}
}
else { return "black" }
;})
Viz Hub의 코드
답변
바 길이가 더 작은 경우 바 색상을 약간 변경 하려면 동일 fill-opacity
하게 사용 하고 유지할 수 있습니다 fill
! 이렇게하면 값이 더 밝 으면 색상이 덜 뚜렷하고 더 밝아집니다.
opacity
range 로 새 척도 를 만드십시오 [0.3, 1]
. 불투명도가 0이면 막대가 보이지 않고 일반적으로 원하지 않기 때문에 0.3을 선택했습니다. d.height
막대의 전체 보이는 높이를 나타 내기 위해 별도의 값 을 추가했습니다 . 이는 시작과는 별개이지만 d.Min + d.All + d.Max
. 이제 모든 막대에 속성을 적용하기 만하면됩니다.
범위를으로 설정하고 도메인에 [0, 1]
사용하지 않도록 선택할 수 있습니다. d3.extent
이는 아마도 유사한 결과로 이어질 수 있지만 사고 실험으로 발견 할 수있는 몇 가지 차이점이 있습니다.
지금은 fill-opacity
모든 막대에 속성이 설정되어 있습니다. 따라서 동일한 스택의 막대는 동일한 fill-opacity
값을 갖습니다. 그러나 이것은 전적으로 선택 사항이며 고유 한 값을 적용 할 수도 있습니다.
// set the dimensions and margins of the graph
const margin = {
top: 90,
right: 20,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 960 - margin.top - margin.bottom;
const svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
const y = d3.scaleBand()
.range([0, height])
.padding(0.1);
const x = d3.scaleLinear()
.range([0, width]);
const z = d3.scaleOrdinal()
.range(["none", "lightsteelblue", "steelblue", "darksteelblue"]);
const opacity = d3.scaleLinear()
.range([0.3, 1]);
d3.csv("https://gist.githubusercontent.com/JimMaltby/844ca313589e488b249b86ead0d621a9/raw/f328ad6291ffd3c9767a2dbdba5ce8ade59a5dfa/TimeBarDummyFormat.csv", d3.autoType, (d, i, columns) => {
var i = 3;
var t = 0;
for (; i < columns.length; ++i)
t += d[columns[i]] = +d[columns[i]];
d.total = t;
d.height = d.total - d.Start;
return d;
}
).then(function(data) {
const keys = data.columns.slice(3); // takes the column names, ignoring the first 3 items = ["EarlyMin","EarlyAll", "LateAll", "LateMax"]
// List of groups = species here = value of the first column called group -> I show them on the X axis
const Groups = d3.map(data, d => d.Group);
y.domain(data.map(d => d.Ser));
x.domain([2000, d3.max(data, d => d.total)]).nice();
z.domain(keys);
opacity.domain(d3.extent(data, d => d.height));
console.log(opacity.domain());
svg.append("g")
.selectAll("g")
.data(d3.stack().keys(keys)(data))
.enter().append("g")
.attr("fill", d => z(d.key))
.selectAll("rect")
.data(d => d)
.enter()
.append("rect")
.attr("y", d => y(d.data.Ser))
.attr("x", d => x(d[0]))
.attr("height", y.bandwidth())
.attr("width", d => x(d[1]) - x(d[0]))
.attr("fill-opacity", d => opacity(d.data.height));
svg.append("g")
.attr("transform", "translate(0,0)")
.call(d3.axisTop(x));
svg.append("g")
.call(d3.axisLeft(y));
});
.bar {
fill: rgb(70, 131, 180);
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<div id="my_dataviz"></div>
편집 : 그룹별로 막대의 색상을 지정하고 싶다면 동일한 논리를 사용하지만 몇 가지 조정을 수행합니다.
우선, 나는 다른 그룹을 강조하기 위해 여전히 사용하는 z
처리로 전환 하고 색상에 fill-opacity
새로운 서수 척도 group
를 사용했습니다 . 그 척도의 핵심은 단순히 기존 필드 d.Group
입니다.
// set the dimensions and margins of the graph
const margin = {
top: 90,
right: 20,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 960 - margin.top - margin.bottom;
const svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
const y = d3.scaleBand()
.range([0, height])
.padding(0.1);
const x = d3.scaleLinear()
.range([0, width]);
const z = d3.scaleOrdinal()
.range([0.25, 0.5, 0.75, 1]);
const group = d3.scaleOrdinal()
.range(["darkgreen", "darkred", "steelblue", "purple"]);
d3.csv("https://gist.githubusercontent.com/JimMaltby/844ca313589e488b249b86ead0d621a9/raw/f328ad6291ffd3c9767a2dbdba5ce8ade59a5dfa/TimeBarDummyFormat.csv", d3.autoType, (d, i, columns) => {
var i = 3;
var t = 0;
for (; i < columns.length; ++i)
t += d[columns[i]] = +d[columns[i]];
d.total = t;
return d;
}
).then(function(data) {
const keys = data.columns.slice(3); // takes the column names, ignoring the first 3 items = ["EarlyMin","EarlyAll", "LateAll", "LateMax"]
y.domain(data.map(d => d.Ser));
x.domain([2000, d3.max(data, d => d.total)]).nice();
z.domain(keys);
group.domain(data.map(d => d.Group));
svg.append("g")
.selectAll("g")
.data(d3.stack().keys(keys)(data))
.enter().append("g")
.attr("fill-opacity", d => z(d.key))
.selectAll("rect")
.data(d => d)
.enter()
.append("rect")
.attr("y", d => y(d.data.Ser))
.attr("x", d => x(d[0]))
.attr("height", y.bandwidth())
.attr("width", d => x(d[1]) - x(d[0]))
.attr("fill", d => group(d.data.Group));
svg.append("g")
.attr("transform", "translate(0,0)")
.call(d3.axisTop(x));
svg.append("g")
.call(d3.axisLeft(y));
});
.bar {
fill: rgb(70, 131, 180);
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<div id="my_dataviz"></div>
편집 2 : 색상을 직접 지정하려면 색상에 대한 키 맵을 사용합니다.
// set the dimensions and margins of the graph
const margin = {
top: 90,
right: 20,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 960 - margin.top - margin.bottom;
const svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
const y = d3.scaleBand()
.range([0, height])
.padding(0.1);
const x = d3.scaleLinear()
.range([0, width]);
const z = d3.scaleOrdinal()
.range([0.25, 0.5, 0.75, 1]);
const group = d3.scaleOrdinal()
.range([
{ Start: "none", Min: "lightgreen", All: "green", Max: "darkgreen" },
{ Start: "none", Min: "indianred", All: "red", Max: "darkred" },
{ Start: "none", Min: "lightsteelblue", All: "steelblue", Max: "darksteelblue" }
]);
d3.csv("https://gist.githubusercontent.com/JimMaltby/844ca313589e488b249b86ead0d621a9/raw/f328ad6291ffd3c9767a2dbdba5ce8ade59a5dfa/TimeBarDummyFormat.csv", d3.autoType, (d, i, columns) => {
var i = 3;
var t = 0;
for (; i < columns.length; ++i)
t += d[columns[i]] = +d[columns[i]];
d.total = t;
return d;
}
).then(function(data) {
const keys = data.columns.slice(3); // takes the column names, ignoring the first 3 items = ["EarlyMin","EarlyAll", "LateAll", "LateMax"]
y.domain(data.map(d => d.Ser));
x.domain([2000, d3.max(data, d => d.total)]).nice();
z.domain(keys);
group.domain(data.map(d => d.Group));
svg.append("g")
.selectAll("g")
.data(d3.stack().keys(keys)(data))
.enter().append("g")
.each(function(e) {
d3.select(this)
.selectAll("rect")
.data(d => d)
.enter()
.append("rect")
.attr("y", d => y(d.data.Ser))
.attr("x", d => x(d[0]))
.attr("height", y.bandwidth())
.attr("width", d => x(d[1]) - x(d[0]))
.attr("fill", d => group(d.data.Group)[e.key]);
});
svg.append("g")
.attr("transform", "translate(0,0)")
.call(d3.axisTop(x));
svg.append("g")
.call(d3.axisLeft(y));
});
.bar {
fill: rgb(70, 131, 180);
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<div id="my_dataviz"></div>