I'm messing around with d3 but a basic line seems to be inverted. Seems like the origin is at the top of the page (like the default orientation on a page). However, i assumed that the d3 points were relative to the svg graph.
How can I set the origin to be bottom left of graph? (Without transforming the data)
// make dataset
var dataset = [[1,1]];
for (var x = 0; x< 10000; x +=1) {
var y = x*x;
dataset.push([x, y])
}
// set graph dims
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// Adds the svg canvas
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");;
// add scale
var x = d3.scale.linear().domain([0, 200]).range([0, width]);
var y = d3.scale.linear().domain([0, 1000]).range([height, 0]);
// add x axis
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(10);
// add y axis
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(10);
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (height - margin.top - margin.bottom) + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
//add dots
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) {
return d[0];
})
.attr("cy", function(d) {
return d[1];
})
.attr("r", 1);
Regarding the orientation: it has nothing to do with D3. D3 manipulates DOM elements, and normally (but not always) we use D3 to manipulate SVG elements. And the SVG specs say that the origin (0,0) is at the top left corner.
Regarding your problem: you correctly set the y scale to go from the bottom to the top, but you simply forgot to use it! Use the scale:
.attr("cx", function(d) {
return x(d[0]);
})
.attr("cy", function(d) {
return y(d[1]);
})
Here is the demo:
// make dataset
var dataset = [[1,1]];
for (var x = 0; x< 100; x +=1) {
var y = x*x;
dataset.push([x, y])
}
// set graph dims
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// Adds the svg canvas
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");;
// add scale
var x = d3.scale.linear().domain([0, 200]).range([0, width]);
var y = d3.scale.linear().domain([0, 1000]).range([height - margin.bottom - margin.top, 0]);
// add x axis
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(10);
// add y axis
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(10);
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (height - margin.top - margin.bottom) + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
//add dots
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) {
return x(d[0]);
})
.attr("cy", function(d) {
return y(d[1]);
})
.attr("r", 1);
line, path {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Related
For learning how to use d3.js, I was trying to use titanic dataset for learning available on kaggle.
I am trying to achieve the objective:
Make a scatterplot of age vs fare with age in x axis and fare in y axis
Use the sex column to have male as square and female as circles in the scatterplot
Have opacity to indicate the condition - survived or not survived.
I have used the following code:
// set the dimensions and margins of the graph
var margin = {
top: 10,
right: 30,
bottom: 30,
left: 60
},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
//Read the data
d3.csv("https://gist.githubusercontent.com/michhar/2dfd2de0d4f8727f873422c5d959fff5/raw/fa71405126017e6a37bea592440b4bee94bf7b9e/titanic.csv", function(data) {
// Add X axis
var x = d3.scaleLinear()
.domain([0, 80])
.range([0, width]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, 600])
.range([height, 0]);
svg.append("g")
.call(d3.axisLeft(y));
// Add dots
svg.append('g')
.selectAll("dot")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) {
return x(d.age);
})
.attr("cy", function(d) {
return y(d.fare);
})
.attr("r", 1.5)
.style("fill", "#69b3a2")
})
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
The condition I want to implement is:
if(d.sex == "female"){
return d3.symbolCircle;
} else if (d.sex == "male"){
return d3.symbolSquare;
}
But, being absolute new to the syntax, I am not understanding how. Also, How to have two colors for the 3rd objective of indicating survivor vs dead.
Can anyone help me please. I really thank you in advance.
In case you do not have the dataset, it can also be found here.
Firstly, you need to look at your data. Your properties are all uppercase, and reading a CSV file always means that you need to parse your rows: numbers and dates are still strings, you need to cast them as such:
// set the dimensions and margins of the graph
var margin = {
top: 10,
right: 30,
bottom: 30,
left: 60
},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
//Read the data
d3.csv("https://gist.githubusercontent.com/michhar/2dfd2de0d4f8727f873422c5d959fff5/raw/fa71405126017e6a37bea592440b4bee94bf7b9e/titanic.csv", function(rawData) {
// All values are strings here, so we need to parse some of them.
// You can do that using `+x` or `Number(x)`, where `x = "123"`
const data = rawData.map(function(d) {
return {
age: Number(d.Age),
// cabin: d.Cabin,
// embarked: e.Embarked,
fare: Number(d.Fare),
// name: d.Name,
// parch: Number(d.Parch),
// passengerId: Number(d.PassengerId)
// pclass: Number(Pclass),
sex: d.Sex,
// sibSp: Number(d.SibSp),
survived: d.Survived === "1"
// ticket: d.Ticket,
};
});
// Add X axis
var x = d3.scaleLinear()
.domain([0, 80])
.range([0, width]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, 600])
.range([height, 0]);
svg.append("g")
.call(d3.axisLeft(y));
// Add dots
svg.append('g')
.selectAll("dot")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) {
return x(d.age);
})
.attr("cy", function(d) {
return y(d.fare);
})
.attr("r", 1.5)
.style("fill", "#69b3a2")
})
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
Secondly, circles can only be circles. To draw both squares and circles, you'll need to use <path>. Your d3.symbol* were correct, but you need to access their .draw() function. d3.path is a generator to easily draw the d attribute of a path:
// set the dimensions and margins of the graph
var margin = {
top: 10,
right: 30,
bottom: 30,
left: 60
},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
//Read the data
d3.csv("https://gist.githubusercontent.com/michhar/2dfd2de0d4f8727f873422c5d959fff5/raw/fa71405126017e6a37bea592440b4bee94bf7b9e/titanic.csv", function(rawData) {
// All values are strings here, so we need to parse some of them.
// You can do that using `+x` or `Number(x)`, where `x = "123"`
const data = rawData.map(function(d) {
return {
age: Number(d.Age),
// cabin: d.Cabin,
// embarked: e.Embarked,
fare: Number(d.Fare),
// name: d.Name,
// parch: Number(d.Parch),
// passengerId: Number(d.PassengerId)
// pclass: Number(Pclass),
sex: d.Sex,
// sibSp: Number(d.SibSp),
survived: d.Survived === "1"
// ticket: d.Ticket,
};
});
// Add X axis
var x = d3.scaleLinear()
.domain([0, 80])
.range([0, width]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, 600])
.range([height, 0]);
svg.append("g")
.call(d3.axisLeft(y));
// Add dots
svg.append('g')
.selectAll("path")
.data(data)
.enter()
.append("path")
.attr("transform", function(d) {
return "translate(" + [x(d.age), y(d.fare)] + ")";
})
.attr("d", function(d) {
const path = d3.path();
const shape = d.sex == "female" ? d3.symbolCircle : d3.symbolSquare;
shape.draw(path, 8);
return path.toString();
})
.style("fill", "#69b3a2")
})
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
(As an aside, you also could have drawn rects for both, but given the female ones rounded corners with the rx attribute).
Finally, you can use a colour scale for fill, with d3.scaleOrdinal, but if you have only two colours and don't use it to colour multiple things (like a line chart and a legend), just use an if statement:
// set the dimensions and margins of the graph
var margin = {
top: 10,
right: 30,
bottom: 30,
left: 60
},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
//Read the data
d3.csv("https://gist.githubusercontent.com/michhar/2dfd2de0d4f8727f873422c5d959fff5/raw/fa71405126017e6a37bea592440b4bee94bf7b9e/titanic.csv", function(rawData) {
// All values are strings here, so we need to parse some of them.
// You can do that using `+x` or `Number(x)`, where `x = "123"`
const data = rawData.map(function(d) {
return {
age: Number(d.Age),
// cabin: d.Cabin,
// embarked: e.Embarked,
fare: Number(d.Fare),
// name: d.Name,
// parch: Number(d.Parch),
// passengerId: Number(d.PassengerId)
// pclass: Number(Pclass),
sex: d.Sex,
// sibSp: Number(d.SibSp),
survived: d.Survived === "1"
// ticket: d.Ticket,
};
});
// Add X axis
var x = d3.scaleLinear()
.domain([0, 80])
.range([0, width]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, 600])
.range([height, 0]);
svg.append("g")
.call(d3.axisLeft(y));
// Add dots
svg.append('g')
.selectAll("path")
.data(data)
.enter()
.append("path")
.attr("transform", function(d) {
return "translate(" + [x(d.age), y(d.fare)] + ")";
})
.attr("d", function(d) {
const path = d3.path();
const shape = d.sex == "female" ? d3.symbolCircle : d3.symbolSquare;
shape.draw(path, 8);
return path.toString();
})
.style("fill", function(d) {
return d.survived ? "#69b3a2" : "#ddd";
})
})
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
One interesting thing in SVG is that a <rect> with the rx and ry attributes equal to half its width or height (those being the same, of course) becomes effectively a circle.
So, supposing you have
var diameter = 3;
All you need is:
.attr("rx", function(d) {
return d.Sex === "male" ? 0 : diameter / 2
})
.attr("ry", function(d) {
return d.Sex === "male" ? 0 : diameter / 2
})
And, of course, subtract the x and y positions by half the diameter (i.e., the radius).
That seems like a hack, but the advantage of that approach is that it's quite easy to transition between the square and the "circle" by just changing the rx/ry values (one can transition paths, but that's a bit more complicated). Have a look at this transition, with an exaggerated radius and a clipped domain:
// set the dimensions and margins of the graph
var margin = {
top: 10,
right: 30,
bottom: 10,
left: 60
},
width = 660 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var diameter = 12;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
//Read the data
d3.csv("https://gist.githubusercontent.com/michhar/2dfd2de0d4f8727f873422c5d959fff5/raw/fa71405126017e6a37bea592440b4bee94bf7b9e/titanic.csv", row, function(data) {
// Add X axis
var x = d3.scaleLinear()
.domain([0, 80])
.range([0, width]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, 300])
.range([height, 0]);
svg.append("g")
.call(d3.axisLeft(y));
// Add dots
svg.append('g')
.selectAll("dot")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) {
return x(d.Age) - diameter / 2;
})
.attr("y", function(d) {
return y(d.Fare) - diameter / 2;
})
.attr("width", diameter)
.attr("height", diameter)
.style("fill", "#69b3a2")
.transition()
.duration(2000)
.attr("rx", function(d) {
return d.Sex === "male" ? 0 : diameter / 2
})
.attr("ry", function(d) {
return d.Sex === "male" ? 0 : diameter / 2
})
.style("fill", function(d) {
return +d.Survived ? "#69b3a2" : "tan"
});
})
function row(d) {
d.Age = +d.Age;
d.Fare = +d.Fare;
return d;
}
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
I have a dataset that has 3 fields a name, a min value (value2) and a max value (value) that represent a range.
//The data
var data =[{"name": 'Scotty', "value2":0, "value":17},
{"name":'Dick', "value2":10, "value":17},
{"name":'James', "value2":5, "value":null},
{"name":'Max', "value2":2, "value":9}]
Currently I have it represented with a bar chart using this code that works ok except in cases where the points value2 and value are very close or there is a null value.
//Chart size parameters
var margin = {top: 20, right: 30, bottom: 90, left: 40},
width = 830 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//Chart axis
var x = d3.scaleBand()
.domain(data.map(function(d) { return d.name; }))
.range([2, width])
.scaleBand(0.10);
var y = d3.scaleLinear()
.domain([0, d3.max(data, function(d) { return d.value; })])
.range([height, 0]);
var xAxis = d3.axisBottom(x);
var yAxis = d3.axisLeft(y);
//Initialize chart
var chart = d3.select("#mychart").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 + ")");
//Adding both axis
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
chart.append("g")
.attr("class", "y axis")
.call(yAxis);
//Where the box is drawn
chart.selectAll(".box")
.data(data)
.enter().append("rect")
.attr("class", "box")
.attr("x", function(d) { return x(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return y(d.value2) - y(d.value); }) //Plot range
.attr("width", x.bandwidth()); //spacing for bars
Is there a way to plot the data with the following:
if two points are present plot both of them with a path connecting them
if one point is present with a null in field value only plot the one point
I am creating a d3 plot of some data and I would like to have zooming and panning capabilities. When I plot my data and zoom in or pan, the data can move outside the defined plot area without being clipped.
As you can see in the image, the orange dots appear on the green area when I would have expected them to be clipped.
Any ideas on what is wrong?
Demo at http://jsfiddle.net/ng6srz4o/
var dataX = [1,3,5,7,9];
var dataY = [0,2,4,6,8];
var margin, padding, outerWidth, outerHeight, innerWidth, innerHeight;
var xAxis, yAxis, xScale, yScale, svg, container, zoom, tooltip;
// assign the dimensions here, works the best
outerWidth = Math.ceil ($(window).width() * 0.90);
outerHeight = Math.ceil ($(window).height() * 0.55);
// distance of the outerRect from the innerRect
margin = { top: 16, right: 16, bottom: 32, left: 32 };
// distance of the actual data from the innerRect
padding = { top: 0, right: 32, bottom: 0, left: 32 };
innerWidth = outerWidth - margin.left - margin.right;
innerHeight = outerHeight - margin.top - margin.bottom;
xScale = d3.scale.linear()
.domain([ d3.min(dataX), d3.max(dataX) ])
.range([ padding.left, innerWidth - padding.right ]);
yScale = d3.scale.linear()
.domain([ d3.min(dataY), d3.max(dataY) ])
.range([ innerHeight - padding.bottom, padding.top ]);
zoom = d3.behavior.zoom()
.x(xScale).y(yScale)
.scaleExtent([1, 20])
.on ("zoom", onZoom);
svg = d3.select("#svgContainer")
.append("svg:svg")
.attr("width", outerWidth)
.attr("height", outerHeight)
.style("background", "green")
.append("svg:g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
drawAxis();
// white rect behind the graph
var rect = svg.append("svg:rect")
.attr("width", innerWidth)
.attr("height", innerHeight)
.style("fill", "gray");
container = svg.append("svg:g");
var graph = container.append("svg:g").selectAll("scatter-dots")
.data(dataY)
.enter().append("svg:circle")
.attr("cy", function (d) { return yScale(d); })
.attr("cx", function (d,i) { return xScale(dataX[i]); })
.attr("r", 10)
.style("fill", "orange");
function onZoom() {
var t = d3.event.translate,
s = d3.event.scale;
container.attr("transform", "translate(" + t + ")scale(" + s + ")");
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
}
function drawAxis()
{
xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
svg.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0," + innerHeight + ")")
.style("fill", "blue")
.call(xAxis);
yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
svg.append("svg:g")
.attr("class", "y axis")
.style("fill", "red")
.call(yAxis);
}
Right now, you have all of your points contained in a g element, which won't clip any of the points that you're drawing.
If you want to clip the points, you can add an svg element to your main svg, which will clip anything contained within, like so:
var mainSVG = d3.select('body').append('svg')
.attr('width', outerWidth)
.attr('height', outerHeight);
var innerSVG = mainSVG.append('svg')
.attr('width', innerWidth)
.attr('height', innerHeight)
.selectAll('scatter-dots')
...
(updated) fiddle.
I'm looking for some times now to allow zooming into one or the other (or both) directions X/Y on a d3.js chart. Here is my simple chart :
var margin = parseInt(attrs.margin) || {top: 20, right: 20, bottom: 20, left: 20},
padding = parseInt(attrs.padding) || 30;
var svg = d3.select(ele[0]).append('svg')
.style('width', '100%');
var width = 960 - margin.left - margin.right - padding,
height = 500 - margin.top - margin.bottom;
// X scale
var x = d3.scale.linear().domain([d3.min(data, function(d){return d.x;}), d3.max(data, function(d){return d.x})]).range([0, width]);
// Y scale
var y = d3.scale.linear().domain([0, d3.max(data, function(d){return d.y;})]).range([height, 0]);
svg.call(d3.behavior.zoom().x(x).y(x).on("zoom", zoomed));
// create a line function that can convert data[] into x and y points
var line = d3.svg.line()
.interpolate("basis")
// assign the X function to plot our line as we wish
.x(function(d,i) { return x(d.x); })
.y(function(d) { return y(d.y); });
// Add an SVG element with the desired dimensions and margin.
svg.attr("width", width + margin.left + margin.right + padding)
.attr("height", height + margin.top + margin.bottom);
var g = svg.append("g").attr("transform", "translate(60," + margin.top + ")");
// create Axis
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var yAxis = d3.svg.axis().scale(y).orient("left");
// Add the x-axis.
g.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + 0 + "," + height + ")")
.call(xAxis);
// Add the y-axis to the left
g.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + 0 +",0)")
.call(yAxis);
// objects for the zooming clipping
var clip = g.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height);
var chartBody = g.append("g")
.attr("clip-path", "url(#clip)");
chartBody.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
function zoomed() {
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
svg.select(".line")
.attr("class", "line")
.attr("d", line);
}
Is there a way to use a key modifier when zooming (for example when shift+mouse wheel, only zoom X) ?
I am trying to essentially rotate this horizontal bar chart into a vertical bar chart, but can't figure out how to do so. I can create a normal column chart, but once I try to put in the negative values and compute the y and height, all hell breaks loose. Here's my fiddle. (At least I was able to create the y-axis (I think).)
What am I doing wrong here?
var data = [{"letter":"A",'frequency':10},{"letter":"B","frequency":-5},{"letter":"C","frequency":7}];
var margin = {top: 20, right: 20, bottom: 30, left: 40}, width = 750 - margin.left - margin.right, height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal().rangeRoundBands([0, width], .1);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
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 x0 = Math.max(-d3.min(data), d3.max(data));
x.domain(data.map(function(d) { return d.letter; }));
y.domain([d3.min(data, function(d) { return d.frequency; }), d3.max(data, function(d) { return d.frequency; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.selectAll(".bar")
.data(data).enter().append("rect")
.attr("class", "bar")
.attr("x", function(d, i) { return x(d.letter); })
.attr("y", function(d, i) { return x(Math.min(0, d.frequency));})
.attr("width", x.rangeBand())
.attr("height", function(d) { return Math.abs(x(d.frequency) - x(0)); });
Looks like there are two problems here:
The typos: .attr("y", function(d, i) { return x(...);}) should now be .attr("y", function(d, i) { return y(...);}). Same is true for the scales in your height attribute.
The change from a 0 base on the X axis to a 0 base on the Y axis. With a zero-based bar on the X axis, the x attribute of the bar is x(0). With a 0 based bar on the Y axis, the y attribute of the bar is not y(0), but y(value) (because the "base" of the bar is no longer the leading edge of the rectangle) - so in this code you need to use Math.max(0, value) (which will give y(value) for positive values) instead of Math.min(0, value):
svg.selectAll(".bar")
// ...snip...
.attr("y", function(d, i) { return y(Math.max(0, d.frequency));})
.attr("height", function(d) { return Math.abs(y(d.frequency) - y(0)); });
See updated fiddle: http://jsfiddle.net/pYZn8/5/