I want to flip the line so that the higher value goes up and the lower value goes down. I tried to use scale(1,-1) but it doesn't output anything. Please see my code below
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<div class="paths"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
<script>
var canvas = d3.select(".paths").append("svg")
.attr("width", 500)
.attr("height", 500);
var data = [
{x:10, y:200},
{x:30, y:170},
{x:50, y:70},
{x:70, y:140},
{x:90, y:150},
{x:110, y:120},
{x:130, y:150},
{x:150, y:140},
{x:170, y:110}
];
var group = canvas.append('g')
.attr("transform", "scale(1,1)");
var line = d3.svg.line()
.x(function(d){ return d.x })
.y(function(d){ return d.y });
group.selectAll("path")
.data([data])
.enter()
.append("path")
.attr("d", line)
.attr("fill", "none")
.attr("stroke", "red")
.attr("stroke-width", 2);
</script>
</body>
</html>
https://jsbin.com/dayoxon/7/edit?html,output
You have to use a scale, which by the way will fix another problem you have: your data values should not be (or normally will not be) SVG coordinates.
This is a basic example of a linear scale:
var scale = d3.scale.linear()
.domain([0, 200])
.range([height,0]);
Here, the domain goes from 0 to 200, which is the maximum in your data. Then, those values will be mapped to:
.range([height, 0])
Where height is the height of the SVG.
Finally, use the scale in the line generator:
var line = d3.svg.line()
.x(function(d){ return d.x })
.y(function(d){ return scale(d.y) });
Here is your code with that scale:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<div class="paths"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
<script>
var canvas = d3.select(".paths").append("svg")
.attr("width", 500)
.attr("height", 300);
var data = [
{x:10, y:200},
{x:30, y:170},
{x:50, y:70},
{x:70, y:140},
{x:90, y:150},
{x:110, y:120},
{x:130, y:150},
{x:150, y:140},
{x:170, y:110}
];
var group = canvas.append('g');
var scale = d3.scale.linear()
.domain([0, 200])
.range([300,0]);
var line = d3.svg.line()
.x(function(d){ return d.x })
.y(function(d){ return scale(d.y) });
group.selectAll("path")
.data([data])
.enter()
.append("path")
.attr("d", line)
.attr("fill", "none")
.attr("stroke", "red")
.attr("stroke-width", 2);
</script>
</body>
</html>
Related
I am trying to create a bar chart in d3.js and unfortunately, the bars are not on the axes. They are appearing somewhere else. Can you please point out what I am doing wrong here.
I think it has do with the placing of axes and all but I am unable to figure it out.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<div id="rer"></div>
<script>
var width = 400, height = 400;
var data1 = { 2011: 9, 2012: 12, 2013: 10, 2014: 8, 2015: 12, 2016: 20 }
data_ready = d3.entries(data1);
var barColor = '#50740a';
var svg = d3.select("#rer")
.append("svg")
.attr("width", width)
.attr("height", height);
var xscale = d3.scaleBand()
.domain(data_ready.map((function (d) { return d.key; })))
.range([0, width - 100]);
var yscale = d3.scaleLinear()
.domain([0, d3.max(data_ready, function (d) { return d.value; })])
.range([height / 2, 0]);
var x_axis = d3.axisBottom().scale(xscale);
var y_axis = d3.axisLeft().scale(yscale);
svg.append("g")
.attr("transform", "translate(100, 20)")
.call(y_axis);
var xAxisTranslate = height / 2 + 10;
svg.append("g")
.attr("transform", "translate(100, " + xAxisTranslate + ")")
.call(x_axis);
svg.selectAll(".bar")
.data(data_ready)
.enter().append("rect")
.attr('class', 'bar')
.attr("x", function (d) { return xscale(d.key); })
.attr("y", function (d) { return yscale(d.value); })
.attr("width", xscale.bandwidth())
.attr("height", function (d) { return height - yscale(d.value); })
.attr("fill", barColor)
</script>
</body>
</html>
I tried to correct it and got the graph as below. Can someone please explain the attr ( transform, translate ) thing. This seems to be the issue. I did a trial and error method to place the position of the graph. Is there a better way to do this ?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<div id="rer"></div>
<script>
var width = 400,
height = 400;
var data1 = {
2011: 9,
2012: 12,
2013: 10,
2014: 8,
2015: 12,
2016: 20
}
data_ready = d3.entries(data1);
var barColor = '#50740a';
var svg = d3.select("#rer")
.append("svg")
.attr("width", width)
.attr("height", height);
var xscale = d3.scaleBand()
.domain(data_ready.map((function(d) {
return d.key;
})))
.range([0, width - 100])
.padding(0.1);
var yscale = d3.scaleLinear()
.domain([0, d3.max(data_ready, function(d) {
return d.value;
})])
.range([height / 2, 0]);
var x_axis = d3.axisBottom().scale(xscale);
var y_axis = d3.axisLeft().scale(yscale);
svg.append("g")
.attr("transform", "translate(100, 10)")
.call(y_axis);
var xAxisTranslate = height / 2 + 10;
svg.append("g")
.attr("transform", "translate(100, " + xAxisTranslate + ")")
.call(x_axis);
svg.selectAll(".bar")
.data(data_ready)
.enter().append("rect")
.attr('class', 'bar')
.attr("x", function(d) {
return xscale(d.key);
})
.attr("y", function(d) {
return yscale(d.value);
})
.attr("width", xscale.bandwidth())
.attr("height", function(d) {
return height / 2 - yscale(d.value);
})
.attr("fill", barColor)
.attr("transform", "translate(100, 10)")
</script>
</body>
</html>
This question already has answers here:
Pass parameter with Python Flask in external Javascript
(2 answers)
JavaScript raises SyntaxError with data rendered in Jinja template
(3 answers)
Closed 3 years ago.
I have this in templates/index.html`:
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<style>
</style>
</head>
<body>
<div id="graphDiv"></div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="{{ url_for('static', filename='main.js') }}"></script>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<script>
</script>
</body>
</html>
and I have this in app.py:
import json
from flask import Flask, render_template
import pandas as pd
app = Flask(__name__)
#app.route("/")
def index():
df = pd.read_csv('data.csv').drop('Open', axis=1)
chart_data = df.to_dict(orient='records')
chart_data = json.dumps(chart_data, indent=2)
data = {'chart_data': chart_data}
return render_template("index.html", data=data)
if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0", port =5000)
and I didn't want to put the javascript inline in index.html but instead created a directory called static/ and put there the style.css and main.js and it contains this code:
var graphData = {{ data.chart_data | safe }}
var margin = {top: 30, right: 50, bottom: 30, left: 50};
var svgWidth = 600;
var svgHeight = 270;
var graphWidth = svgWidth - margin.left - margin.right;
var graphHeight = svgHeight - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y-%m-%d").parse;
var x = d3.time.scale().range([0, graphWidth]);
var y = d3.scale.linear().range([graphHeight, 0]);
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
var highLine = d3.svg.line()
.x(function(d) { return x(d.Date); })
.y(function(d) { return y(d.High); });
var closeLine = d3.svg.line()
.x(function(d) { return x(d.Date); })
.y(function(d) { return y(d.Close); });
var lowLine = d3.svg.line()
.x(function(d) { return x(d.Date); })
.y(function(d) { return y(d.Low); });
var area = d3.svg.area()
.x(function(d) { return x(d.Date); })
.y0(function(d) { return y(d.Low); })
.y1(function(d) { return y(d.High); })
var svg = d3.select("#graphDiv")
.append("svg")
.attr("width", svgWidth)
.attr("height", svgHeight)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")")
function drawGraph(data) {
// For each row in the data, parse the date
// and use + to make sure data is numerical
data.forEach(function(d) {
d.Date = parseDate(d.Date);
d.High = +d.High;
d.Close = +d.Close;
d.Low = +d.Low;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.Date; }));
y.domain([d3.min(data, function(d) {
return Math.min(d.High, d.Close, d.Low) }),
d3.max(data, function(d) {
return Math.max(d.High, d.Close, d.Low) })]);
// Add the area path
svg.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area)
// Add the highLine as a green line
svg.append("path")
.style("stroke", "green")
.style("fill", "none")
.attr("class", "line")
.attr("d", highLine(data));
// Add the closeLine as a blue dashed line
svg.append("path")
.style("stroke", "blue")
.style("fill", "none")
.style("stroke-dasharray", ("3, 3"))
.attr("d", closeLine(data));
// Add the lowLine as a red dashed line
svg.append("path")
.style("stroke", "red")
.attr("d", lowLine(data));
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + graphHeight + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
// Add the text for the "High" line
svg.append("text")
.attr("transform", "translate("+(graphWidth+3)+","+y(graphData[0].High)+")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "green")
.text("High");
// Add the text for the "Low" line
svg.append("text")
.attr("transform", "translate("+(graphWidth+3)+","+y(graphData[0].Low)+")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "red")
.text("Low");
// Add the text for the "Close" line
svg.append("text")
.attr("transform", "translate("+(graphWidth+3)+","+y(graphData[0].Close)+")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "blue")
.text("Close");
};
drawGraph(graphData);
when I ran the flask server I get this error: SyntaxError: expected property name, got '{' in the first line of the js code var graphData = {{ data.chart_data | safe }}. Now if I put this javascript code inline in index.html everything works fine. I did some research but nothing fixed my issue and I still don't know why is this happening.
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<style>
</style>
</head>
<body>
<div id="graphDiv"></div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript">
var graphData = {{ data.chart_data | safe }}
</script>
<script type="text/javascript" src="{{ url_for('static', filename='main.js') }}"></script>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</body>
</html>
Its happening because thats not valid JavaScript syntax. Thats Jinja2 templating syntax.
You need to define graphData before you can use it in your main.js file.
In my project, I am using d3.js donut chart to display some data. On click of each of the arc sections, it will be highlighted. The requirement is to add a drop shadow effect to the highlighted section when clicked, so it will highlighted much more. I am using stroke to create a shadow effect, but it is not looking like a shadow.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title> D3 Js Example </title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" media="screen" href="main.css" />
</head>
<body>
<div id="my_dataviz"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.9.7/d3.min.js" ></script>
<script>
var lastSelected = "";
var firstSelected = "";
var width = 450
height = 450
margin = 40
var radius = Math.min(width, height) / 2 - margin
var normalArc = d3.arc().outerRadius(radius - 30).innerRadius(radius - 70);
var biggerArc = d3.arc().outerRadius(radius - 80).innerRadius(radius - 10);
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var data = {a: 9, b: 20, c:30, d:8, e:12}
var color = d3.scaleOrdinal()
.domain(data)
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56"])
var pie = d3.pie()
.value(function(d) {return d.value; })
var data_ready = pie(d3.entries(data))
svg
.selectAll('whatever')
.data(data_ready)
.enter()
.append('path')
.attr('d', normalArc)
.attr('fill', function(d){ return(color(d.data.key)) })
.style("opacity", 0.7)
.attr('d', function(d, index) {
// If this is the first segment make it a wider arc
if (index === 0) {
firstSelected = this;
return biggerArc(d);
} else {
return normalArc(d);
}
}).on("click", function(d) {
if (firstSelected) {
d3.select(firstSelected).attr("d", normalArc).style("stroke-width", "0px")
firstSelected = false;
}
if (lastSelected) {
d3.select(lastSelected).attr("d", normalArc).style("stroke-width", "0px")
}
d3.select(this).attr("d", biggerArc).style("stroke", "black").style("stroke-width", "10px")
.style("stroke-opacity","0.08")
.style('stroke-location',"outer")
.style('paint-order','stroke')
.style('stroke-linejoin',"round")
lastSelected = this;
})
</script>
</body>
</html>
After hours of searching i am able to find the answer. we need to create the filter for the drop shadow and append that to the svg and in which ever arc you want ,you just add the filter as an attribute
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title> D3 Js Example </title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" media="screen" href="main.css" />
</head>
<body>
<div id="my_dataviz"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.9.7/d3.min.js" ></script>
<script>
var lastSelected = "";
var firstSelected = "";
var width = 450
height = 450
margin = 40
var radius = Math.min(width, height) / 2 - margin
var normalArc = d3.arc().outerRadius(radius - 30).innerRadius(radius - 70);
var biggerArc = d3.arc().outerRadius(radius - 80).innerRadius(radius - 10);
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
/* For the drop shadow filter... */
var defs = svg.append("defs");
var filter = defs.append("filter")
.attr("id", "dropshadow")
filter.append("feGaussianBlur")
.attr("in", "SourceAlpha")
.attr("stdDeviation", 4)
.attr("result", "blur");
filter.append("feOffset")
.attr("in", "blur")
.attr("dx", 2)
.attr("dy", 2)
.attr("result", "offsetBlur");
var feMerge = filter.append("feMerge");
feMerge.append("feMergeNode")
.attr("in", "offsetBlur")
feMerge.append("feMergeNode")
.attr("in", "SourceGraphic");
var data = {a: 9, b: 20, c:30, d:8, e:12}
var color = d3.scaleOrdinal()
.domain(data)
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56"])
var pie = d3.pie()
.value(function(d) {return d.value; })
var data_ready = pie(d3.entries(data))
svg
.selectAll('whatever')
.data(data_ready)
.enter()
.append('path')
.attr('d', normalArc)
.attr('fill', function(d){ return(color(d.data.key)) })
.style("opacity", 0.7)
.attr('d', function(d, index) {
// If this is the first segment make it a wider arc
if (index === 0) {
firstSelected = this;
return biggerArc(d);
} else {
return normalArc(d);
}
}).on("click", function(d) {
if (firstSelected) {
d3.select(firstSelected).attr("d", normalArc).attr("filter", "");
firstSelected = false;
}
if (lastSelected) {
d3.select(lastSelected).attr("d", normalArc).attr("filter", "");
}
d3.select(this).attr("d", biggerArc).attr("filter", "url(#dropshadow)");
lastSelected = this;
})
if(firstSelected){
d3.select(firstSelected).attr("filter", "url(#dropshadow)");
}
</script>
</body>
</html>
I'm new here, I've got a problem. Actually, I've been working with D3.js. I tried to work with some graphs and can do it well when I work all it on the same html document. But When I have the D3.js code in a js document and call it from a html document, it doesn't graph anything. It doesn't show me any error but don't know what is happening and why it doesn't graph nothing
function dotChart() {
this.drawChart = function(uri_data) {
d3.json(uri_data, function(error, data){
keys = data.map((o) => {
return Object.keys(o)
}).reduce((prev, curr) => {
return prev.concat(curr)
}).filter((col, i, array) => {
return array.indexOf(col) === i
});
sum = 0;
data.forEach(function(d){
d[keys[1]] = +d[keys[1]]
sum += d[keys[1]];
});
dotChart.X.domain(data.map(function(d){
return d[keys[0]];
}));
dotChart.Y.domain([0, d3.max(data, function(d){
return d[keys[1]]/sum;
})]);
dotChart.SVG.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0, "+ dotChart.HEIGHT + ")")
.call(dotChart.X_AXIS);
dotChart.SVG.append("g")
.attr("class", "y axis")
.call(dotChart.Y_AXIS);
dotChart.SVG.selectAll(".dot")
.data(data)
.enter()
.append("circle")
.attr("class", "dot")
.attr("r", 3)
.attr("cx", function(d){
return dotChart.X(d[keys[0]]) + 13;
})
.attr("cy", function(d){
return dotChart.Y(d[keys[1]]/sum);
})
.attr("fill", "lightblue")
.attr("stroke", "#1A237E");
dotChart.LABELS.append("text")
.attr("transform", "translate(0, "+ dotChart.HEIGHT +")")
.attr("x", (dotChart.WIDTH - dotChart.MARGIN.right))
.attr("dx", "-1.0em")
.attr("dy", "3.0em")
.text("["+ keys[0] +"]");
dotChart.LABELS.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -40)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text(keys[1]);
dotChart.TITLE.append("text")
.attr("x", dotChart.WIDTH/2)
.attr("y", -30)
.attr("text-anchor", "middle")
.attr("font-size", "22px")
.text("Memoria Libre (RAM)");
});
}
}
dotChart.MARGIN = {
top: 70,
right: 30,
bottom: 50,
left: 50
}
dotChart.WIDTH = 500 - dotChart.MARGIN.left - dotChart.MARGIN.right,
dotChart.HEIGHT = 300 - dotChart.MARGIN.top - dotChart.MARGIN.bottom;
dotChart.X = d3.scaleLinear().range([dotChart.HEIGHT, 0]);
dotChart.Y = d3.scaleBand().rangeRound([0, dotChart.WIDTH]);
dotChart.FORMAT_PERCENT = d3.format(".0%");
dotChart.X_AXIS = d3.axisBottom()
.scale(dotChart.X);
dotChart.Y_AXIS = d3.axisLeft()
.scale(dotChart.Y)
.ticks(5)
.tickFormat(dotChart.FORMAT_PERCENT);
dotChart.SVG = d3.select("body").append("svg")
.attr("width", dotChart.WIDTH + dotChart.MARGIN.left + dotChart.MARGIN.right)
.attr("height", dotChart.HEIGHT + dotChart.MARGIN.top + dotChart.MARGIN.bottom)
.style("font", "10px verdana")
.append("g")
.attr("transform", "translate("+ dotChart.MARGIN.left +", "+ dotChart.MARGIN.top +")");
dotChart.LABELS = dotChart.SVG.append("g")
.attr("class", "labels");
dotChart.TITLE = dotChart.SVG.append("g")
.attr("class", "title");
My html code is
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="js/d3.js"></script>
<script src="dotChart.js"></script>
<style>
.axis path,
.axis line {
fill: none;
stroke: lightgrey;
}
</style>
</head>
<body>
<script type="text/javascript">
var line = new dotChart();
line.drawChart("data_01.json");
</script>
</body>
</html>
Data is
[
{"Country":14,"Income":457.81},
{"Country":15,"Income":4924.5},
{"Country":16,"Income":4792.6},
{"Country":17,"Income": 460.1},
{"Country":18,"Income":4821.7},
{"Country":19,"Income":7601.1},
{"Country":20,"Income": 5267},
{"Country":21,"Income":5006.1},
{"Country":22,"Income":4709.2},
{"Country":23,"Income":7719.2},
{"Country":24,"Income":7754.7},
{"Country":25,"Income":4873.6},
{"Country":26,"Income":4873.6},
{"Country":27,"Income":7754.7},
{"Country":28,"Income":5006.1},
{"Country":29,"Income":5267},
{"Country":30,"Income":4792.6}
]
I solved this adding de doChart.js in the body's sctructure..., another mistake I had, was the x and y domains because was inverted
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="js/d3.js"></script>
<style>
.axis path,
.axis line {
fill: none;
stroke: lightgrey;
}
</style>
</head>
<body>
<script src="dotChart.js"></script>
<script type="text/javascript">
var line = new dotChart();
line.drawChart("data_01.json");
</script>
</body>
</html>
And change the X and Y domains
dotChart.Y = d3.scaleLinear().range([dotChart.HEIGHT, 0]);
dotChart.X = d3.scaleBand().rangeRound([0, dotChart.WIDTH]);
I made a D3 map following Let's make a map tutorial by M. Bostock.
It is intended to create .subunit.id class and color it using CSS like .subunit.23 { fill: #f44242; }. But while .subunit is adressed well I can not reach each unit by specifying its id. Any ideas?
TopoJSON file is available here
https://gist.github.com/Avtan/649bbf5a28fd1f76278c752aca703d18
<!DOCTYPE html>
<meta charset="utf-8">
<html lang="en">
<style>
.subunit {
fill: #4286f4;
stroke: #efbfe9;}
.subunit.23 { fill: #f44242; }
</style>
<head>
<title>D3 Uzbekisztan map</title>
<meta charset="utf-8">
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script> -->
</head>
<body>
<script>
var width = 960,
height = 1160;
var projection = d3.geo.albers()
.center([-10, 40])
.rotate([-75, 0])
.parallels([38, 44])
.scale(4000)
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
d3.json("uzb_topo.json", function (error, uzb) {
if (error) return console.error(error);
console.log(uzb);
svg.selectAll(".subunit")
.data(topojson.feature(uzb, uzb.objects.uzb).features)
.enter().append("path")
.attr("class", function(d) { return "subunit " + d.id; })
.attr("d", path);
});
</script>
</body>
</html>
IDs cannot start by a number. Right now, you're setting two different classes, and the last one start with a number.
A simple solution is removing the space in your class name. So, this:
.attr("class", function(d) { return "subunit " + d.id; })
Should be this:
.attr("class", function(d) { return "subunit" + d.id; })//no space
And set your CSS accordingly.
Another solution is adding a letter before the number, like this:
.attr("class", function(d) { return "subunit " + "a" + d.id; })
So, you'll have the classes "a01", "a02", "a03" etc...