Hexagonal binning with exponential random numbers - javascript

I have been following the block that uses hexagonal binning of random points with the normal distribution but instead trying to tailor it to the exponential distribution.
The code runs, but the output seems to show a mirror along the x-axis. That is, the points are all clustered along the upper-left instead of lower-left. I've been playing with the transform function but can't quite get it. What am I missing? JSFiddle
<!DOCTYPE html>
<style>
.hexagon {
stroke: #000;
stroke-width: 0.5px;
}
</style>
<svg width="500" height="200"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-hexbin.v0.2.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var randomX = d3.randomExponential(1 / 30),
randomY = d3.randomExponential(1 / 30),
points = d3.range(2000).map(function() { return [randomX(), randomY()]; });
var color = d3.scaleSequential(d3.interpolateLab("white", "steelblue"))
.domain([0, 20]);
var hexbin = d3.hexbin()
.radius(5)
.extent([[0, 0], [width, height]]);
var x = d3.scaleLinear()
.domain([0, width])
.range([0, width]);
var y = d3.scaleLinear()
.domain([0, height])
.range([height, 0]);
g.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
g.append("g")
.attr("class", "hexagon")
.attr("clip-path", "url(#clip)")
.selectAll("path")
.data(hexbin(points))
.enter().append("path")
.attr("d", hexbin.hexagon())
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.attr("fill", function(d) { return color(d.length); });
</script>

You set your scales, but you never use them:
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
})
Solution: use your scales:
.attr("transform", function(d) {
return "translate(" + x(d.x) + "," + y(d.y) + ")";
//scales here --------^--------------^
})
Here is your code with that change:
var svg = d3.select("svg"),
margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var randomX = d3.randomExponential(1 / 30),
randomY = d3.randomExponential(1 / 30),
points = d3.range(2000).map(function() {
return [randomX(), randomY()];
});
var color = d3.scaleSequential(d3.interpolateLab("white", "steelblue"))
.domain([0, 20]);
var hexbin = d3.hexbin()
.radius(5)
.extent([
[0, 0],
[width, height]
]);
var x = d3.scaleLinear()
.domain([0, width])
.range([0, width]);
var y = d3.scaleLinear()
.domain([0, height])
.range([height, 0]);
g.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
g.append("g")
.attr("class", "hexagon")
.attr("clip-path", "url(#clip)")
.selectAll("path")
.data(hexbin(points))
.enter().append("path")
.attr("d", hexbin.hexagon())
.attr("transform", function(d) {
return "translate(" + x(d.x) + "," + y(d.y) + ")";
})
.attr("fill", function(d) {
return color(d.length);
});
.hexagon {
stroke: #000;
stroke-width: 0.5px;
}
<svg width="500" height="200"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-hexbin.v0.2.min.js"></script>

Related

d3.js scaleBand()'s ticks coming out of place

I am using scaleBand() for both x and y axes for a bar chart. For some reason, the height of the bars are in between the ticks of the y axis. I would appreciate any help. Here is my code:
var margin_ = { top: 20, right: 20, bottom: 30, left: 40 },
width_ = 960 - margin_.left - margin_.right,
height_ = 500 - margin_.top - margin_.bottom;
var svg_ = d3.select("body").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 + ")");
var x = d3.scaleBand()
.range([0, width_])
.padding(0.2)
var y = d3.scaleBand()
.range([height_, 0]);
x.domain(satisfactScaleKeyValues);
y.domain(graphYvalues);
svg_.selectAll(".bar")
.data(datas)
.enter().append("rect")
.attr("class", "bar__")
.attr("x", function (d) { return x(d.variable); })
.attr("width", x.bandwidth())
.attr("y", function (d) { return y(d.satisLevel); })
.attr("height", function (d) { return height_ - y(d.satisLevel); });
// add the x Axis
svg_.append("g")
.attr("transform", "translate(0," + height_ + ")")
.call(d3.axisBottom(x));
// add the y Axis
svg_.append("g")
.call(d3.axisLeft(y))
You should use d3.scalePoint, which would provide a better translation from an ordinal domain to linear points on a range:
let datas = [{variable: 1, satisLevel: "Neutral"}]
var margin_ = { top: 20, right: 50, bottom: 30, left: 75 },
width_ = 960 - margin_.left - margin_.right,
height_ = 500 - margin_.top - margin_.bottom;
var svg_ = d3.select("body").append("svg")
.attr("width", width_ + margin_.left + margin_.right)
.attr("height", height_ + margin_.top + margin_.bottom)
var g = svg_.append("g")
.attr("transform",
"translate(" + margin_.left + "," + margin_.top + ")");
var x = d3.scaleBand()
.range([0, width_])
.padding(0.2)
var y = d3.scalePoint()
.range([height_, 0])
.padding(0.2)
x.domain([1]);
y.domain(["Not satisfied", "Neutral", "Satisfied"]);
g.selectAll(".bar")
.data(datas)
.enter().append("rect")
.attr("class", "bar__")
.attr("x", function (d) { return x(d.variable); })
.attr("width", x.bandwidth())
.attr("y", function (d) { return y(d.satisLevel); })
.attr("height", function (d) { return height_ - y(d.satisLevel); });
// add the x Axis
g.append("g")
.attr("transform", "translate(0," + height_ + ")")
.call(d3.axisBottom(x));
// add the y Axis
g.append("g")
.call(d3.axisLeft(y))
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
If you want the height of the lowest Satisfaction Level to be more than zero, you can add padding to the scalePoint, like in the example.

X axis values for histogram

How should one define values range for X axis?
I've took example which used decimal values in range 0 to 1, and this clearly doesn't work for greater numbers.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.bar rect {
fill: steelblue;
}
.bar text {
fill: #fff;
font: 10px sans-serif;
}
</style>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
// var data = d3.range(1000).map(d3.randomBates(10));
var data = [1321017167, 1421017167, 1421017167, 1421017167, 1521017167, 1521017167];
var formatCount = d3.format(",.0f");
var svg = d3.select("svg"),
margin = {top: 10, right: 30, bottom: 30, left: 30},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleLinear()
.rangeRound([0, width]);
var bins = d3.histogram()
.domain(x.domain())
.thresholds(x.ticks(20))
(data);
var y = d3.scaleLinear()
.domain([0, d3.max(bins, function(d) { return d.length; })])
.range([height, 0]);
var bar = g.selectAll(".bar")
.data(bins)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function(d) { return "translate(" + x(d.x0) + "," + y(d.length) + ")"; });
bar.append("rect")
.attr("x", 1)
.attr("width", x(bins[0].x1) - x(bins[0].x0) - 1)
.attr("height", function(d) { return height - y(d.length); });
bar.append("text")
.attr("dy", ".75em")
.attr("y", 6)
.attr("x", (x(bins[0].x1) - x(bins[0].x0)) / 2)
.attr("text-anchor", "middle")
.text(function(d) { return formatCount(d.length); });
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
</script>
By default, the domain of a linear scale is [0, 1]. You just copied that code from Mike Bostock without changing the domain. In his original code the domain is, coincidentally, the default domain, but in your code you have to define it:
var x = d3.scaleLinear()
.rangeRound([0, width])
.domain(d3.extent(data))//domain here
Here I'm using d3.extent, but you can use any other array you want.
Here is your code with that change only:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.bar rect {
fill: steelblue;
}
.bar text {
fill: #fff;
font: 10px sans-serif;
}
</style>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
// var data = d3.range(1000).map(d3.randomBates(10));
var data = [1321017167, 1421017167, 1421017167, 1421017167, 1521017167, 1521017167];
var formatCount = d3.format(",.0f");
var svg = d3.select("svg"),
margin = {top: 10, right: 30, bottom: 30, left: 30},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleLinear()
.rangeRound([0, width])
.domain(d3.extent(data))
var bins = d3.histogram()
.domain(x.domain())
.thresholds(x.ticks(20))
(data);
var y = d3.scaleLinear()
.domain([0, d3.max(bins, function(d) { return d.length; })])
.range([height, 0]);
var bar = g.selectAll(".bar")
.data(bins)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function(d) { return "translate(" + x(d.x0) + "," + y(d.length) + ")"; });
bar.append("rect")
.attr("x", 1)
.attr("width", x(bins[0].x1) - x(bins[0].x0) - 1)
.attr("height", function(d) { return height - y(d.length); });
bar.append("text")
.attr("dy", ".75em")
.attr("y", 6)
.attr("x", (x(bins[0].x1) - x(bins[0].x0)) / 2)
.attr("text-anchor", "middle")
.text(function(d) { return formatCount(d.length); });
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
</script>
PS: You'll have to adjust the horizontal position of the rectangles.

d3.hexbin data points not being displayed correctly

The following snippet is basically this example https://bl.ocks.org/mbostock/4248145 but with custom data points. No matter how I scale or modify my points array, the hexagons are always at the upper left corner, though it seems that the distribution is displayed correctly.
How can I fix this?
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var points = [[1,1]]
var color = d3.scaleSequential(d3.interpolateLab("white", "#5B85AA"))
.domain([0, 3]);
var hexbin = d3.hexbin()
.radius(20)
.size([0, 3]);
var x = d3.scaleLinear()
.domain([1, 4])
.range([0, width]);
var y = d3.scaleLinear()
.domain([1, 4])
.range([height, 0]);
g.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
g.append("g")
.attr("class", "hexagon")
.attr("clip-path", "url(#clip)")
.selectAll("path")
.data(hexbin(points))
.enter().append("path")
.attr("d", hexbin.hexagon())
.attr("transform", function(d) { return "translate(" + 0 + "," + height + ")"; })
.transition()
.duration(1000)
.delay(function (d, i) {
return i * 10;
})
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.attr("fill", function(d) { return color(d.length); });
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-hexbin.v0.2.min.js"></script>
<svg width="960" height="500"></svg>
That's the expected result, since your data is just:
[1, 1]
Which is a single data point next to the origin. For instance, using the same code but creating 1000 random data points from 0 to the width...
var points = d3.range(1000).map(d=>([Math.random()*width, Math.random()*width]));
... will have a different result:
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var points = d3.range(1000).map(d=>([Math.random()*width, Math.random()*width]));
var color = d3.scaleSequential(d3.interpolateLab("white", "#5B85AA"))
.domain([0, 3]);
var hexbin = d3.hexbin()
.radius(20)
.size([0, 3]);
var x = d3.scaleLinear()
.domain([1, 4])
.range([0, width]);
var y = d3.scaleLinear()
.domain([1, 4])
.range([height, 0]);
g.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
g.append("g")
.attr("class", "hexagon")
.attr("clip-path", "url(#clip)")
.selectAll("path")
.data(hexbin(points))
.enter().append("path")
.attr("d", hexbin.hexagon())
.attr("transform", function(d) { return "translate(" + 0 + "," + height + ")"; })
.transition()
.duration(1000)
.delay(function (d, i) {
return i * 10;
})
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.attr("fill", function(d) { return color(d.length); });
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-hexbin.v0.2.min.js"></script>
<svg width="960" height="500"></svg>
Besides that, what you said:
No matter how I scale or modify my points array, the hexagons are always at the upper left corner.
Is not accurate. For instance, this is the same code, but using [[100,100]]. You can see the hexagon further down and to the right:
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var points = [[100,100]];
var color = d3.scaleSequential(d3.interpolateLab("white", "#5B85AA"))
.domain([0, 3]);
var hexbin = d3.hexbin()
.radius(20)
.size([0, 3]);
var x = d3.scaleLinear()
.domain([1, 4])
.range([0, width]);
var y = d3.scaleLinear()
.domain([1, 4])
.range([height, 0]);
g.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
g.append("g")
.attr("class", "hexagon")
.attr("clip-path", "url(#clip)")
.selectAll("path")
.data(hexbin(points))
.enter().append("path")
.attr("d", hexbin.hexagon())
.attr("transform", function(d) { return "translate(" + 0 + "," + height + ")"; })
.transition()
.duration(1000)
.delay(function (d, i) {
return i * 10;
})
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.attr("fill", function(d) { return color(d.length); });
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-hexbin.v0.2.min.js"></script>
<svg width="960" height="500"></svg>

d3 v4 Why can't I get my x axis and bars to line up?

I've got as far as making my bar chart with an x axis but can't work out why I can't get the bars and the tick spacing on the x axis to line up exactly. At the moment the ticks are slightly to the right of center of the bar.
csv file example:
crop,records
CASSAVA,350
MAIZE,226
TOMATOES,137
code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Abbie's attempt at D3</title>
<script type="text/javascript" src="https://d3js.org/d3.v4.min.js"></script>
<style>
svg {
background-color: rgba(227, 227, 227, 0.97);
}
.bar {
margin: 20px;
}
</style>
</head>
<body>
<script type="text/javascript">
var margin = {top: 20, right: 30, bottom: 100, left: 40};
var w = 500 - margin.left - margin.right;
var h = 500 - margin.top - margin.bottom;
var barPadding = 5;
var cropData;
// load the csv file
d3.csv("crops.csv", function(error, data) {
data.forEach(function(d) {
d.records = +d.records;
});
cropData = data;
var arrayLength = cropData.length;
var yMax = d3.max(cropData, function(d) {
return d.records;
});
var yScale = d3.scaleLinear()
.domain([0, yMax])
.range([h, 0]);
var xScale = d3.scaleBand()
.domain(cropData.map(function(d) {
return d.crop;
}))
.rangeRound([0, w]);
// create the svg
var svg = d3.select("body")
.append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// bars
svg.selectAll("rect")
.data(cropData)
.enter()
.append("rect")
.attr("x", function(d, i) {
return i * xScale.bandwidth() + 5;
})
.attr("y", function(d) {
return yScale(d.records);
})
.attr("width", xScale.bandwidth() - 5)
.attr("height", function(d) {
return h - yScale(d.records);
})
.attr("fill", "teal")
.attr("class", "bar");
// x Axis
var xAxis = d3.axisBottom(xScale);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d) {
return "rotate(-60)"
});
// y Axis
var yAxis = d3.axisLeft(yScale);
svg.append("g")
.call(yAxis);
</script>
</body>
Am I doing it right with i * xScale.bandwidth() + 5 and xScale.bandwidth() - 5 in the x and width attributes of rect or is this not the correct way to do it? How do I change the spacing of the ticks on the x axis if I change the bars? They both use xScale so I feel it must be something to do with that.
I'd make the following changes.
First, introduce a padding to your xScale:
var xScale = d3.scaleBand()
.domain(cropData.map(function(d) {
return d.crop;
}))
.rangeRound([0, w])
.padding(0.1);
This will space the bars by a percent of the bandWidth.
Second, place your bars using your xScale:
svg.selectAll("rect")
.data(cropData)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(d.crop); //<-- place by xScale
})
.attr("y", function(d) {
return yScale(d.records);
})
.attr("width", xScale.bandwidth()) //<-- no -5 padding takes care of breaks
Runnable code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Abbie's attempt at D3</title>
<script type="text/javascript" src="https://d3js.org/d3.v4.min.js"></script>
<style>
svg {
background-color: rgba(227, 227, 227, 0.97);
}
.bar {
margin: 20px;
}
</style>
</head>
<body>
<script type="text/javascript">
var margin = {
top: 20,
right: 30,
bottom: 100,
left: 40
};
var w = 500 - margin.left - margin.right;
var h = 500 - margin.top - margin.bottom;
var barPadding = 5;
var cropData;
// load the csv file
//d3.csv("crops.csv", function(error, data) {
var data = [{
"crop": "CASSAVA",
"records": "350"
}, {
"crop": "MAIZE",
"records": "226"
}, {
"crop": "TOMATOES",
"records": "137"
}];
data.forEach(function(d) {
d.records = +d.records;
});
cropData = data;
var arrayLength = cropData.length;
var yMax = d3.max(cropData, function(d) {
return d.records;
});
var yScale = d3.scaleLinear()
.domain([0, yMax])
.range([h, 0]);
var xScale = d3.scaleBand()
.domain(cropData.map(function(d) {
return d.crop;
}))
.rangeRound([0, w])
.padding(0.1);
// create the svg
var svg = d3.select("body")
.append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// bars
svg.selectAll("rect")
.data(cropData)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(d.crop);
})
.attr("y", function(d) {
return yScale(d.records);
})
.attr("width", xScale.bandwidth())
.attr("height", function(d) {
return h - yScale(d.records);
})
.attr("fill", "teal")
.attr("class", "bar");
// x Axis
var xAxis = d3.axisBottom(xScale);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d) {
return "rotate(-60)"
});
// y Axis
var yAxis = d3.axisLeft(yScale);
svg.append("g")
.call(yAxis);
// });
</script>
</body>

Simple line chart joining top of histograms

In the following example I mapped histograms on letter vs frequency. Now, I want a line chart also for the same data without making much change. This means just a red line joining top of histograms. Can someone help me out?
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js"></script>
</head>
<body>
<svg width="960" height="500"></svg>
<script>
//Our basic data
var data = [
{frequency:0.08, letter:"A"},
{frequency:0.11,letter:"B"},
{frequency:0.13,letter:"C"}
];
var svg = d3.select("svg");
var margin = {top: 40, bottom: 40, left: 40, right: 40};
var width = svg.attr("width") - margin.left - margin.right;
var height = svg.attr("height") - margin.top - margin.bottom;
var x = d3.scaleBand().rangeRound([0, width]).padding(0.6);
var y = d3.scaleLinear().rangeRound([height, 0]);
//defining our main g in svg
var g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//Looping for data bars
data.forEach(function(){
x.domain(data.map(function(d) { return d.letter; }));
y.domain([0, d3.max(data, function(d) { return d.frequency; })]);
g
.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g
.append("g")
.call(d3.axisLeft(y).ticks(10, "%"));
g
.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) { return x(d.letter); })
.attr("y", function(d) { return y(d.frequency); })
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d.frequency); });
});
</script>
</body>
</html>
HISTOGRAM:
First, get rid of that data.forEach: why do you want to paint everything 3 times?
After that, define your line generator:
var line = d3.line()
.x(function(d){ return x(d.letter) + x.bandwidth()/2})
.y(function(d){ return y(d.frequency)})
.curve(d3.curveCardinal);;
Here, x.bandwidth()/2 will put the line in the middle of the top of each bar. I'm using d3.curveCardinal, but you have other options for the curve.
Then, append the line:
g.append("path")
.datum(data)
.attr("d", line);
Here is the demo:
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js"></script>
</head>
<body>
<svg width="960" height="500"></svg>
<script>
//Our basic data
var data = [
{frequency:0.08, letter:"A"},
{frequency:0.11,letter:"B"},
{frequency:0.13,letter:"C"}
];
var svg = d3.select("svg");
var margin = {top: 40, bottom: 40, left: 40, right: 40};
var width = svg.attr("width") - margin.left - margin.right;
var height = svg.attr("height") - margin.top - margin.bottom;
var x = d3.scaleBand().rangeRound([0, width]).padding(0.6);
var y = d3.scaleLinear().rangeRound([height, 0]);
//defining our main g in svg
var g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//Looping for data bars
x.domain(data.map(function(d) { return d.letter; }));
y.domain([0, d3.max(data, function(d) { return d.frequency; })]);
var line = d3.line()
.x(function(d){ return x(d.letter) + x.bandwidth()/2})
.y(function(d){ return y(d.frequency)})
.curve(d3.curveCardinal);
g
.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g
.append("g")
.call(d3.axisLeft(y).ticks(10, "%"));
g
.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) { return x(d.letter); })
.attr("y", function(d) { return y(d.frequency); })
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d.frequency); });
g.append("path")
.datum(data)
.attr("d", line)
.attr("stroke", "red")
.attr("fill", "none");
</script>

Categories