在下面的代码片段中,我尝试使用对数刻度使较小的条可见,但这导致上部条变小。我是D3的新手,真的很难使它工作。我想使我的条形图可见,以便标签在条形图区域内可见,现在它与其他较小的条形图标签重叠。
var data = [{"month":"JAN","Insolvency":"1","Operating Company":"1","SPV / Asset Backed":"0","Operational Company":"0","TBD":"0"},{"month":"FEB","Insolvency":"1","Operating Company":"11","SPV / Asset Backed":"9","TBD":"1","Operational Company":"0"},{"month":"MAR","Insolvency":"3","Operating Company":"44","SPV / Asset Backed":"33","TBD":"8","Operational Company":"0"},{"month":"APR","Insolvency":"3","Operating Company":"27","SPV / Asset Backed":"31","TBD":"3","Operational Company":"0"},{"month":"MAY","Operating Company":"2","SPV / Asset Backed":"5","TBD":"1","Operational Company":"0","Insolvency":"0"},{"month":"JUL","Insolvency":"44","Operating Company":"9","TBD":"1","SPV / Asset Backed":"0","Operational Company":"0"},{"month":"AUG","Operating Company":"24","SPV / Asset Backed":"30","TBD":"2","Operational Company":"0","Insolvency":"0"},{"month":"SEP","Insolvency":"189","Operating Company":"74","SPV / Asset Backed":"20","Operational Company":"0","TBD":"0"},{"month":"OCT","Insolvency":"3","Operating Company":"16","SPV / Asset Backed":"54","Operational Company":"0","TBD":"0"},{"month":"NOV","Insolvency":"1","Operating Company":"3","SPV / Asset Backed":"25","TBD":"3","Operational Company":"0"},{"month":"DEC","Insolvency":"6","Operating Company":"0","SPV / Asset Backed":"0","Operational Company":"0","TBD":"0"}];
let xData = d3.keys(data[0]);
const yData = xData.shift();
var div = d3.select("body").append("div").attr("class", "tooltip")
.style("opacity", 0);
var margin = {
top : 40,
right : 50,
bottom : 30,
left : 50
}, width = 500, height = 300, padding = 100;
var x = d3.scale.ordinal().rangeRoundBands([ 0, width ], .05);
var y = d3.scale.log().range([ height, 0.5 ]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var y_axis = d3.svg.axis().scale(y).orient("left").ticks(0, ".1s")
.innerTickSize(-width).tickPadding(10);
var svg = d3
.select("#ashu")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
var dataIntermediate = xData.map(function(c) {
return data.map(function(d, yData) {
return {
x : d[Object.keys(d)[0]],
y : +d[c]
};
});
});
var totals = d3.nest().key(function(d) {
return d.health;
}).rollup(function(d) {
return d3.sum(d, function(g) {
return d3.sum(d3.values(g));
});
}).entries(data);
var dataStackLayout = d3.layout.stack()(dataIntermediate);
x.domain(dataStackLayout[0].map(function(d) {
return d.x;
}));
var total = 0;
var maximumY = d3.max(dataStackLayout[dataStackLayout.length - 1],
function(d) {
return (d.y + d.y0);
});
y.domain([ 0.5, maximumY ]).base(2).nice().clamp(true);
var layer1 = svg.selectAll(".stack1").data(dataStackLayout).enter()
.append("g").attr("class", "stack1").style("fill",
function(d, i) {
return color(i);
}).attr("transform",
"translate(5,0)");
layer1.selectAll("rect").data(function(d) {
return d;
}).enter().append("rect").attr("x", function(d) {
return x(d.x);
}).attr("y", function(d) {
return y(d.y + d.y0);
}).attr("height", function(d) {
return y(d.y0) - y(d.y + d.y0);
}).attr("width", x.rangeBand()-2);
layer1.selectAll("text").data(totals).enter().append("text").text(
function(d) {
return d.values;
}).attr("x", function(d, i) {
return x(d.key) + (x.rangeBand()) / 2;
}).attr("y", function(d) {
return y(d.values + 10);
}).attr("dy", "0.35em").attr("text-anchor", "middle").style("fill",
"#1a8cff");
var layer2 = svg.selectAll(".stack2").data(dataStackLayout).enter()
.append("g").attr("class", "stack2").style("fill", "black");
layer2.selectAll(".shadow").data(function(d) {
return d;
}).enter().append("text")
.attr("class", "shadow")
.text(function(d) {
return (d.y === 0 ? '':d.y);
}).attr("x", function(d, i) {
return x(d.x) + (x.rangeBand()) / 2;
}).attr("y", function(d) {
return y(d.y0 + (d.y / 2));
}).attr("dy", "0.35em").attr("text-anchor", "middle").style("fill",
"black");
svg.append("g").attr("class", "axis").attr("transform",
"translate(0," + (height + 5) + ")").call(xAxis);
svg.append("g").attr("class", "axis").attr("transform",
"translate(15px,0)").call(y_axis);
var legend = svg.selectAll(".legend").data(color.domain().slice())
.enter().append("g").attr("class", "legend").attr(
"transform",
function(d, i) {
return "translate(0," + Math.abs((i - 8) * 20)
+ ")";
});
legend.append("rect").attr("x", width + 10).attr("width", 18).attr(
"height", 18).style("fill", color);
legend.append("text").attr("class", "legendText").attr("x",
width + 32).attr("y", 10).attr("dy", ".35em").style(
"text-anchor", "start").text(function(d, i) {
return xData[i];
});
.textRect {
fill: grey;
stroke: none;
}
text {
font-family: 'Raleway', sans-serif;
font-weight: bold;
font-size: 13px;
}
path {
color: green;
}
div.tooltip {
position: absolute;
text-align: center;
padding: 7px;
font: 12px sans-serif;
background: #333333;
border: 0px;
border-radius: 8px;
pointer-events: none;
color: white;
}
.axis path, .axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
.grid .tick {
stroke: lightgrey;
stroke-opacity: 0.7;
shape-rendering: crispEdges;
}
.grid path {
stroke-width: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="ashu" style="width: 700px; height:400px;"></div>
答案 0 :(得分:0)
您的旧代码似乎对新数据不符合的数据格式做了一些假设。在旧代码中,数字被列为字符串。另外,health
(month
)字段是数据集中的第一个字段,而不是第二个字段。
以下是纠正这些问题的一种方法,但不是唯一的方法。我认为这可以使您朝正确的方向前进。
旧答案:
我所做的只是:1)将month
移动到每个对象的第一个位置,并2)在+
函数中为dataIntermediate
值添加一个y
运算符
编辑:
根据您的评论,我编辑了代码段以容忍“月”字段的位置。
这些问题与JavaScript和D3一样多。您可以在图表的y轴上使用任何想要的字段,但是必须告诉D3它的内容。
我已经更新了代码段,以手动将您的yData
变量设置为"month"
。 D3不对数据的结构做任何假设,但是您的基础JavaScript遵循以下几行:
let xData = d3.keys(data[0]);
const yData = xData.shift();
对于我的代码段中的xData
,这些函数在数据中循环以收集所有键,然后过滤掉所有重复项,然后过滤出month
。
关于每个对象是否必须具有每个字段:从技术上讲,并不是出于D3的缘故,但同时D3不会假定它存在或不存在。这完全取决于您如何编码图表。因此,简短的答案是D3不会对您的数据做出假设,现在您的代码正在告诉D3尝试在图形上为不存在的数据绘制点。
如果您查看代码段:
var dataIntermediate = xData.map(function(c) {
return data.map(function(d, yData) {
return {
x: d[Object.keys(d)[0]],
y: +d[c]
};
});
});
您正在为data
和xData
中列出的每个密钥浏览yData
,并尝试访问data
。但是,对于数据中的某些点,键/值对不一定存在。这意味着您最终将获得传递给D3的数据,如下所示:
[
{x: "7", y: 4, y0: 9}
{x: "3", y: NaN, y0: 46}
{x: "6", y: 27, y0: NaN}
{x: "54", y: 15, y0: 168}
]
D3不知道如何在图形上平移和绘制NaN
。我相信您可以省略数据中的某些字段,但是您需要遍历代码并确保您不尝试处理不存在的数据并将其传递给D3。您还需要仔细检查并确保您不尝试访问诸如工具提示之类的字段。
var data = [
{
"Operating Company" : "7",
"month" : "March",
"Insolvency" : "2",
"SPV / Asset Backed" : "4",
"Operational Company" : "0",
"TBD": "0"
},
{
"Operating Company" : "3",
"month" : "July",
"Insolvency" : "43",
"TBD" : "1",
"Operational Company" : "0",
"SPV / Asset Backed" : "0",
}, {
"Operating Company" : "6",
"month" : "August",
"SPV / Asset Backed" : "27",
"Operational Company" : "0",
"Insolvency" : "0",
"TBD": "0"
},
{
"Operating Company" : "54",
"month" : "September",
"Insolvency" : "114",
"Operational Company" : "1",
"SPV / Asset Backed" : "15",
"TBD": "0"
}
];
let yData = "month";
const xData = data.reduce((accumulator, currentValue) => {
keys = Object.keys(currentValue);
keys.forEach(k => {
accumulator.push(k);
});
return accumulator;
}, [])
.filter((value, index, self) => self.indexOf(value) === index)
.filter(value => value !== "month");
var margin = {
top: 20,
right: 50,
bottom: 30,
left: 50
},
width = 400,
height = 300,
padding = 100;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .05);
var y = d3.scale.linear().range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var y_axis = d3.svg.axis().scale(y).orient("left").ticks(6).innerTickSize(-width)
.tickPadding(10);
var svg = d3.select("#ashu").append("svg").attr("width",
"100%").attr("height",
"100%").append("g").attr(
"transform",
"translate(" + margin.left + "," + margin.top + ")");
var dataIntermediate = xData.map(function(c) {
return data.map(function(d, yData) {
return {
x: d[Object.keys(d)[0]],
y: +d[c]
};
});
});
var dataStackLayout = d3.layout.stack()(dataIntermediate);
x.domain(dataStackLayout[0].map(function(d) {
return d.x;
}));
var total = 0;
var maximumY = d3.max(dataStackLayout[dataStackLayout.length - 1], function(d) {
return (d.y + d.y0);
});
y.domain([0, maximumY]).nice();
var layer1 = svg.selectAll(".stack1").data(dataStackLayout).enter()
.append("g").attr("class", "stack").style("fill",
function(d, i) {
return color(i);
});
var layer2 = svg.selectAll(".stack2").data(dataStackLayout).enter()
.append("g")
.attr("class", "stack")
.style("fill", "black");
layer1.selectAll("rect")
.data(function(d) {
return d;
})
.enter()
.append("rect")
.attr("x", function(d) {
return x(d.x);
})
.attr("y", function(d) {
return y(d.y + d.y0);
})
.attr("height", function(d) {
return y(d.y0) - y(d.y + d.y0);
})
.attr("width", x.rangeBand())
.on("mouseover", function(d) {
// this code places your hover text at the bottom of the page
// it is not required
// you can modify it as you see fit, and even delete it
div.transition()
.duration(200)
.style("opacity", .9);
div.html("Total : " + (d.y + d.y0))
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
// this code removes the text on the bottom of the screen when
// you move the mouse off the bar
div.transition()
.duration(500)
.style("opacity", 0);
});
// this code places your text labels on the bars
// it is not required
// you can modify it as you see fit, and even just delete it
layer2.selectAll("text")
.data(function(d) {
return d;
})
.enter()
.append("text")
.text(function(d) {
return d.y;
})
.attr("x", function(d, i) {
return x(d.x) + (x.rangeBand())/2;
})
.attr("y", function(d) {
return y(d.y0 + (d.y/2));
})
.attr("dy", "0.35em")
.attr("text-anchor", "middle")
.style("fill", "black");
svg.append("g").attr("class", "axis").attr("transform",
"translate(0," + height + ")").call(xAxis);
svg.append("g").attr("class", "y axis").attr("transform", "translate(0,0)").call(y_axis);
var legend = svg.selectAll(".legend")
.data(color.domain().slice())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(0," + Math.abs((i - 8) * 20) + ")";
});
legend.append("rect")
.attr("x", width + 10)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width + 32)
.attr("y", 10)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d, i) {
return xData[i];
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="ashu" style="width: 700px; height:400px;"></div>
答案 1 :(得分:0)
我已将代码转换为D3v5版本。
无需转换数据,您可以使用对象。
我不知道您对layer1
和layer2
想要什么。
您必须添加mouseover
和mouseout
。
具有对数Y轴。使用2对数并不常见,因此我使用10对数。 但是现在破产情况看起来非常大。
对于JavaScript(health
重命名为month
)
var data = [{
"month": "JAN",
"INSOLVENCIES": 1,
"SPV/ASSETBACKED": 67,
"OPERATINGCOMPANIES": 13,
"Bank": 15
}, {
"month": "FEB",
"INSOLVENCIES": 60,
"SPV/ASSETBACKED": 9,
"OPERATINGCOMPANIES": 20,
"Bank": 5
}, {
"month": "MAR",
"INSOLVENCIES": 40,
"SPV/ASSETBACKED": 22,
"OPERATINGCOMPANIES": 21,
"Bank": 99
}, {
"month": "APR",
"INSOLVENCIES": 60,
"SPV/ASSETBACKED": 1,
"OPERATINGCOMPANIES": 99,
"Bank": 90
}, {
"month": "MAY",
"INSOLVENCIES": 2,
"SPV/ASSETBACKED": 27,
"OPERATINGCOMPANIES": 43,
"Bank": 82
}, {
"month": "JUN",
"INSOLVENCIES": 17,
"SPV/ASSETBACKED": 52,
"OPERATINGCOMPANIES": 79,
"Bank": 9
}, {
"month": "JUL",
"INSOLVENCIES": 37,
"SPV/ASSETBACKED": 24,
"OPERATINGCOMPANIES": 35,
"Bank": 51
}, {
"month": "AUG",
"INSOLVENCIES": 16,
"SPV/ASSETBACKED": 17,
"OPERATINGCOMPANIES": 53,
"Bank": 38
}, {
"month": "SEP",
"INSOLVENCIES": 15,
"SPV/ASSETBACKED": 32,
"OPERATINGCOMPANIES": 5,
"Bank": 31
}];
let xKey = "month";
let keys = d3.keys(data[0]).filter( e => e != xKey );
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var margin = {
top: 20,
right: 50,
bottom: 30,
left: 50
},
width = 400,
height = 300,
padding = 100;
var x = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(.05);
var y = d3.scaleLog().range([height, 0]);
var color = d3.scaleOrdinal(d3.schemeCategory10);
var xAxis = d3.axisBottom().scale(x);
var y_axis = d3.axisLeft().scale(y).ticks(6); //.innerTickSize(-width).tickPadding(10);
var svg = d3.select("#ashu")
.append("svg")
.attr("width", "100%")
.attr("height", "100%");
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var dataStackLayout = d3.stack().keys(keys)(data);
dataStackLayout[0].forEach(e => { e[0]=0.1; }); // do not start at 0
x.domain(dataStackLayout[0].map(function(d) { return d.data[xKey]; }));
var maximumY = d3.max(dataStackLayout[dataStackLayout.length - 1], function(d) { return d[1]; });
y.domain([0.1, maximumY]).base(10).nice();
g.append("g")
.selectAll("g")
.data(dataStackLayout)
.enter().append("g")
.attr("fill", function(d) { return color(d.key); })
.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("x", function(d) { return x(d.data[xKey]); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width", x.bandwidth());
g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
g.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0,0)")
.call(y_axis);
var legend = g.selectAll(".legend")
.data(color.domain().slice())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + Math.abs((i - 8) * 20) + ")"; });
legend.append("rect")
.attr("x", width + 10)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width + 32)
.attr("y", 10)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d, i) { return keys[i]; });
<script src="https://d3js.org/d3.v5.min.js"></script>
<div id="ashu" style="width: 700px; height:400px;"></div>
修改
我没有使用对数刻度,而是添加了缩放功能。翻译是在range
中完成的,这让我感到困惑。我找到了所需的方程式,但需要提示在哪里应用https://stackoverflow.com/a/44359905/9938317的转换限制。
当栏的高度足够高时,您必须有条件地放置标签;或者在不放置标签时,使用工具提示。
添加工具提示有点困惑。我不知道一种获取附加到父节点的数据的方法,因此我将组的键复制到了各个条形区域。 (也许https://stackoverflow.com/a/17459746/9938317是解决方案)
var data = [{
"month": "JAN",
"INSOLVENCIES": 1,
"SPV/ASSETBACKED": 67,
"OPERATINGCOMPANIES": 13,
"Bank": 15
}, {
"month": "FEB",
"INSOLVENCIES": 60,
"SPV/ASSETBACKED": 9,
"OPERATINGCOMPANIES": 20,
"Bank": 5
}, {
"month": "MAR",
"INSOLVENCIES": 40,
"SPV/ASSETBACKED": 22,
"OPERATINGCOMPANIES": 21,
"Bank": 99
}, {
"month": "APR",
"INSOLVENCIES": 60,
"SPV/ASSETBACKED": 1,
"OPERATINGCOMPANIES": 99,
"Bank": 90
}, {
"month": "MAY",
"INSOLVENCIES": 2,
"SPV/ASSETBACKED": 27,
"OPERATINGCOMPANIES": 43,
"Bank": 82
}, {
"month": "JUN",
"INSOLVENCIES": 17,
"SPV/ASSETBACKED": 52,
"OPERATINGCOMPANIES": 79,
"Bank": 9
}, {
"month": "JUL",
"INSOLVENCIES": 37,
"SPV/ASSETBACKED": 24,
"OPERATINGCOMPANIES": 35,
"Bank": 51
}, {
"month": "AUG",
"INSOLVENCIES": 16,
"SPV/ASSETBACKED": 17,
"OPERATINGCOMPANIES": 53,
"Bank": 38
}, {
"month": "SEP",
"INSOLVENCIES": 15,
"SPV/ASSETBACKED": 32,
"OPERATINGCOMPANIES": 5,
"Bank": 31
}];
let xKey = "month";
let keys = d3.keys(data[0]).filter( e => e != xKey );
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var margin = {
top: 20,
right: 50,
bottom: 30,
left: 50
},
width = 400,
height = 300,
padding = 100;
var x = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(.05);
var y = d3.scaleLinear().range([height, 0]);
var color = d3.scaleOrdinal(d3.schemeCategory10);
var xAxis = d3.axisBottom().scale(x);
var yAxis = d3.axisLeft().scale(y).ticks(6); //.innerTickSize(-width).tickPadding(10);
var svg = d3.select("#ashu")
.append("svg")
.attr("width", "100%")
.attr("height", "100%");
const clipPath = svg.append('defs')
.append('clipPath')
.attr('id', 'clip')
.append('rect')
.attr('width', width)
.attr('height', height);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var dataStackLayout = d3.stack().keys(keys)(data);
// add the key to the bar elements
dataStackLayout.forEach(keyElem => {
var key = keyElem.key;
keyElem.forEach(d => { d.key = key; });
});
x.domain(dataStackLayout[0].map(function(d) { return d.data[xKey]; }));
var maximumY = d3.max(dataStackLayout[dataStackLayout.length - 1], function(d) { return d[1]; });
y.domain([0.1, maximumY]);
var gBars = g.append("g")
.attr("class", "bars")
.attr('clip-path', 'url(#clip)');
gBars.selectAll("g")
.data(dataStackLayout)
.enter().append("g")
.attr("fill", function(d) { return color(d.key); })
.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("x", function(d) { return x(d.data[xKey]); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width", x.bandwidth())
.append("title").text(function(d) { return d.key; });
g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
var gY = g.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0,0)")
.call(yAxis);
var legend = g.selectAll(".legend")
.data(color.domain().slice())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + Math.abs((i - 8) * 20) + ")"; });
legend.append("rect")
.attr("x", width + 10)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width + 32)
.attr("y", 10)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d, i) { return keys[i]; });
var zoom = d3.zoom()
.scaleExtent([1, 40])
.on("zoom", zoomed);
svg.call(zoom);
d3.select("button")
.on("click", resetted);
function zoomed() {
// https://stackoverflow.com/a/44359905/9938317
var t = d3.event.transform;
t.y = d3.min([t.y, 0]);
t.y = d3.max([t.y, (1-t.k) * height]);
var yTransform = t.rescaleY(y);
gY.call(yAxis.scale(yTransform));
gBars.selectAll("rect")
.attr("y", function(d) { return yTransform(d[1]); })
.attr("height", function(d) { return yTransform(d[0]) - yTransform(d[1]); });
}
function resetted() {
svg.transition()
.duration(750)
.call(zoom.transform, d3.zoomIdentity);
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<button>Reset</button>
<div id="ashu" style="width: 700px; height:400px;"></div>