histogram = (data, options={}) => {
const {
num_bins = Math.pow(data.length, 1/3),
bar_color = 'steelblue',
chart_width = width,
chart_height = 300,
margin = {top: 20, right: 20, bottom: 30, left: 40},
} = options;
const x = d3.scaleLinear()
.domain(d3.extent(data)).nice()
.range([margin.left, chart_width - margin.right]);
const bins = d3.histogram()
.domain(x.domain())
.thresholds(num_bins)
(data);
const y = d3.scaleLinear()
.domain([0, d3.max(bins, d => d.length)]).nice()
.range([chart_height - margin.bottom, margin.top]);
const xAxis = g => g
.attr("transform", `translate(0,${chart_height - margin.bottom})`)
.call(d3.axisBottom(x).ticks(chart_width / 80 ).tickSizeOuter(0))
.call(g => g.append("text")
.attr("x", chart_width - margin.right)
.attr("y", -4)
.attr("fill", "currentColor")
.attr("font-weight", "bold")
.attr("text-anchor", "end")
.text(data.x));
const yAxis = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y).ticks(chart_height / 60))
.call(g => g.select(".domain").remove())
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", 4)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text(data.y));
const svg = d3.create("svg")
.attr("width", chart_width)
.attr("height", chart_height);
svg.append("g")
.attr("fill", bar_color)
.selectAll("rect")
.data(bins)
.join("rect")
.attr("x", d => x(d.x0))
.attr("width", d => Math.max(1, x(d.x1) - x(d.x0) - 2))
.attr("y", d => y(d.length))
.attr("height", d => y(0) - y(d.length));
svg.append("g")
.call(xAxis);
svg.append("g")
.call(yAxis);
return svg.node();
}