I have made considerable progress in replicating a descriptive statistics beeswarm-style visual that can be seen here. I'll also include the picture here for extra convenience:
From my code snippet you will see that I have all the trappings of that visual except for the pyramid style stacking.
var margins = {top:20, bottom:300, left:30, right:100};
var height = 150;
var width = 900;
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 xScale = d3.scaleLinear()
.range([0, width]);
var data = [{'age': 32.0, 'educ': 12.0, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 35.0, 'educ': 12.0, 'inlf': 1},
{'age': 34.0, 'educ': 12.0, 'inlf': 1},
{'age': 31, 'educ': 14.0, 'inlf': 1},
{'age': 54.0, 'educ': 12.0, 'inlf': 1},
{'age': 37.0, 'educ': 16.0, 'inlf': 1},
{'age': 54.0, 'educ': 12.0, 'inlf': 1},
{'age': 48.0, 'educ': 12.0, 'inlf': 1},
{'age': 39.0, 'educ': 12.0, 'inlf': 1},
{'age': 33.0, 'educ': 12.0, 'inlf': 1},
{'age': 42.0, 'educ': 11, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 43.0, 'educ': 12.0, 'inlf': 1},
{'age': 43.0, 'educ': 10.0, 'inlf': 1},
{'age': 35.0, 'educ': 11, 'inlf': 1},
{'age': 43.0, 'educ': 12.0, 'inlf': 1},
{'age': 39.0, 'educ': 12.0, 'inlf': 1},
{'age': 45.0, 'educ': 12.0, 'inlf': 1},
{'age': 35.0, 'educ': 12.0, 'inlf': 1},
{'age': 42.0, 'educ': 16.0, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 48.0, 'educ': 13.0, 'inlf': 1},
{'age': 45.0, 'educ': 12.0, 'inlf': 1},
{'age': 31, 'educ': 12.0, 'inlf': 1},
{'age': 43.0, 'educ': 17.0, 'inlf': 1},
{'age': 59.0, 'educ': 12.0, 'inlf': 1},
{'age': 32.0, 'educ': 12.0, 'inlf': 1},
{'age': 31, 'educ': 17.0, 'inlf': 1},
{'age': 42.0, 'educ': 12.0, 'inlf': 1},
{'age': 50.0, 'educ': 11, 'inlf': 1},
{'age': 59.0, 'educ': 16.0, 'inlf': 1},
{'age': 36.0, 'educ': 13.0, 'inlf': 1},
{'age': 51, 'educ': 12.0, 'inlf': 1},
{'age': 45.0, 'educ': 16.0, 'inlf': 1},
{'age': 42.0, 'educ': 11, 'inlf': 1},
{'age': 46.0, 'educ': 12.0, 'inlf': 1},
{'age': 46.0, 'educ': 10.0, 'inlf': 1},
{'age': 51, 'educ': 14.0, 'inlf': 1},
{'age': 30.0, 'educ': 17.0, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 57.0, 'educ': 12.0, 'inlf': 1},
{'age': 31, 'educ': 16.0, 'inlf': 1},
{'age': 48.0, 'educ': 12.0, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 34.0, 'educ': 12.0, 'inlf': 1},
{'age': 48.0, 'educ': 16.0, 'inlf': 1},
{'age': 45.0, 'educ': 12.0, 'inlf': 1},
{'age': 51, 'educ': 12.0, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 46.0, 'educ': 12.0, 'inlf': 1},
{'age': 58.0, 'educ': 12.0, 'inlf': 1},
{'age': 37.0, 'educ': 12.0, 'inlf': 1},
{'age': 52.0, 'educ': 8.0, 'inlf': 1},
{'age': 52.0, 'educ': 10.0, 'inlf': 1},
{'age': 31, 'educ': 16.0, 'inlf': 1},
{'age': 55.0, 'educ': 14.0, 'inlf': 1},
{'age': 34.0, 'educ': 17.0, 'inlf': 1},
{'age': 55.0, 'educ': 14.0, 'inlf': 1},
{'age': 39.0, 'educ': 12.0, 'inlf': 1},
{'age': 40.0, 'educ': 14.0, 'inlf': 1},
{'age': 43.0, 'educ': 12.0, 'inlf': 1},
{'age': 48.0, 'educ': 8.0, 'inlf': 1},
{'age': 47.0, 'educ': 12.0, 'inlf': 1},
{'age': 41, 'educ': 12.0, 'inlf': 1},
{'age': 36.0, 'educ': 8.0, 'inlf': 1},
{'age': 46.0, 'educ': 17.0, 'inlf': 1},
{'age': 34.0, 'educ': 12.0, 'inlf': 1},
{'age': 41, 'educ': 12.0, 'inlf': 1},
{'age': 51, 'educ': 12.0, 'inlf': 1},
{'age': 33.0, 'educ': 12.0, 'inlf': 1},
{'age': 52.0, 'educ': 12.0, 'inlf': 1},
{'age': 58.0, 'educ': 9.0, 'inlf': 1},
{'age': 34.0, 'educ': 10.0, 'inlf': 1},
{'age': 31, 'educ': 12.0, 'inlf': 1},
{'age': 48.0, 'educ': 12.0, 'inlf': 1},
{'age': 32.0, 'educ': 12.0, 'inlf': 1},
{'age': 49.0, 'educ': 17.0, 'inlf': 1},
{'age': 32.0, 'educ': 15.0, 'inlf': 1},
{'age': 58.0, 'educ': 12.0, 'inlf': 1},
{'age': 50.0, 'educ': 6.0, 'inlf': 1},
{'age': 60.0, 'educ': 14.0, 'inlf': 1},
{'age': 50.0, 'educ': 12.0, 'inlf': 1},
{'age': 56.0, 'educ': 14.0, 'inlf': 1},
{'age': 51, 'educ': 9.0, 'inlf': 1},
{'age': 54.0, 'educ': 17.0, 'inlf': 1},
{'age': 59.0, 'educ': 13.0, 'inlf': 1},
{'age': 46.0, 'educ': 9.0, 'inlf': 1},
{'age': 46.0, 'educ': 15.0, 'inlf': 1},
{'age': 39.0, 'educ': 12.0, 'inlf': 0},
{'age': 44.0, 'educ': 12.0, 'inlf': 0},
{'age': 33.0, 'educ': 12.0, 'inlf': 0},
{'age': 33.0, 'educ': 12.0, 'inlf': 0},
{'age': 48.0, 'educ': 12.0, 'inlf': 0},
{'age': 30, 'educ': 12.0, 'inlf': 0},
{'age': 45.0, 'educ': 12.0, 'inlf': 0},
{'age': 45.0, 'educ': 12.0, 'inlf': 0},
{'age': 32.0, 'educ': 13.0, 'inlf': 0},
{'age': 47.0, 'educ': 12.0, 'inlf': 0},
{'age': 34.0, 'educ': 13.0, 'inlf': 0},
{'age': 37.0, 'educ': 12.0, 'inlf': 0},
{'age': 36.0, 'educ': 12.0, 'inlf': 0},
{'age': 47.0, 'educ': 12.0, 'inlf': 0},
{'age': 48.0, 'educ': 16.0, 'inlf': 0},
{'age': 42.0, 'educ': 12.0, 'inlf': 0},
{'age': 33.0, 'educ': 13.0, 'inlf': 0},
{'age': 46.0, 'educ': 10, 'inlf': 0},
{'age': 47.0, 'educ': 12.0, 'inlf': 0},
{'age': 44.0, 'educ': 12.0, 'inlf': 0},
{'age': 36.0, 'educ': 12.0, 'inlf': 0},
{'age': 30, 'educ': 17.0, 'inlf': 0},
{'age': 55.0, 'educ': 14.0, 'inlf': 0},
{'age': 45.0, 'educ': 16.0, 'inlf': 0},
{'age': 47.0, 'educ': 17.0, 'inlf': 0},
{'age': 46.0, 'educ': 12.0, 'inlf': 0},
{'age': 49.0, 'educ': 10, 'inlf': 0},
{'age': 49.0, 'educ': 12.0, 'inlf': 0},
{'age': 45.0, 'educ': 12.0, 'inlf': 0},
{'age': 38.0, 'educ': 17.0, 'inlf': 0},
{'age': 47.0, 'educ': 10.0, 'inlf': 0},
{'age': 54.0, 'educ': 13.0, 'inlf': 0},
{'age': 40, 'educ': 10, 'inlf': 0},
{'age': 43.0, 'educ': 12.0, 'inlf': 0},
{'age': 30, 'educ': 16.0, 'inlf': 0},
{'age': 47.0, 'educ': 17.0, 'inlf': 0},
{'age': 35.0, 'educ': 12.0, 'inlf': 0},
{'age': 45.0, 'educ': 16.0, 'inlf': 0},
{'age': 33.0, 'educ': 12.0, 'inlf': 0},
{'age': 54.0, 'educ': 16.0, 'inlf': 0},
{'age': 35.0, 'educ': 8.0, 'inlf': 0},
{'age': 30, 'educ': 12.0, 'inlf': 0},
{'age': 55.0, 'educ': 12.0, 'inlf': 0},
{'age': 34.0, 'educ': 12.0, 'inlf': 0},
{'age': 38.0, 'educ': 13.0, 'inlf': 0},
{'age': 45.0, 'educ': 10, 'inlf': 0},
{'age': 47.0, 'educ': 12.0, 'inlf': 0},
{'age': 39.0, 'educ': 12.0, 'inlf': 0},
{'age': 36.0, 'educ': 14.0, 'inlf': 0},
{'age': 33.0, 'educ': 12.0, 'inlf': 0},
{'age': 50.0, 'educ': 12.0, 'inlf': 0},
{'age': 58.0, 'educ': 12.0, 'inlf': 0},
{'age': 49.0, 'educ': 17.0, 'inlf': 0},
{'age': 40, 'educ': 14.0, 'inlf': 0},
{'age': 50, 'educ': 12.0, 'inlf': 0},
{'age': 53.0, 'educ': 9.0, 'inlf': 0},
{'age': 36.0, 'educ': 12.0, 'inlf': 0},
{'age': 46.0, 'educ': 12.0, 'inlf': 0},
{'age': 36.0, 'educ': 12.0, 'inlf': 0},
{'age': 53.0, 'educ': 14.0, 'inlf': 0},
{'age': 40.0, 'educ': 16.0, 'inlf': 0}];
var colorScale = d3.scaleLinear()
.range(["#e7eef8","#003366"]);
xScale.domain(d3.extent(data, function(d) { return d.educ; }));
colorScale.domain(d3.extent(data, function(d) {return d.age; }));
var simulation = d3.forceSimulation(data)
.force("x", d3.forceX(function(d) {
return xScale(d.educ);
}).strength(1))
.force("y", d3.forceY(function(d) {
return d.inlf ? height - 75 : height + 100
}))
.force("collide", d3.forceCollide(4))
.stop();
for (var i = 0; i < 120; ++i) simulation.tick();
graphGroup.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
var circles = graphGroup.selectAll(null)
.data(data)
.enter()
.append("circle")
.attr("r", 3)
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.style('fill', function(d) {return colorScale(d.age)});
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(17))
.attr('y1', height-5)
.attr('y2', height-5)
.style('stroke',"#000");
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(17))
.attr('y1', height+35)
.attr('y2', height+35)
.style('stroke',"#000");
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(12))
.attr('y1', height-5)
.attr('y2', height-5)
.style('stroke',"#b8cce4")
.style('stroke-width',"5px");
graphGroup.append('line')
.attr('x1', xScale(12))
.attr('x2', xScale(14))
.attr('y1', height-5)
.attr('y2', height-5)
.style('stroke',"#4f81b9")
.style('stroke-width',"5px");
graphGroup.append('rect')
.attr('x',xScale(12))
.attr('y', height-10)
.attr('width', 5)
.attr('height',10)
.style('fill', "#f6d18b");
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(11))
.attr('y1', height+35)
.attr('y2', height+35)
.style('stroke',"#b8cce4")
.style('stroke-width',"5px");
graphGroup.append('line')
.attr('x1', xScale(11))
.attr('x2', xScale(12))
.attr('y1', height+35)
.attr('y2', height+35)
.style('stroke',"#4f81b9")
.style('stroke-width',"5px");
graphGroup.append('rect')
.attr('x',xScale(11.7))
.attr('y', height+30)
.attr('width', 5)
.attr('height',10)
.style('fill', "#f6d18b");
.cells path {
fill: none;
pointer-events: all;
}
.cells :hover circle {
fill: red;
}
text {
font-size: 17px;
font-family: TW Cen MT;
}
.axis path, .axis line {
fill: none;
stroke: none;
}
<script src="https://d3js.org/d3.v5.min.js"></script>
As it stands, the stacking shape seems to be a kind of ellipse. I have not found a way to alter the default shape.
Question
How can I modify the default shape of the circle piling to resemble a pyramid? It doesn't have to be a perfect pyramid, just gunning for more at the bottom less at the top. I can foresee things might be especially tricky for data points where d.educ=12, as that is highly saturated.
If it doesn't have to be perfect, you can achieve the effect by imposing hard constraints on the simulation and adjusting forces.
Hard Constraints
To form hard constraints (like having a ceiling or a floor), you can manually move points after every tick.
For instance, if you want to form a floor for the top points at height - 15, and a ceiling for the bottom points at height + 45, you could change:
for (var i = 0; i < 120; ++i) simulation.tick();
to:
for (var i = 0; i < 120; ++i) {
simulation.tick();
data.forEach(function (d) {
if (d.inlf) {
d.y = Math.min(d.y, height - 15);
} else {
d.y = Math.max(d.y, height + 45);
}
});
}
Force Settings
After that, you probably want to play around with the strength of forceX and forceY. I also added .iterations to forceCollide, just in case you need to adjust it later. These settings seemed to work okay for me:
var simulation = d3.forceSimulation(data)
.force("x", d3.forceX(function(d) {
return xScale(d.educ);
}).strength(0.05))
.force("y", d3.forceY(function(d) {
return d.inlf ? height - 15 : height + 45
}).strength(0.1))
.force("collide", d3.forceCollide(4).iterations(1))
.stop();
You may need to increase the number of tick iterations for weak forces or for overlapping circles. I increased it to 240 for this example.
for (var i = 0; i < 120; ++i) {
simulation.tick();
// ...
to:
for (var i = 0; i < 240; ++i) {
simulation.tick();
// ...
Initial positions
As pointed out in the comments, the circles are not where they should be! This is due to the weak forces not moving them far enough from their initial position. To fix this, we can set the initial position to be around where we want them.
data.forEach(function (d) {
d.x = xScale(d.educ);
d.y = d.inlf ? height - 15 : height + 45;
});
Result
After moving the points up, all in all, it looks like:
var margins = {top:20, bottom:300, left:30, right:100};
var height = 150;
var width = 900;
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 xScale = d3.scaleLinear()
.range([0, width]);
var data = [{'age': 32.0, 'educ': 12.0, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 35.0, 'educ': 12.0, 'inlf': 1},
{'age': 34.0, 'educ': 12.0, 'inlf': 1},
{'age': 31, 'educ': 14.0, 'inlf': 1},
{'age': 54.0, 'educ': 12.0, 'inlf': 1},
{'age': 37.0, 'educ': 16.0, 'inlf': 1},
{'age': 54.0, 'educ': 12.0, 'inlf': 1},
{'age': 48.0, 'educ': 12.0, 'inlf': 1},
{'age': 39.0, 'educ': 12.0, 'inlf': 1},
{'age': 33.0, 'educ': 12.0, 'inlf': 1},
{'age': 42.0, 'educ': 11, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 43.0, 'educ': 12.0, 'inlf': 1},
{'age': 43.0, 'educ': 10.0, 'inlf': 1},
{'age': 35.0, 'educ': 11, 'inlf': 1},
{'age': 43.0, 'educ': 12.0, 'inlf': 1},
{'age': 39.0, 'educ': 12.0, 'inlf': 1},
{'age': 45.0, 'educ': 12.0, 'inlf': 1},
{'age': 35.0, 'educ': 12.0, 'inlf': 1},
{'age': 42.0, 'educ': 16.0, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 48.0, 'educ': 13.0, 'inlf': 1},
{'age': 45.0, 'educ': 12.0, 'inlf': 1},
{'age': 31, 'educ': 12.0, 'inlf': 1},
{'age': 43.0, 'educ': 17.0, 'inlf': 1},
{'age': 59.0, 'educ': 12.0, 'inlf': 1},
{'age': 32.0, 'educ': 12.0, 'inlf': 1},
{'age': 31, 'educ': 17.0, 'inlf': 1},
{'age': 42.0, 'educ': 12.0, 'inlf': 1},
{'age': 50.0, 'educ': 11, 'inlf': 1},
{'age': 59.0, 'educ': 16.0, 'inlf': 1},
{'age': 36.0, 'educ': 13.0, 'inlf': 1},
{'age': 51, 'educ': 12.0, 'inlf': 1},
{'age': 45.0, 'educ': 16.0, 'inlf': 1},
{'age': 42.0, 'educ': 11, 'inlf': 1},
{'age': 46.0, 'educ': 12.0, 'inlf': 1},
{'age': 46.0, 'educ': 10.0, 'inlf': 1},
{'age': 51, 'educ': 14.0, 'inlf': 1},
{'age': 30.0, 'educ': 17.0, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 57.0, 'educ': 12.0, 'inlf': 1},
{'age': 31, 'educ': 16.0, 'inlf': 1},
{'age': 48.0, 'educ': 12.0, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 34.0, 'educ': 12.0, 'inlf': 1},
{'age': 48.0, 'educ': 16.0, 'inlf': 1},
{'age': 45.0, 'educ': 12.0, 'inlf': 1},
{'age': 51, 'educ': 12.0, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 46.0, 'educ': 12.0, 'inlf': 1},
{'age': 58.0, 'educ': 12.0, 'inlf': 1},
{'age': 37.0, 'educ': 12.0, 'inlf': 1},
{'age': 52.0, 'educ': 8.0, 'inlf': 1},
{'age': 52.0, 'educ': 10.0, 'inlf': 1},
{'age': 31, 'educ': 16.0, 'inlf': 1},
{'age': 55.0, 'educ': 14.0, 'inlf': 1},
{'age': 34.0, 'educ': 17.0, 'inlf': 1},
{'age': 55.0, 'educ': 14.0, 'inlf': 1},
{'age': 39.0, 'educ': 12.0, 'inlf': 1},
{'age': 40.0, 'educ': 14.0, 'inlf': 1},
{'age': 43.0, 'educ': 12.0, 'inlf': 1},
{'age': 48.0, 'educ': 8.0, 'inlf': 1},
{'age': 47.0, 'educ': 12.0, 'inlf': 1},
{'age': 41, 'educ': 12.0, 'inlf': 1},
{'age': 36.0, 'educ': 8.0, 'inlf': 1},
{'age': 46.0, 'educ': 17.0, 'inlf': 1},
{'age': 34.0, 'educ': 12.0, 'inlf': 1},
{'age': 41, 'educ': 12.0, 'inlf': 1},
{'age': 51, 'educ': 12.0, 'inlf': 1},
{'age': 33.0, 'educ': 12.0, 'inlf': 1},
{'age': 52.0, 'educ': 12.0, 'inlf': 1},
{'age': 58.0, 'educ': 9.0, 'inlf': 1},
{'age': 34.0, 'educ': 10.0, 'inlf': 1},
{'age': 31, 'educ': 12.0, 'inlf': 1},
{'age': 48.0, 'educ': 12.0, 'inlf': 1},
{'age': 32.0, 'educ': 12.0, 'inlf': 1},
{'age': 49.0, 'educ': 17.0, 'inlf': 1},
{'age': 32.0, 'educ': 15.0, 'inlf': 1},
{'age': 58.0, 'educ': 12.0, 'inlf': 1},
{'age': 50.0, 'educ': 6.0, 'inlf': 1},
{'age': 60.0, 'educ': 14.0, 'inlf': 1},
{'age': 50.0, 'educ': 12.0, 'inlf': 1},
{'age': 56.0, 'educ': 14.0, 'inlf': 1},
{'age': 51, 'educ': 9.0, 'inlf': 1},
{'age': 54.0, 'educ': 17.0, 'inlf': 1},
{'age': 59.0, 'educ': 13.0, 'inlf': 1},
{'age': 46.0, 'educ': 9.0, 'inlf': 1},
{'age': 46.0, 'educ': 15.0, 'inlf': 1},
{'age': 39.0, 'educ': 12.0, 'inlf': 0},
{'age': 44.0, 'educ': 12.0, 'inlf': 0},
{'age': 33.0, 'educ': 12.0, 'inlf': 0},
{'age': 33.0, 'educ': 12.0, 'inlf': 0},
{'age': 48.0, 'educ': 12.0, 'inlf': 0},
{'age': 30, 'educ': 12.0, 'inlf': 0},
{'age': 45.0, 'educ': 12.0, 'inlf': 0},
{'age': 45.0, 'educ': 12.0, 'inlf': 0},
{'age': 32.0, 'educ': 13.0, 'inlf': 0},
{'age': 47.0, 'educ': 12.0, 'inlf': 0},
{'age': 34.0, 'educ': 13.0, 'inlf': 0},
{'age': 37.0, 'educ': 12.0, 'inlf': 0},
{'age': 36.0, 'educ': 12.0, 'inlf': 0},
{'age': 47.0, 'educ': 12.0, 'inlf': 0},
{'age': 48.0, 'educ': 16.0, 'inlf': 0},
{'age': 42.0, 'educ': 12.0, 'inlf': 0},
{'age': 33.0, 'educ': 13.0, 'inlf': 0},
{'age': 46.0, 'educ': 10, 'inlf': 0},
{'age': 47.0, 'educ': 12.0, 'inlf': 0},
{'age': 44.0, 'educ': 12.0, 'inlf': 0},
{'age': 36.0, 'educ': 12.0, 'inlf': 0},
{'age': 30, 'educ': 17.0, 'inlf': 0},
{'age': 55.0, 'educ': 14.0, 'inlf': 0},
{'age': 45.0, 'educ': 16.0, 'inlf': 0},
{'age': 47.0, 'educ': 17.0, 'inlf': 0},
{'age': 46.0, 'educ': 12.0, 'inlf': 0},
{'age': 49.0, 'educ': 10, 'inlf': 0},
{'age': 49.0, 'educ': 12.0, 'inlf': 0},
{'age': 45.0, 'educ': 12.0, 'inlf': 0},
{'age': 38.0, 'educ': 17.0, 'inlf': 0},
{'age': 47.0, 'educ': 10.0, 'inlf': 0},
{'age': 54.0, 'educ': 13.0, 'inlf': 0},
{'age': 40, 'educ': 10, 'inlf': 0},
{'age': 43.0, 'educ': 12.0, 'inlf': 0},
{'age': 30, 'educ': 16.0, 'inlf': 0},
{'age': 47.0, 'educ': 17.0, 'inlf': 0},
{'age': 35.0, 'educ': 12.0, 'inlf': 0},
{'age': 45.0, 'educ': 16.0, 'inlf': 0},
{'age': 33.0, 'educ': 12.0, 'inlf': 0},
{'age': 54.0, 'educ': 16.0, 'inlf': 0},
{'age': 35.0, 'educ': 8.0, 'inlf': 0},
{'age': 30, 'educ': 12.0, 'inlf': 0},
{'age': 55.0, 'educ': 12.0, 'inlf': 0},
{'age': 34.0, 'educ': 12.0, 'inlf': 0},
{'age': 38.0, 'educ': 13.0, 'inlf': 0},
{'age': 45.0, 'educ': 10, 'inlf': 0},
{'age': 47.0, 'educ': 12.0, 'inlf': 0},
{'age': 39.0, 'educ': 12.0, 'inlf': 0},
{'age': 36.0, 'educ': 14.0, 'inlf': 0},
{'age': 33.0, 'educ': 12.0, 'inlf': 0},
{'age': 50.0, 'educ': 12.0, 'inlf': 0},
{'age': 58.0, 'educ': 12.0, 'inlf': 0},
{'age': 49.0, 'educ': 17.0, 'inlf': 0},
{'age': 40, 'educ': 14.0, 'inlf': 0},
{'age': 50, 'educ': 12.0, 'inlf': 0},
{'age': 53.0, 'educ': 9.0, 'inlf': 0},
{'age': 36.0, 'educ': 12.0, 'inlf': 0},
{'age': 46.0, 'educ': 12.0, 'inlf': 0},
{'age': 36.0, 'educ': 12.0, 'inlf': 0},
{'age': 53.0, 'educ': 14.0, 'inlf': 0},
{'age': 40.0, 'educ': 16.0, 'inlf': 0}];
var colorScale = d3.scaleLinear()
.range(["#e7eef8","#003366"]);
xScale.domain(d3.extent(data, function(d) { return d.educ; }));
colorScale.domain(d3.extent(data, function(d) {return d.age; }));
data.forEach(function (d) {
d.x = xScale(d.educ);
d.y = d.inlf ? height - 15 : height + 45;
});
var simulation = d3.forceSimulation(data)
.force("x", d3.forceX(function(d) {
return xScale(d.educ);
}).strength(0.05))
.force("y", d3.forceY(function(d) {
return d.inlf ? height - 15 : height + 45
}).strength(0.1))
.force("collide", d3.forceCollide(4).iterations(1))
.stop();
for (var i = 0; i < 240; ++i) {
simulation.tick();
data.forEach(function (d) {
if (d.inlf) {
d.y = Math.min(d.y, height - 15);
} else {
d.y = Math.max(d.y, height + 45);
}
});
}
graphGroup.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
var circles = graphGroup.selectAll(null)
.data(data)
.enter()
.append("circle")
.attr("r", 3)
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.style('fill', function(d) {return colorScale(d.age)});
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(17))
.attr('y1', height-5)
.attr('y2', height-5)
.style('stroke',"#000");
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(17))
.attr('y1', height+35)
.attr('y2', height+35)
.style('stroke',"#000");
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(12))
.attr('y1', height-5)
.attr('y2', height-5)
.style('stroke',"#b8cce4")
.style('stroke-width',"5px");
graphGroup.append('line')
.attr('x1', xScale(12))
.attr('x2', xScale(14))
.attr('y1', height-5)
.attr('y2', height-5)
.style('stroke',"#4f81b9")
.style('stroke-width',"5px");
graphGroup.append('rect')
.attr('x',xScale(12))
.attr('y', height-10)
.attr('width', 5)
.attr('height',10)
.style('fill', "#f6d18b");
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(11))
.attr('y1', height+35)
.attr('y2', height+35)
.style('stroke',"#b8cce4")
.style('stroke-width',"5px");
graphGroup.append('line')
.attr('x1', xScale(11))
.attr('x2', xScale(12))
.attr('y1', height+35)
.attr('y2', height+35)
.style('stroke',"#4f81b9")
.style('stroke-width',"5px");
graphGroup.append('rect')
.attr('x',xScale(11.7))
.attr('y', height+30)
.attr('width', 5)
.attr('height',10)
.style('fill', "#f6d18b");
.cells path {
fill: none;
pointer-events: all;
}
.cells :hover circle {
fill: red;
}
text {
font-size: 17px;
font-family: TW Cen MT;
}
.axis path, .axis line {
fill: none;
stroke: none;
}
<script src="https://d3js.org/d3.v5.min.js"></script>
Little late to the party but if you want a stronger stacked pyramid, I'd abandon the force simulation. Here's a custom implementation with a custom pyramid stacking algorithm:
<!DOCTYPE html>
<html>
<head>
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<script>
var margins = {
top: 20,
bottom: 300,
left: 30,
right: 100
};
var height = 150;
var width = 900;
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 xScale = d3.scaleLinear()
.range([0, width]);
var data = [{
'age': 32.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 30.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 35.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 34.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 31,
'educ': 14.0,
'inlf': 1
}, {
'age': 54.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 37.0,
'educ': 16.0,
'inlf': 1
}, {
'age': 54.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 48.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 39.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 33.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 42.0,
'educ': 11,
'inlf': 1
}, {
'age': 30.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 43.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 43.0,
'educ': 10.0,
'inlf': 1
}, {
'age': 35.0,
'educ': 11,
'inlf': 1
}, {
'age': 43.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 39.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 45.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 35.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 42.0,
'educ': 16.0,
'inlf': 1
}, {
'age': 30.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 48.0,
'educ': 13.0,
'inlf': 1
}, {
'age': 45.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 31,
'educ': 12.0,
'inlf': 1
}, {
'age': 43.0,
'educ': 17.0,
'inlf': 1
}, {
'age': 59.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 32.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 31,
'educ': 17.0,
'inlf': 1
}, {
'age': 42.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 50.0,
'educ': 11,
'inlf': 1
}, {
'age': 59.0,
'educ': 16.0,
'inlf': 1
}, {
'age': 36.0,
'educ': 13.0,
'inlf': 1
}, {
'age': 51,
'educ': 12.0,
'inlf': 1
}, {
'age': 45.0,
'educ': 16.0,
'inlf': 1
}, {
'age': 42.0,
'educ': 11,
'inlf': 1
}, {
'age': 46.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 46.0,
'educ': 10.0,
'inlf': 1
}, {
'age': 51,
'educ': 14.0,
'inlf': 1
}, {
'age': 30.0,
'educ': 17.0,
'inlf': 1
}, {
'age': 30.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 57.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 31,
'educ': 16.0,
'inlf': 1
}, {
'age': 48.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 30.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 34.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 48.0,
'educ': 16.0,
'inlf': 1
}, {
'age': 45.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 51,
'educ': 12.0,
'inlf': 1
}, {
'age': 30.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 46.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 58.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 37.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 52.0,
'educ': 8.0,
'inlf': 1
}, {
'age': 52.0,
'educ': 10.0,
'inlf': 1
}, {
'age': 31,
'educ': 16.0,
'inlf': 1
}, {
'age': 55.0,
'educ': 14.0,
'inlf': 1
}, {
'age': 34.0,
'educ': 17.0,
'inlf': 1
}, {
'age': 55.0,
'educ': 14.0,
'inlf': 1
}, {
'age': 39.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 40.0,
'educ': 14.0,
'inlf': 1
}, {
'age': 43.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 48.0,
'educ': 8.0,
'inlf': 1
}, {
'age': 47.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 41,
'educ': 12.0,
'inlf': 1
}, {
'age': 36.0,
'educ': 8.0,
'inlf': 1
}, {
'age': 46.0,
'educ': 17.0,
'inlf': 1
}, {
'age': 34.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 41,
'educ': 12.0,
'inlf': 1
}, {
'age': 51,
'educ': 12.0,
'inlf': 1
}, {
'age': 33.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 52.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 58.0,
'educ': 9.0,
'inlf': 1
}, {
'age': 34.0,
'educ': 10.0,
'inlf': 1
}, {
'age': 31,
'educ': 12.0,
'inlf': 1
}, {
'age': 48.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 32.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 49.0,
'educ': 17.0,
'inlf': 1
}, {
'age': 32.0,
'educ': 15.0,
'inlf': 1
}, {
'age': 58.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 50.0,
'educ': 6.0,
'inlf': 1
}, {
'age': 60.0,
'educ': 14.0,
'inlf': 1
}, {
'age': 50.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 56.0,
'educ': 14.0,
'inlf': 1
}, {
'age': 51,
'educ': 9.0,
'inlf': 1
}, {
'age': 54.0,
'educ': 17.0,
'inlf': 1
}, {
'age': 59.0,
'educ': 13.0,
'inlf': 1
}, {
'age': 46.0,
'educ': 9.0,
'inlf': 1
}, {
'age': 46.0,
'educ': 15.0,
'inlf': 1
}, {
'age': 39.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 44.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 33.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 33.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 48.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 30,
'educ': 12.0,
'inlf': 0
}, {
'age': 45.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 45.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 32.0,
'educ': 13.0,
'inlf': 0
}, {
'age': 47.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 34.0,
'educ': 13.0,
'inlf': 0
}, {
'age': 37.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 36.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 47.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 48.0,
'educ': 16.0,
'inlf': 0
}, {
'age': 42.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 33.0,
'educ': 13.0,
'inlf': 0
}, {
'age': 46.0,
'educ': 10,
'inlf': 0
}, {
'age': 47.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 44.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 36.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 30,
'educ': 17.0,
'inlf': 0
}, {
'age': 55.0,
'educ': 14.0,
'inlf': 0
}, {
'age': 45.0,
'educ': 16.0,
'inlf': 0
}, {
'age': 47.0,
'educ': 17.0,
'inlf': 0
}, {
'age': 46.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 49.0,
'educ': 10,
'inlf': 0
}, {
'age': 49.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 45.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 38.0,
'educ': 17.0,
'inlf': 0
}, {
'age': 47.0,
'educ': 10.0,
'inlf': 0
}, {
'age': 54.0,
'educ': 13.0,
'inlf': 0
}, {
'age': 40,
'educ': 10,
'inlf': 0
}, {
'age': 43.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 30,
'educ': 16.0,
'inlf': 0
}, {
'age': 47.0,
'educ': 17.0,
'inlf': 0
}, {
'age': 35.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 45.0,
'educ': 16.0,
'inlf': 0
}, {
'age': 33.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 54.0,
'educ': 16.0,
'inlf': 0
}, {
'age': 35.0,
'educ': 8.0,
'inlf': 0
}, {
'age': 30,
'educ': 12.0,
'inlf': 0
}, {
'age': 55.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 34.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 38.0,
'educ': 13.0,
'inlf': 0
}, {
'age': 45.0,
'educ': 10,
'inlf': 0
}, {
'age': 47.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 39.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 36.0,
'educ': 14.0,
'inlf': 0
}, {
'age': 33.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 50.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 58.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 49.0,
'educ': 17.0,
'inlf': 0
}, {
'age': 40,
'educ': 14.0,
'inlf': 0
}, {
'age': 50,
'educ': 12.0,
'inlf': 0
}, {
'age': 53.0,
'educ': 9.0,
'inlf': 0
}, {
'age': 36.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 46.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 36.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 53.0,
'educ': 14.0,
'inlf': 0
}, {
'age': 40.0,
'educ': 16.0,
'inlf': 0
}];
var colorScale = d3.scaleLinear()
.range(["#e7eef8", "#003366"]);
xScale.domain(d3.extent(data, function(d) {
return d.educ;
}));
colorScale.domain(d3.extent(data, function(d) {
return d.age;
}));
// bin the data based on top bottom and x-position
var bins = d3.nest()
.key(function(d) { return [(d.inlf ? 1 : 0), d.educ] })
.entries(data);
// calculate positions (build the pyramid)
var r = 3;
bins.forEach( (bin) => {
var ds = bin.values;
var br = 1, n = 1;
while (br < ds.length){
br += n; n += 1;
}
n -= 1
var c = 0; rn = n; g = 0;
var s = r * 2 + 3;
ds.forEach((d)=>{
d.y = (rn - n) * s;
d.x = (c * s);
d.g = g;
c += 1;
if (c == n){
c = 0;
n -= 1;
g += 1
}
});
});
// re-bin by age and row so that we can traslate the rows
var binsAndRow = d3.nest()
.key(function(d) { return [(d.inlf ? 1 : 0), d.educ]; })
.key(function(d) { return d.g; })
.entries(data);
// draw circles
var x = graphGroup.selectAll("g")
.data(binsAndRow)
.enter()
.append("g")
.attr("class", (d) => "bin " + "b" + d.key)
.selectAll("g")
.data( (d) => {
return d.values
})
.enter()
.append("g")
.attr("class", (d) => "row " + "r" + d.key)
.selectAll("circle")
.data( (d) => {
return d.values
})
.enter()
.append("circle")
.attr("r", 3)
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.style('fill', function(d) {return colorScale(d.age)});
// position the pyramids
graphGroup
.selectAll(".bin")
.attr("transform", function(d){
var bb = this.getBBox(),
xPos = (xScale(d.key.split(",")[1]) - bb.width/2),
isFlip = d.key.startsWith("1"),
yPos = isFlip ? height - 20 : height + 50,
t = "translate(" + xPos + "," + yPos + ")";
if (isFlip) t+= "scale(1,-1)";
return t;
})
.selectAll(".row")
.attr("transform", function(d) {
var pbb = this.parentNode.getBBox();
var bb = this.getBBox();
return "translate(" + ((pbb.width - bb.width)/2) + "," + 0 + ")";
});
graphGroup.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(17))
.attr('y1', height - 5)
.attr('y2', height - 5)
.style('stroke', "#000");
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(17))
.attr('y1', height + 35)
.attr('y2', height + 35)
.style('stroke', "#000");
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(12))
.attr('y1', height - 5)
.attr('y2', height - 5)
.style('stroke', "#b8cce4")
.style('stroke-width', "5px");
graphGroup.append('line')
.attr('x1', xScale(12))
.attr('x2', xScale(14))
.attr('y1', height - 5)
.attr('y2', height - 5)
.style('stroke', "#4f81b9")
.style('stroke-width', "5px");
graphGroup.append('rect')
.attr('x', xScale(12))
.attr('y', height - 10)
.attr('width', 5)
.attr('height', 10)
.style('fill', "#f6d18b");
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(11))
.attr('y1', height + 35)
.attr('y2', height + 35)
.style('stroke', "#b8cce4")
.style('stroke-width', "5px");
graphGroup.append('line')
.attr('x1', xScale(11))
.attr('x2', xScale(12))
.attr('y1', height + 35)
.attr('y2', height + 35)
.style('stroke', "#4f81b9")
.style('stroke-width', "5px");
graphGroup.append('rect')
.attr('x', xScale(11.7))
.attr('y', height + 30)
.attr('width', 5)
.attr('height', 10)
.style('fill', "#f6d18b");
</script>
</body>
</html>
Related
I have written this elastic search query:
es.search(index=['ind1'],doc_type=['doc'])
I am getting following result:
{'_shards': {'failed': 0, 'skipped': 0, 'successful': 5, 'total': 5},
'hits': {'hits': [{'_id': '1327',
'_index': 'ind1',
'_score': 1.0,
'_source': {'val1': 1,
'val2': None,
'value1': 1327,
'value2': 1531,
'new_values': {'nv1': 1,
'nv2': 0},
{'_id': '1349',
'_index': 'ind1',
'_score': 1.0,
'_source': {'val1': 2,
'val2': 3,
'value1': 1328,
'value2': 1539,
'new_values': {'nv1': 1,
'nv2': 3}},.......
I want all the add val1, value1 and nv1 and store it in another field Let's call total. I want the result will be like:
{'_shards': {'failed': 0, 'skipped': 0, 'successful': 5, 'total': 5},
'hits': {'hits': [{'_id': '1327',
'_index': 'ind1',
'_score': 1.0,
'_source': {'val1': 1,
'val2': None,
'value1': 1327,
'value2': 1531,
'new_values': {'nv1': 1,
'nv2': 0},
'total1': 1329},
{'_id': '1349',
'_index': 'ind1',
'_score': 1.0,
'_source': {'val1': 2,
'val2': 3,
'value1': 1328,
'value2': 1539,
'new_values': {'nv1': 1,
'nv2': 3},
'total': 1331},.......
What you can do is use a script field in order to compute that value on the fly:
es.search(index=['ind1'],doc_type=['doc'], body={
'_source': true,
'script_fields': {
'total': {
'script': {
'source': 'doc.val1 + doc.value1 + doc.new_values.nv1'
}
}
}
})
The best way, though, would be to pre-compute that at index time and store the value in a new total field.
https://github.com/evrencoskun/TableView
It's primarily the first row and the first column fixed,That's what I want
Try PaginatedDataTable. This example is extracted from Flutter Gallery example.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() => runApp(new MaterialApp(debugShowCheckedModeBanner: false, home: new DataTableDemo(),));
class Dessert {
Dessert(this.name, this.calories, this.fat, this.carbs, this.protein, this.sodium, this.calcium, this.iron);
final String name;
final int calories;
final double fat;
final int carbs;
final double protein;
final int sodium;
final int calcium;
final int iron;
bool selected = false;
}
class DessertDataSource extends DataTableSource {
final List<Dessert> _desserts = <Dessert>[
new Dessert('Frozen yogurt', 159, 6.0, 24, 4.0, 87, 14, 1),
new Dessert('Ice cream sandwich', 237, 9.0, 37, 4.3, 129, 8, 1),
new Dessert('Eclair', 262, 16.0, 24, 6.0, 337, 6, 7),
new Dessert('Cupcake', 305, 3.7, 67, 4.3, 413, 3, 8),
new Dessert('Gingerbread', 356, 16.0, 49, 3.9, 327, 7, 16),
new Dessert('Jelly bean', 375, 0.0, 94, 0.0, 50, 0, 0),
new Dessert('Lollipop', 392, 0.2, 98, 0.0, 38, 0, 2),
new Dessert('Honeycomb', 408, 3.2, 87, 6.5, 562, 0, 45),
new Dessert('Donut', 452, 25.0, 51, 4.9, 326, 2, 22),
new Dessert('KitKat', 518, 26.0, 65, 7.0, 54, 12, 6),
new Dessert('Frozen yogurt with sugar', 168, 6.0, 26, 4.0, 87, 14, 1),
new Dessert('Ice cream sandwich with sugar', 246, 9.0, 39, 4.3, 129, 8, 1),
new Dessert('Eclair with sugar', 271, 16.0, 26, 6.0, 337, 6, 7),
new Dessert('Cupcake with sugar', 314, 3.7, 69, 4.3, 413, 3, 8),
new Dessert('Gingerbread with sugar', 345, 16.0, 51, 3.9, 327, 7, 16),
new Dessert('Jelly bean with sugar', 364, 0.0, 96, 0.0, 50, 0, 0),
new Dessert('Lollipop with sugar', 401, 0.2, 100, 0.0, 38, 0, 2),
new Dessert('Honeycomb with sugar', 417, 3.2, 89, 6.5, 562, 0, 45),
new Dessert('Donut with sugar', 461, 25.0, 53, 4.9, 326, 2, 22),
new Dessert('KitKat with sugar', 527, 26.0, 67, 7.0, 54, 12, 6),
new Dessert('Frozen yogurt with honey', 223, 6.0, 36, 4.0, 87, 14, 1),
new Dessert('Ice cream sandwich with honey', 301, 9.0, 49, 4.3, 129, 8, 1),
new Dessert('Eclair with honey', 326, 16.0, 36, 6.0, 337, 6, 7),
new Dessert('Cupcake with honey', 369, 3.7, 79, 4.3, 413, 3, 8),
new Dessert('Gingerbread with honey', 420, 16.0, 61, 3.9, 327, 7, 16),
new Dessert('Jelly bean with honey', 439, 0.0, 106, 0.0, 50, 0, 0),
new Dessert('Lollipop with honey', 456, 0.2, 110, 0.0, 38, 0, 2),
new Dessert('Honeycomb with honey', 472, 3.2, 99, 6.5, 562, 0, 45),
new Dessert('Donut with honey', 516, 25.0, 63, 4.9, 326, 2, 22),
new Dessert('KitKat with honey', 582, 26.0, 77, 7.0, 54, 12, 6),
new Dessert('Frozen yogurt with milk', 262, 8.4, 36, 12.0, 194, 44, 1),
new Dessert('Ice cream sandwich with milk', 339, 11.4, 49, 12.3, 236, 38, 1),
new Dessert('Eclair with milk', 365, 18.4, 36, 14.0, 444, 36, 7),
new Dessert('Cupcake with milk', 408, 6.1, 79, 12.3, 520, 33, 8),
new Dessert('Gingerbread with milk', 459, 18.4, 61, 11.9, 434, 37, 16),
new Dessert('Jelly bean with milk', 478, 2.4, 106, 8.0, 157, 30, 0),
new Dessert('Lollipop with milk', 495, 2.6, 110, 8.0, 145, 30, 2),
new Dessert('Honeycomb with milk', 511, 5.6, 99, 14.5, 669, 30, 45),
new Dessert('Donut with milk', 555, 27.4, 63, 12.9, 433, 32, 22),
new Dessert('KitKat with milk', 621, 28.4, 77, 15.0, 161, 42, 6),
new Dessert('Coconut slice and frozen yogurt', 318, 21.0, 31, 5.5, 96, 14, 7),
new Dessert('Coconut slice and ice cream sandwich', 396, 24.0, 44, 5.8, 138, 8, 7),
new Dessert('Coconut slice and eclair', 421, 31.0, 31, 7.5, 346, 6, 13),
new Dessert('Coconut slice and cupcake', 464, 18.7, 74, 5.8, 422, 3, 14),
new Dessert('Coconut slice and gingerbread', 515, 31.0, 56, 5.4, 316, 7, 22),
new Dessert('Coconut slice and jelly bean', 534, 15.0, 101, 1.5, 59, 0, 6),
new Dessert('Coconut slice and lollipop', 551, 15.2, 105, 1.5, 47, 0, 8),
new Dessert('Coconut slice and honeycomb', 567, 18.2, 94, 8.0, 571, 0, 51),
new Dessert('Coconut slice and donut', 611, 40.0, 58, 6.4, 335, 2, 28),
new Dessert('Coconut slice and KitKat', 677, 41.0, 72, 8.5, 63, 12, 12),
];
void _sort<T>(Comparable<T> getField(Dessert d), bool ascending) {
_desserts.sort((Dessert a, Dessert b) {
if (!ascending) {
final Dessert c = a;
a = b;
b = c;
}
final Comparable<T> aValue = getField(a);
final Comparable<T> bValue = getField(b);
return Comparable.compare(aValue, bValue);
});
notifyListeners();
}
int _selectedCount = 0;
#override
DataRow getRow(int index) {
assert(index >= 0);
if (index >= _desserts.length)
return null;
final Dessert dessert = _desserts[index];
return new DataRow.byIndex(
index: index,
selected: dessert.selected,
onSelectChanged: (bool value) {
if (dessert.selected != value) {
_selectedCount += value ? 1 : -1;
assert(_selectedCount >= 0);
dessert.selected = value;
notifyListeners();
}
},
cells: <DataCell>[
new DataCell(new Text('${dessert.name}')),
new DataCell(new Text('${dessert.calories}')),
new DataCell(new Text('${dessert.fat.toStringAsFixed(1)}')),
new DataCell(new Text('${dessert.carbs}')),
new DataCell(new Text('${dessert.protein.toStringAsFixed(1)}')),
new DataCell(new Text('${dessert.sodium}')),
new DataCell(new Text('${dessert.calcium}%')),
new DataCell(new Text('${dessert.iron}%')),
]
);
}
#override
int get rowCount => _desserts.length;
#override
bool get isRowCountApproximate => false;
#override
int get selectedRowCount => _selectedCount;
void _selectAll(bool checked) {
for (Dessert dessert in _desserts)
dessert.selected = checked;
_selectedCount = checked ? _desserts.length : 0;
notifyListeners();
}
}
class DataTableDemo extends StatefulWidget {
static const String routeName = '/material/data-table';
#override
_DataTableDemoState createState() => new _DataTableDemoState();
}
class _DataTableDemoState extends State<DataTableDemo> {
int _rowsPerPage = PaginatedDataTable.defaultRowsPerPage;
int _sortColumnIndex;
bool _sortAscending = true;
final DessertDataSource _dessertsDataSource = new DessertDataSource();
void _sort<T>(Comparable<T> getField(Dessert d), int columnIndex, bool ascending) {
_dessertsDataSource._sort<T>(getField, ascending);
setState(() {
_sortColumnIndex = columnIndex;
_sortAscending = ascending;
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: const Text('Data tables')),
body: new ListView(
padding: const EdgeInsets.all(20.0),
children: <Widget>[
new PaginatedDataTable(
header: const Text('Nutrition'),
rowsPerPage: _rowsPerPage,
onRowsPerPageChanged: (int value) { setState(() { _rowsPerPage = value; }); },
sortColumnIndex: _sortColumnIndex,
sortAscending: _sortAscending,
onSelectAll: _dessertsDataSource._selectAll,
columns: <DataColumn>[
new DataColumn(
label: const Text('Dessert (100g serving)'),
onSort: (int columnIndex, bool ascending) => _sort<String>((Dessert d) => d.name, columnIndex, ascending)
),
new DataColumn(
label: const Text('Calories'),
tooltip: 'The total amount of food energy in the given serving size.',
numeric: true,
onSort: (int columnIndex, bool ascending) => _sort<num>((Dessert d) => d.calories, columnIndex, ascending)
),
new DataColumn(
label: const Text('Fat (g)'),
numeric: true,
onSort: (int columnIndex, bool ascending) => _sort<num>((Dessert d) => d.fat, columnIndex, ascending)
),
new DataColumn(
label: const Text('Carbs (g)'),
numeric: true,
onSort: (int columnIndex, bool ascending) => _sort<num>((Dessert d) => d.carbs, columnIndex, ascending)
),
new DataColumn(
label: const Text('Protein (g)'),
numeric: true,
onSort: (int columnIndex, bool ascending) => _sort<num>((Dessert d) => d.protein, columnIndex, ascending)
),
new DataColumn(
label: const Text('Sodium (mg)'),
numeric: true,
onSort: (int columnIndex, bool ascending) => _sort<num>((Dessert d) => d.sodium, columnIndex, ascending)
),
new DataColumn(
label: const Text('Calcium (%)'),
tooltip: 'The amount of calcium as a percentage of the recommended daily amount.',
numeric: true,
onSort: (int columnIndex, bool ascending) => _sort<num>((Dessert d) => d.calcium, columnIndex, ascending)
),
new DataColumn(
label: const Text('Iron (%)'),
numeric: true,
onSort: (int columnIndex, bool ascending) => _sort<num>((Dessert d) => d.iron, columnIndex, ascending)
),
],
source: _dessertsDataSource
)
]
)
);
}
}
DessertDataSource class contains the input fields displayed in the data table. Change them and update getRow method based on your needs.
I am trying to build a chart, but I see that the starting data labels at the beginning being cut off. Not sure what is the reason. I tried using the padding solution but it doesnt seem to work.
My code is based on the C3jS library.
My x-axis padding is :
padding: {
top: 0,
bottom: 0,
left: 0
}
and y axis padding :
padding: {
top: 0,
bottom: 0,
left: 0,
right: 0
}
My fiddle: https://jsfiddle.net/sourabhtewari/8uz8j01j/371/
my code:
var stuff = [{
"year": 2015,
"month": 12,
"s1": 0.38,
"s2": 100,
"s3": 22.2,
"s4": 61,
"s5": -7,
},
{
"year": 2016,
"month": 1,
"s1": 0.39,
"s2": 101,
"s3": 22.12,
"s4": 62,
"s5": -6.0,
},
{
"year": 2016,
"month": 2,
"s1": 0.43,
"s2": 102,
"s3": 22.11,
"s4": 65,
"s5": -5.7,
},
{
"year": 2016,
"month": 3,
"s1": 0.40,
"s2": 103,
"s3": 22.07,
"s4": 63,
"s5": -5.3,
},
{
"year": 2016,
"month": 4,
"s1": 0.39,
"s2": 104,
"s3": 22.04,
"s4": 61,
"s5": -7.0,
},
{
"year": 2016,
"month": 5,
"s1": 0.38,
"s2": 105,
"s3": 22.2,
"s4": 56,
"s5": -11.3,
},
{
"year": 2016,
"month": 6,
"s1": 0.38,
"s2": 106,
"s3": 22.05,
"s4": 56,
"s5": -12.8,
},
{
"year": 2016,
"month": 7,
"s1": 0.37,
"s2": 102,
"s3": 22.7,
"s4": 58,
"s5": -9.7,
},
{
"year": 2016,
"month": 8,
"s1": 0.37,
"s2": 105,
"s3": 21.20,
"s4": 53,
"s5": -6.2,
},
{
"year": 2016,
"month": 9,
"s1": 0.35,
"s2": 108,
"s3": 20.52,
"s4": 64,
"s5": -5.3,
},
{
"year": 2016,
"month": 10,
"s1": 0.37,
"s2": 103,
"s3": 20.92,
"s4": 62,
"s5": -3.3,
},
{
"year": 2016,
"month": 11,
"s1": 0.36,
"s2": 107,
"s3": 21.11,
"s4": 51,
"s5": -5.7,
},
{
"year": 2016,
"month": 12,
"s1": 0.37,
"s2": 114,
"s3": 22.08,
"s4": 56,
"s5": -8.3,
},
{
"year": 2017,
"month": 1,
"s1": 0.35,
"s2": 103,
"s3": 22.07,
"s4": 56,
"s5": -7.2,
},
{
"year": 2017,
"month": 2,
"s1": 0.36,
"s2": 108,
"s3": 22.2,
"s4": 63,
"s5": -9.0,
},
{
"year": 2017,
"month": 3,
"s1": 0.37,
"s2": 96,
"s3": 25.67,
"s4": 62,
"s5": -9.4,
},
{
"year": 2017,
"month": 4,
"s1": 0.38,
"s2": 102,
"s3": 21.94,
"s4": 63,
"s5": -9.5,
},
{
"year": 2017,
"month": 5,
"s1": 0.35,
"s2": 114,
"s3": 22.07,
"s4": 61,
"s5": -9.0,
},
{
"year": 2017,
"month": 6,
"s1": 0.36,
"s2": 103,
"s3": 21.51,
"s4": 54,
"s5": -9.7,
},
{
"year": 2017,
"month": 7,
"s1": 0.36,
"s2": 107,
"s3": 22.2,
"s4": 65,
"s5": -11.3,
},
{
"year": 2017,
"month": 8,
"s1": 0.35,
"s2": 108,
"s3": 20.2,
"s4": 61,
"s5": -12.8,
},
{
"year": 2017,
"month": 9,
"s1": 0.36,
"s2": 103,
"s3": 22.15,
"s4": 50,
"s5": -8,
},
{
"year": 2017,
"month": 10,
"s1": 0.37,
"s2": 112,
"s3": 22.07,
"s4": 65,
"s5": -9,
},
];
var colors = {
data1: '#ff6666',
data2: '#737575',
data3: '#27A5CF',
data4: '#C9BC22',
data5: '#4D4B39',
default: '#000000'
};
var filterShade = {
data1: '#ff666685',
data2: '#73757587',
data3: '#27a5cf87',
data4: '#c9bc228f',
data5: '#4d4b3985',
default: '#000000'
}
var xAxisDates = [];
xAxisDates.push('x')
for (var i = 0; i < stuff.length; ++i) {
xAxisDates.push(stuff[i]["year"] + '-' + stuff[i]["month"] + '-1');
}
var factor = {
s1: 10,
s2: 0.08,
s3: 0.52,
s4: 0.18,
s5: 20
};
var vals1 = [];
vals1.push('data1');
for (var i = 0; i < stuff.length; ++i) {
vals1.push(stuff[i]["s1"] * factor.s1)
};
var vals2 = [];
vals2.push('data2');
for (var i = 0; i < stuff.length; ++i) {
vals2.push(stuff[i]["s2"] * factor.s2)
};
var vals3 = [];
vals3.push('data3');
for (var i = 0; i < stuff.length; ++i) {
vals3.push(stuff[i]["s3"] * factor.s3)
};
var vals4 = [];
vals4.push('data4');
for (var i = 0; i < stuff.length; ++i) {
vals4.push(stuff[i]["s4"] * factor.s4)
};
var vals5 = [];
vals5.push('data5');
for (var i = 0; i < stuff.length; ++i) {
vals5.push((stuff[i]["s5"] + factor.s5))
};
var chart = c3.generate({
size: {
width: 2000,
height: 1000
},
legend: {
show: false
},
data: {
onmouseover: onMouseover,
onmouseout: onMouseout,
type: 'spline',
groups: [
['data1', 'data2', 'data3', 'data4', 'data5']
],
order: null,
x: 'x',
columns: [xAxisDates, vals1, vals2, vals3, vals4, vals5],
labels: {
format: {
data1: function(value, id, i, j) {
return value / factor.s1 + "%"
},
data2: function(value, id, i, j) {
return Math.round(value / factor.s2);
},
data3: function(value, id, i, j) {
return value / factor.s3 + "%"
},
data4: function(value, id, i, j) {
return Math.round(value / factor.s4) + "%"
},
data5: function(value, id, i, j) {
return (value - factor.s5).toFixed(1);
}
}
},
colors: {
data1: colors.data1,
data2: colors.data2,
data3: colors.data3,
data4: colors.data4,
data5: colors.data5,
}
},
tooltip: {
show: false,
format: {
value: function(value, ratio, id) {
if (id == 'data1') {
return value / factor.s1 + "%";
}
if (id == 'data2') {
return (value / factor.s2).toFixed(2);
}
if (id == 'data3') {
return value / factor.s3 + "%";
}
if (id == 'data4') {
return Math.round(value / factor.s4) + "%"
}
if (id == 'data5') {
return (value - factor.s5).toFixed(1);
}
}
},
},
axis: {
y: {
min: 0,
max: 60,
padding: {
top: 0,
bottom: 0,
left: 0,
right: 0
}
},
x: {
type: 'timeseries',
padding: {
top: 0,
bottom: 0,
left: 0
},
tick: {
values: ['2015-12-01', '2016-02-01', '2016-04-01', '2016-06-01', '2016-08-01', '2016-10-01', '2016-12-01', '2017-02-01', '2017-04-01', '2017-06-01', '2017-08-01', '2017-10-01'],
format: "%b-%y",
}
}
},
grid: {
x: {
lines: [
{value: '2017-06-01', text: 'June 17'}
]
}
},
padding: {
right: 30,
left: 80,
top: 80
},
point: {
show: false
}
});
d3.select("svg").append("foreignObject")
.attr("x", -20)
.attr("width", 200)
.style("text-anchor", "left")
//.append("xhtml:body")
.style("font", "14px 'Calibri'")
.style("text-align", "center")
.style("line-height", "0.2")
.html("<h3>ABC Approval of</h3></b><h3>need for speed Strategy</h3></b><h3>(Dec-15)</h3>");
d3.select("svg").append("circle")
.attr("class", "xyZero")
.attr("cx", 80)
.attr("cy", 970)
.attr("r", 6);
d3.select("svg").append("circle")
.attr("class", "data1")
.attr("cx", 40)
.attr("cy", 920)
.attr("r", 30)
.attr("fill", colors.data1);
d3.select("svg").append("foreignObject")
.attr("x", -60)
.attr("y", 920)
.attr('class', 'dataLabel')
.attr("width", 200)
.html("Data 1<sup>1</sup>");
d3.select("svg").append("circle")
.attr("class", "data2")
.attr("cx", 40)
.attr("cy", 800)
.attr("r", 30)
.attr("fill", colors.data2);
d3.select("svg").append("foreignObject")
.attr("x", -60)
.attr("y", 800)
.attr('class', 'dataLabel')
.attr("width", 200)
.html("Data 2<sup>1</sup>");
d3.select("svg").append("circle")
.attr("class", "data3")
.attr("cx", 40)
.attr("cy", 620)
.attr("r", 30)
.attr("fill", colors.data3);
d3.select("svg").append("foreignObject")
.attr("x", -60)
.attr("y", 620)
.attr('class', 'dataLabel')
.attr("width", 200)
.html("Data 3<sup>1</sup>");
d3.select("svg").append("circle")
.attr("class", "data4")
.attr("cx", 40)
.attr("cy", 470)
.attr("r", 30)
.attr("fill", colors.data4);
d3.select("svg").append("foreignObject")
.attr("x", -60)
.attr("y", 470)
.attr('class', 'dataLabelBlack')
.attr("width", 200)
.html("Data 4<sup>1</sup>");
d3.select("svg").append("circle")
.attr("class", "data5")
.attr("cx", 40)
.attr("cy", 280)
.attr("r", 30)
.attr("fill", colors.data5);
d3.select("svg").append("foreignObject")
.attr("x", -60)
.attr("y", 280)
.attr('class', 'dataLabel')
.attr("width", 200)
.html("Data 5<sup>1</sup>");
console.log(d3.select("svg defs"));
var data1 = d3.select("svg defs").append("filter")
.attr("id", "filter1")
.attr("height", "130%")
.append('feFlood')
.attr('flood-color', filterShade.data1)
.append('feComposite').append('feComposite')
.attr('in', 'SourceGraphic');
var filter1 = d3.select("#filter1")
.append('feComposite')
.attr('in', 'SourceGraphic');
var data2 = d3.select("svg defs").append("filter")
.attr("id", "filter2")
.attr("height", "130%")
.append('feFlood')
.attr('flood-color', filterShade.data2)
.append('feComposite').append('feComposite')
.attr('in', 'SourceGraphic');
var filter2 = d3.select("#filter2")
.append('feComposite')
.attr('in', 'SourceGraphic');
var data3 = d3.select("svg defs").append("filter")
.attr("id", "filter3")
.attr("height", "130%")
.append('feFlood')
.attr('flood-color', filterShade.data3)
.append('feComposite').append('feComposite')
.attr('in', 'SourceGraphic');
var filter3 = d3.select("#filter3")
.append('feComposite')
.attr('in', 'SourceGraphic');
var data4 = d3.select("svg defs").append("filter")
.attr("id", "filter4")
.attr("height", "130%")
.append('feFlood')
.attr('flood-color', filterShade.data4)
.append('feComposite').append('feComposite')
.attr('in', 'SourceGraphic');
var filter4 = d3.select("#filter4")
.append('feComposite')
.attr('in', 'SourceGraphic');
var data5 = d3.select("svg defs").append("filter")
.attr("id", "filter5")
.attr("height", "130%")
.append('feFlood')
.attr('flood-color', filterShade.data5)
.append('feComposite').append('feComposite')
.attr('in', 'SourceGraphic');
var filter1 = d3.select("#filter5")
.append('feComposite')
.attr('in', 'SourceGraphic');
function onMouseover(elemData) {
var id = elemData.name;
if (id == undefined) id = elemData.id;
var el = d3.select('.c3-chart-texts')
.selectAll(".c3-chart-text > .c3-texts-" + id + " > .c3-text-" + elemData.index);
if (id == 'data1')
el.attr('filter', 'url(#filter1)');
if (id == 'data2')
el.attr('filter', 'url(#filter2)');
if (id == 'data3')
el.attr('filter', 'url(#filter3)');
if (id == 'data4')
el.attr('filter', 'url(#filter4)');
if (id == 'data5')
el.attr('filter', 'url(#filter5)');
}
function onMouseout(elemData) {
var id = elemData.name;
if (id == undefined) id = elemData.id;
var el = d3.select('.c3-chart-texts')
.selectAll(".c3-chart-text > .c3-texts-" + id + " > .c3-text-" + elemData.index);
el.attr('filter', '')
}
Use a min/max configuration also for x axis like below:
x: {
type: 'timeseries',
min: '2015-11-01',
max: '2017-11-01',
...
}
I am trying to use the onMouseover api of c3js to include a data-label highlight instead of a tooltip (that is just highlight data-labels for that x-axis unlike showing it in a tooltip).
But when I try and look at the data, it (function) doesn't give me anything. I believe there is a different approach to do this.
My fiddle: https://jsfiddle.net/sourabhtewari/8uz8j01j/198/
my code:
var stuff = [{
"year": 2015,
"month": 12,
"s1": 0.38,
"s2": 100,
"s3": 22.2,
"s4": 61,
"s5": -7,
},
{
"year": 2016,
"month": 1,
"s1": 0.39,
"s2": 101,
"s3": 22.12,
"s4": 62,
"s5": -6.0,
},
{
"year": 2016,
"month": 2,
"s1": 0.43,
"s2": 102,
"s3": 22.11,
"s4": 65,
"s5": -5.7,
},
{
"year": 2016,
"month": 3,
"s1": 0.40,
"s2": 103,
"s3": 22.07,
"s4": 63,
"s5": -5.3,
},
{
"year": 2016,
"month": 4,
"s1": 0.39,
"s2": 104,
"s3": 22.04,
"s4": 61,
"s5": -7.0,
},
{
"year": 2016,
"month": 5,
"s1": 0.38,
"s2": 105,
"s3": 22.2,
"s4": 56,
"s5": -11.3,
},
{
"year": 2016,
"month": 6,
"s1": 0.38,
"s2": 106,
"s3": 22.05,
"s4": 56,
"s5": -12.8,
},
{
"year": 2016,
"month": 7,
"s1": 0.37,
"s2": 102,
"s3": 22.7,
"s4": 58,
"s5": -9.7,
},
{
"year": 2016,
"month": 8,
"s1": 0.37,
"s2": 105,
"s3": 21.20,
"s4": 53,
"s5": -6.2,
},
{
"year": 2016,
"month": 9,
"s1": 0.35,
"s2": 108,
"s3": 20.52,
"s4": 64,
"s5": -5.3,
},
{
"year": 2016,
"month": 10,
"s1": 0.37,
"s2": 103,
"s3": 20.92,
"s4": 62,
"s5": -3.3,
},
{
"year": 2016,
"month": 11,
"s1": 0.36,
"s2": 107,
"s3": 21.11,
"s4": 51,
"s5": -5.7,
},
{
"year": 2016,
"month": 12,
"s1": 0.37,
"s2": 114,
"s3": 22.08,
"s4": 56,
"s5": -8.3,
},
{
"year": 2017,
"month": 1,
"s1": 0.35,
"s2": 103,
"s3": 22.07,
"s4": 56,
"s5": -7.2,
},
{
"year": 2017,
"month": 2,
"s1": 0.36,
"s2": 108,
"s3": 22.2,
"s4": 63,
"s5": -9.0,
},
{
"year": 2017,
"month": 3,
"s1": 0.37,
"s2": 96,
"s3": 25.67,
"s4": 62,
"s5": -9.4,
},
{
"year": 2017,
"month": 4,
"s1": 0.38,
"s2": 102,
"s3": 21.94,
"s4": 63,
"s5": -9.5,
},
{
"year": 2017,
"month": 5,
"s1": 0.35,
"s2": 114,
"s3": 22.07,
"s4": 61,
"s5": -9.0,
},
{
"year": 2017,
"month": 6,
"s1": 0.36,
"s2": 103,
"s3": 21.51,
"s4": 54,
"s5": -9.7,
},
{
"year": 2017,
"month": 7,
"s1": 0.36,
"s2": 107,
"s3": 22.2,
"s4": 65,
"s5": -11.3,
},
{
"year": 2017,
"month": 8,
"s1": 0.35,
"s2": 108,
"s3": 20.2,
"s4": 61,
"s5": -12.8,
},
{
"year": 2017,
"month": 9,
"s1": 0.36,
"s2": 103,
"s3": 22.15,
"s4": 50,
"s5": -8,
},
{
"year": 2017,
"month": 10,
"s1": 0.37,
"s2": 112,
"s3": 22.07,
"s4": 65,
"s5": -9,
},
];
var xAxisDates = [];
xAxisDates.push('x')
for (var i = 0; i < stuff.length; ++i) {
xAxisDates.push(stuff[i]["year"] + '-' + stuff[i]["month"] + '-1');
}
var factor = {
s1: 10,
s2: 0.08,
s3: 0.52,
s4: 0.18,
s5: 20
};
var vals1 = [];
vals1.push('data1');
for (var i = 0; i < stuff.length; ++i) {
vals1.push(stuff[i]["s1"] * factor.s1)
};
var vals2 = [];
vals2.push('data2');
for (var i = 0; i < stuff.length; ++i) {
vals2.push(stuff[i]["s2"] * factor.s2)
};
var vals3 = [];
vals3.push('data3');
for (var i = 0; i < stuff.length; ++i) {
vals3.push(stuff[i]["s3"] * factor.s3)
};
var vals4 = [];
vals4.push('data4');
for (var i = 0; i < stuff.length; ++i) {
vals4.push(stuff[i]["s4"] * factor.s4)
};
var vals5 = [];
vals5.push('data5');
for (var i = 0; i < stuff.length; ++i) {
vals5.push((stuff[i]["s5"] + factor.s5))
};
var chart = c3.generate({
size: {
width: 2000,
height: 600
},
onmouseover: onMouseover,
data: {
type: 'spline',
groups: [
['data1', 'data2', 'data3', 'data4', 'data5']
],
order: null,
x: 'x',
columns: [xAxisDates, vals1, vals2, vals3, vals4, vals5],
labels: {
format: {
data1: function(value, id, i, j) {
return value / factor.s1 + "%"
},
data2: function(value, id, i, j) {
return Math.round(value / factor.s2);
},
data3: function(value, id, i, j) {
return value / factor.s3 + "%"
},
data4: function(value, id, i, j) {
return Math.round(value / factor.s4) + "%"
},
data5: function(value, id, i, j) {
return (value - factor.s5).toFixed(1);
}
}
},
colors: {
data1: '#ff6666',
data2: '#737575',
data3: '#27A5CF',
data4: '#C9BC22',
data5: '#4D4B39'
}
},
tooltip: {
format: {
value: function(value, ratio, id) {
if (id == 'data1') {
return value / factor.s1 + "%";
}
if (id == 'data2') {
return (value / factor.s2).toFixed(2);
}
if (id == 'data3') {
return value / factor.s3 + "%";
}
if (id == 'data4') {
return Math.round(value / factor.s4) + "%"
}
if (id == 'data5') {
return (value - factor.s5).toFixed(1);
}
}
},
},
axis: {
y: {
min: 0,
max: 60,
padding: {
top: 0,
bottom: 0
}
},
x: {
type: 'timeseries',
padding: {
top: 0,
bottom: 0,
left:0
},
tick: {
values: ['2015-12-01', '2016-02-01', '2016-04-01', '2016-06-01', '2016-08-01', '2016-10-01', '2016-12-01', '2017-02-01', '2017-04-01', '2017-06-01', '2017-08-01', '2017-10-01'],
format: "%b-%y",
}
}
},
padding: {
right: 30
},
point: {
show: false
}
});
function onMouseover(elemData) {
console.log(elemData);
}
Attach onmouseover/onmouseout handler to data instead of to general chart:
data: {
onmouseover: onMouseover,
onmouseout: onMouseout,
...
}
and apply style to data text labels:
function onMouseover(elemData) {
var id = elemData.name;
if (id==undefined) id = elemData.id;
var el = d3 .select('.c3-chart-texts')
.selectAll(".c3-chart-text > .c3-texts-"+id+" > .c3-text-"+elemData.index);
el.style("stroke", "red");
}
function onMouseout(elemData) {
var id = elemData.name;
if (id==undefined) id = elemData.id;
var el = d3 .select('.c3-chart-texts')
.selectAll(".c3-chart-text > .c3-texts-"+id+" > .c3-text-"+elemData.index);
el.style("stroke", "none");
}
Check the fiddle updated: https://jsfiddle.net/beaver71/2pxjdcm2/
var stuff = [{
"year": 2015,
"month": 12,
"s1": 0.38,
"s2": 100,
"s3": 22.2,
"s4": 61,
"s5": -7,
},
{
"year": 2016,
"month": 1,
"s1": 0.39,
"s2": 101,
"s3": 22.12,
"s4": 62,
"s5": -6.0,
},
{
"year": 2016,
"month": 2,
"s1": 0.43,
"s2": 102,
"s3": 22.11,
"s4": 65,
"s5": -5.7,
},
{
"year": 2016,
"month": 3,
"s1": 0.40,
"s2": 103,
"s3": 22.07,
"s4": 63,
"s5": -5.3,
},
{
"year": 2016,
"month": 4,
"s1": 0.39,
"s2": 104,
"s3": 22.04,
"s4": 61,
"s5": -7.0,
},
{
"year": 2016,
"month": 5,
"s1": 0.38,
"s2": 105,
"s3": 22.2,
"s4": 56,
"s5": -11.3,
},
{
"year": 2016,
"month": 6,
"s1": 0.38,
"s2": 106,
"s3": 22.05,
"s4": 56,
"s5": -12.8,
},
{
"year": 2016,
"month": 7,
"s1": 0.37,
"s2": 102,
"s3": 22.7,
"s4": 58,
"s5": -9.7,
},
{
"year": 2016,
"month": 8,
"s1": 0.37,
"s2": 105,
"s3": 21.20,
"s4": 53,
"s5": -6.2,
},
{
"year": 2016,
"month": 9,
"s1": 0.35,
"s2": 108,
"s3": 20.52,
"s4": 64,
"s5": -5.3,
},
{
"year": 2016,
"month": 10,
"s1": 0.37,
"s2": 103,
"s3": 20.92,
"s4": 62,
"s5": -3.3,
},
{
"year": 2016,
"month": 11,
"s1": 0.36,
"s2": 107,
"s3": 21.11,
"s4": 51,
"s5": -5.7,
},
{
"year": 2016,
"month": 12,
"s1": 0.37,
"s2": 114,
"s3": 22.08,
"s4": 56,
"s5": -8.3,
},
{
"year": 2017,
"month": 1,
"s1": 0.35,
"s2": 103,
"s3": 22.07,
"s4": 56,
"s5": -7.2,
},
{
"year": 2017,
"month": 2,
"s1": 0.36,
"s2": 108,
"s3": 22.2,
"s4": 63,
"s5": -9.0,
},
{
"year": 2017,
"month": 3,
"s1": 0.37,
"s2": 96,
"s3": 25.67,
"s4": 62,
"s5": -9.4,
},
{
"year": 2017,
"month": 4,
"s1": 0.38,
"s2": 102,
"s3": 21.94,
"s4": 63,
"s5": -9.5,
},
{
"year": 2017,
"month": 5,
"s1": 0.35,
"s2": 114,
"s3": 22.07,
"s4": 61,
"s5": -9.0,
},
{
"year": 2017,
"month": 6,
"s1": 0.36,
"s2": 103,
"s3": 21.51,
"s4": 54,
"s5": -9.7,
},
{
"year": 2017,
"month": 7,
"s1": 0.36,
"s2": 107,
"s3": 22.2,
"s4": 65,
"s5": -11.3,
},
{
"year": 2017,
"month": 8,
"s1": 0.35,
"s2": 108,
"s3": 20.2,
"s4": 61,
"s5": -12.8,
},
{
"year": 2017,
"month": 9,
"s1": 0.36,
"s2": 103,
"s3": 22.15,
"s4": 50,
"s5": -8,
},
{
"year": 2017,
"month": 10,
"s1": 0.37,
"s2": 112,
"s3": 22.07,
"s4": 65,
"s5": -9,
},
];
var xAxisDates = [];
xAxisDates.push('x')
for (var i = 0; i < stuff.length; ++i) {
xAxisDates.push(stuff[i]["year"] + '-' + stuff[i]["month"] + '-1');
}
var factor = {
s1: 10,
s2: 0.08,
s3: 0.52,
s4: 0.18,
s5: 20
};
var vals1 = [];
vals1.push('data1');
for (var i = 0; i < stuff.length; ++i) {
vals1.push(stuff[i]["s1"] * factor.s1)
};
var vals2 = [];
vals2.push('data2');
for (var i = 0; i < stuff.length; ++i) {
vals2.push(stuff[i]["s2"] * factor.s2)
};
var vals3 = [];
vals3.push('data3');
for (var i = 0; i < stuff.length; ++i) {
vals3.push(stuff[i]["s3"] * factor.s3)
};
var vals4 = [];
vals4.push('data4');
for (var i = 0; i < stuff.length; ++i) {
vals4.push(stuff[i]["s4"] * factor.s4)
};
var vals5 = [];
vals5.push('data5');
for (var i = 0; i < stuff.length; ++i) {
vals5.push((stuff[i]["s5"] + factor.s5))
};
var chart = c3.generate({
size: {
width: 2000,
height: 600
},
//onmouseover: onMouseover,
data: {
onmouseover: onMouseover,
onmouseout: onMouseout,
type: 'spline',
groups: [
['data1', 'data2', 'data3', 'data4', 'data5']
],
order: null,
x: 'x',
columns: [xAxisDates, vals1, vals2, vals3, vals4, vals5],
labels: {
format: {
data1: function(value, id, i, j) {
return value / factor.s1 + "%"
},
data2: function(value, id, i, j) {
return Math.round(value / factor.s2);
},
data3: function(value, id, i, j) {
return value / factor.s3 + "%"
},
data4: function(value, id, i, j) {
return Math.round(value / factor.s4) + "%"
},
data5: function(value, id, i, j) {
return (value - factor.s5).toFixed(1);
}
}
},
colors: {
data1: '#ff6666',
data2: '#737575',
data3: '#27A5CF',
data4: '#C9BC22',
data5: '#4D4B39'
}
},
tooltip: {
format: {
value: function(value, ratio, id) {
if (id == 'data1') {
return value / factor.s1 + "%";
}
if (id == 'data2') {
return (value / factor.s2).toFixed(2);
}
if (id == 'data3') {
return value / factor.s3 + "%";
}
if (id == 'data4') {
return Math.round(value / factor.s4) + "%"
}
if (id == 'data5') {
return (value - factor.s5).toFixed(1);
}
}
},
},
axis: {
y: {
min: 0,
max: 60,
padding: {
top: 0,
bottom: 0
}
},
x: {
type: 'timeseries',
padding: {
top: 0,
bottom: 0,
left:0
},
tick: {
values: ['2015-12-01', '2016-02-01', '2016-04-01', '2016-06-01', '2016-08-01', '2016-10-01', '2016-12-01', '2017-02-01', '2017-04-01', '2017-06-01', '2017-08-01', '2017-10-01'],
format: "%b-%y",
}
}
},
padding: {
right: 30
},
point: {
show: false
}
});
function onMouseover(elemData) {
console.log(elemData.name, elemData.index);
var id = elemData.name;
if (id==undefined) id = elemData.id;
var el = d3 .select('.c3-chart-texts')
.selectAll(".c3-chart-text > .c3-texts-"+id+" > .c3-text-"+elemData.index);
el.style("stroke", "red");
}
function onMouseout(elemData) {
var id = elemData.name;
if (id==undefined) id = elemData.id;
var el = d3 .select('.c3-chart-texts')
.selectAll(".c3-chart-text > .c3-texts-"+id+" > .c3-text-"+elemData.index);
el.style("stroke", "none");
//rgb(255, 102, 102)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<link href="https://rawgit.com/masayuki0812/c3/master/c3.css" rel="stylesheet"/>
<script src="https://rawgit.com/masayuki0812/c3/master/c3.js"></script>
<div id="chart"></div>
I am going to use sparkline in the" usage" column, just in the way that the two sparkline chart cover each other
There is a problem because when I click on the button Edite "sparkline" disappears.
Or click on "usage column" think that happens.
Why tooltip as bad as it can be displayed tooltip not regular.
Why sparkline "usage column" in all rows, there is only one row
jsfiddle code
$(document).ready(function () {
//var ds = new kendo.data.DataSource({
// transport: {
// read: {
// url: '/api/clientssnapshot',
// dataType: 'json',
// type: 'get',
// cache: false
// },
// },
// batch: true,
// pageSize: 10,
// schema: {
// model: {
// fields: {
// Mac: { editable: false, nullable: true },
// RadioName: { type: "string", validation: { required: true } },
// ApName: { type: "string", validation: { required: true, min: 1 } },
// RemoteIp: { type: "boolean" },
// TX: { type: "number", validation: { min: 0, required: true } },
// RX: { type: "number", validation: { min: 0, required: true } },
// Signal: { type: "number", validation: { min: 0, required: true } },
// Uptime: { type: "number", validation: { min: 0, required: true } },
// }
// }
// }
//});
$('.table').kendoGrid({
dataSource: {
data: [{
"Mac": "D4:CA:6D:28:D1:05",
"RadioName": "D4CA6D28D105",
"ApName": "Om11",
"ApIp": "10.20.0.100",
"TX": 48,
"RX": 54,
"Signal": -64,
"Uptime": 797452,
"InRate": 0,
"OutRate": 0,
"AccountingId": 759,
"AccountingName": "فرشاد صفایی زاده",
"RemoteIp": "188.121.123.56",
"IsValidInScan": true,
"Comments": null,
"ApScanId": 26173,
"InRateHistory": "0, 0, 0, 0, 2, 0, 2, 16, 96, 16, 96, 16, 96, 113, 31, 113, 31, 113, 31, 0",
"OutRateHistory": "0, 5, 3, 5, 2, 5, 2, 35, 136, 35, 136, 35, 136, 164, 51, 164, 51, 164, 51, 4"
}, {
"Mac": "00:15:6D:BD:64:92",
"RadioName": "Behrooz Hoseyn",
"ApName": "Om11",
"ApIp": "10.20.0.100",
"TX": 48,
"RX": 18,
"Signal": -65,
"Uptime": 797446,
"InRate": 2,
"OutRate": 2,
"AccountingId": 750,
"AccountingName": "بهروز حسینی",
"RemoteIp": "188.121.123.48",
"IsValidInScan": true,
"Comments": null,
"ApScanId": 26173,
"InRateHistory": "0, 0, 0, 0, 2, 0, 2, 16, 96, 16, 96, 16, 96, 113, 31, 113, 31, 113, 31, 0",
"OutRateHistory": "0, 5, 3, 5, 2, 5, 2, 35, 136, 35, 136, 35, 136, 164, 51, 164, 51, 164, 51, 4"
}, {
"Mac": "00:15:6D:1E:B3:6C",
"RadioName": "UBNT",
"ApName": "Om11",
"ApIp": "10.20.0.100",
"TX": 54,
"RX": 24,
"Signal": -65,
"Uptime": 310336,
"InRate": 0,
"OutRate": 0,
"AccountingId": 820,
"AccountingName": "******",
"RemoteIp": "10.10.15.129",
"IsValidInScan": true,
"Comments": null,
"ApScanId": 26173,
"InRateHistory": "0, 0, 0, 0, 2, 0, 2, 16, 96, 16, 96, 16, 96, 113, 31, 113, 31, 113, 31, 0",
"OutRateHistory": "0, 5, 3, 5, 2, 5, 2, 35, 136, 35, 136, 35, 136, 164, 51, 164, 51, 164, 51, 4"
}, {
"Mac": "00:15:6D:1C:B1:89",
"RadioName": "Grous Tajhiz P",
"ApName": "Om11",
"ApIp": "10.20.0.100",
"TX": 48,
"RX": 6,
"Signal": -62,
"Uptime": 122116,
"InRate": 0,
"OutRate": 0,
"AccountingId": 595,
"AccountingName": "حمید شمس لواسانی",
"RemoteIp": "188.121.124.17",
"IsValidInScan": true,
"Comments": null,
"ApScanId": 26173,
"InRateHistory": "0, 0, 0, 0, 2, 0, 2, 16, 96, 16, 96, 16, 96, 113, 31, 113, 31, 113, 31, 0",
"OutRateHistory": "0, 5, 3, 5, 2, 5, 2, 35, 136, 35, 136, 35, 136, 164, 51, 164, 51, 164, 51, 4"
}, {
"Mac": "00:27:22:3E:91:12",
"RadioName": "Anbar Aminzade",
"ApName": "Om1",
"ApIp": "10.20.0.101",
"TX": 36,
"RX": 36,
"Signal": -68,
"Uptime": 1131461,
"InRate": 4,
"OutRate": 4,
"AccountingId": 977,
"AccountingName": "انبار شهید امین زاده ",
"RemoteIp": "188.121.123.31",
"IsValidInScan": true,
"Comments": null,
"ApScanId": 26173,
"InRateHistory": "0, 0, 0, 0, 2, 0, 2, 16, 96, 16, 96, 16, 96, 113, 31, 113, 31, 113, 31, 0",
"OutRateHistory": "0, 5, 3, 5, 2, 5, 2, 35, 136, 35, 136, 35, 136, 164, 51, 164, 51, 164, 51, 4"
}, {
"Mac": "00:15:6D:1A:59:D0",
"RadioName": "UBNT",
"ApName": "Om1",
"ApIp": "10.20.0.101",
"TX": 36,
"RX": 12,
"Signal": -73,
"Uptime": 734737,
"InRate": 2,
"OutRate": 2,
"AccountingId": 820,
"AccountingName": "******",
"RemoteIp": "10.10.15.76",
"IsValidInScan": true,
"Comments": null,
"ApScanId": 26173,
"InRateHistory": "0, 0, 0, 0, 2, 0, 2, 16, 96, 16, 96, 16, 96, 113, 31, 113, 31, 113, 31, 0",
"OutRateHistory": "0, 5, 3, 5, 2, 5, 2, 35, 136, 35, 136, 35, 136, 164, 51, 164, 51, 164, 51, 4"
}, {
"Mac": "00:15:6D:E2:2D:13",
"RadioName": "UBNT",
"ApName": "Om1",
"ApIp": "10.20.0.101",
"TX": 54,
"RX": 36,
"Signal": -72,
"Uptime": 848,
"InRate": 0,
"OutRate": 0,
"AccountingId": 820,
"AccountingName": "******",
"RemoteIp": "10.10.15.67",
"IsValidInScan": true,
"Comments": null,
"ApScanId": 26173,
"InRateHistory": "0, 0, 0, 0, 2, 0, 2, 16, 96, 16, 96, 16, 96, 113, 31, 113, 31, 113, 31, 0",
"OutRateHistory": "0, 5, 3, 5, 2, 5, 2, 35, 136, 35, 136, 35, 136, 164, 51, 164, 51, 164, 51, 4"
}, {
"Mac": "00:27:22:32:24:C9",
"RadioName": "UBNT",
"ApName": "Om7",
"ApIp": "10.20.0.100",
"TX": 36,
"RX": 24,
"Signal": -78,
"Uptime": 731588,
"InRate": 0,
"OutRate": 0,
"AccountingId": 820,
"AccountingName": "******",
"RemoteIp": "10.10.15.188",
"IsValidInScan": true,
"Comments": null,
"ApScanId": 26173,
"InRateHistory": "0, 0, 0, 0, 2, 0, 2, 16, 96, 16, 96, 16, 96, 113, 31, 113, 31, 113, 31, 0",
"OutRateHistory": "0, 5, 3, 5, 2, 5, 2, 35, 136, 35, 136, 35, 136, 164, 51, 164, 51, 164, 51, 4"
}, {
"Mac": "00:15:6D:FE:BB:E2",
"RadioName": "ketabforooshie",
"ApName": "Om7",
"ApIp": "10.20.0.100",
"TX": 54,
"RX": 36,
"Signal": -72,
"Uptime": 240361,
"InRate": 0,
"OutRate": 0,
"AccountingId": 533,
"AccountingName": "قاسم رضاپور",
"RemoteIp": "188.121.124.214",
"IsValidInScan": true,
"Comments": null,
"ApScanId": 26173,
"InRateHistory": "0, 0, 0, 0, 2, 0, 2, 16, 96, 16, 96, 16, 96, 113, 31, 113, 31, 113, 31, 0",
"OutRateHistory": "0, 5, 3, 5, 2, 5, 2, 35, 136, 35, 136, 35, 136, 164, 51, 164, 51, 164, 51, 4"
}, {
"Mac": "00:27:22:D2:86:56",
"RadioName": "UBNT",
"ApName": "Om7",
"ApIp": "10.20.0.100",
"TX": 48,
"RX": 12,
"Signal": -72,
"Uptime": 126430,
"InRate": 0,
"OutRate": 0,
"AccountingId": 1453,
"AccountingName": "حسن قربانی",
"RemoteIp": "188.121.123.154",
"IsValidInScan": true,
"Comments": null,
"ApScanId": 26173,
"InRateHistory": "0, 0, 0, 0, 2, 0, 2, 16, 96, 16, 96, 16, 96, 113, 31, 113, 31, 113, 31, 0",
"OutRateHistory": "0, 5, 3, 5, 2, 5, 2, 35, 136, 35, 136, 35, 136, 164, 51, 164, 51, 164, 51, 4"
}, {
"Mac": "00:27:22:78:A3:19",
"RadioName": "UBNT",
"ApName": "Om7",
"ApIp": "10.20.0.100",
"TX": 54,
"RX": 54,
"Signal": -56,
"Uptime": 58617,
"InRate": 0,
"OutRate": 0,
"AccountingId": 820,
"AccountingName": "******",
"RemoteIp": "10.10.15.39",
"IsValidInScan": true,
"Comments": null,
"ApScanId": 26173,
"InRateHistory": "0, 0, 0, 0, 2, 0, 2, 16, 96, 16, 96, 16, 96, 113, 31, 113, 31, 113, 31, 0",
"OutRateHistory": "0, 5, 3, 5, 2, 5, 2, 35, 136, 35, 136, 35, 136, 164, 51, 164, 51, 164, 51, 4"
}
]
},
sortable: true,
groupable: true,
selectable: true,
navigatable: true,
height: 500,
scrollable: true,
pageable: true,
columns: [{
field: "Mac",
title: "Mac",
width: 170
}, {
field: "RadioName",
title: "Radio",
width: 150
}, {
field: "ApName",
title: "Ap",
width: 80,
template: '#=ApName#'
}, {
field: "RemoteIp",
title: "Remote IP",
width: 160,
template: '#=RemoteIp#'
}, {
field: "AccountingName",
title: "Name",
width: 130,
template: ' #= AccountingName # '
}, {
field: "TX",
title: "TX",
width: 44
}, {
field: "RX",
title: "RX",
width: 50
}, {
field: "Signal",
title: "Signal",
width: 50
}, {
field: "Uptime",
title: "Uptime",
width: 78
}, {
field: "Usage",
title: "Usage",
template: '<span id="sparkline"></span>'
}, {
command: ["edit"],
title: " "
}],
editable: "popup",
});
$(".ref").click(function () {
$(".table").data("kendoGrid").dataSource.read();
});
$("#sparkline").kendoSparkline({
type: "area",
series: [{
name: "World",
data: [15.7, 16.7, 20, 23.5, 26.6, 24.8, 24.1, 20.1, 14.1, 8.6, 2.5, 3.5],
}, {
name: 'New York',
data: [0.7, 0.8, 5.7, 11.3, 17.0, 22.0, 24.8, 24.1, 20.1, 14.1, 8.6, 2.5],
}],
categoryAxis: {
categories: [2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015]
}
});
<div class="span6 box gradient main_stting">
<div class="dataTables_filter" id="txtSearch">
<label>Search:
<input type="text" aria-controls="DataTables_Table_0">
</label>
</div>
<div class="title">
<button class="btn ref" type="submit">Refresh</button>
<h3></h3>
</div>
<div class="content">
<div class="table"></div>
</div>
thank you
After edit, the HTML elements are destroyed and recreated when the Grid updates. You will need to recreate your sparklines. It is basically the same as this issue.